フォーラムメモ 
自分自身のプロパティアクセサを呼び出すとフリーズ 
- コールトレース
0
1
2
3
4
5
6
7
8
9
10
11
| GameProject.exe!asCArray<asCScriptFunction *>::operator[](unsigned int index=0) Line 147 + 0x6 Bytes C++
GameProject.exe!asCContext::CallInterfaceMethod(asCScriptFunction * func=0x0a8e1058) Line 1254 + 0x15 Bytes C++
GameProject.exe!asCContext::ExecuteNext() Line 2790 C++
GameProject.exe!asCContext::Execute() Line 1004 + 0x8 Bytes C++
GameProject.exe!base::ags::ContextHandle::execute() Line 104 + 0x23 Bytes C++
GameProject.exe!app::scene::SceneASTest::onSceneStart() Line 67 C++
GameProject.exe!app::Application::beginSceneProcess() Line 191 + 0x23 Bytes C++
GameProject.exe!app::Application::execute() Line 99 + 0xc Bytes C++
GameProject.exe!`anonymous namespace'::t_executeApplication() Line 50 + 0xb Bytes C++
GameProject.exe!app::EntryPoint::run() Line 73 C++
GameProject.exe!wmain(int __formal=1, int __formal=1) Line 16 C++
GameProject.exe!__tmainCRTStartup() Line 594 + 0x19 Bytes C
GameProject.exe!wmainCRTStartup() Line 414 C
|
- ASコード
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
-
|
|
|
!
-
|
-
|
!
|
-
|
!
|
|
!
-
|
|
!
| class Vector3
{
float x;
float y;
float z;
};
class Hoge
{
const Vector3 get_pos()const
{
return mPos;
}
const Vector3 foo()const
{
return pos;
}
Vector3 mPos;
};
void main()
{
Hoge h;
const Vector3 vec = h.foo();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
-
|
|
|
!
-
|
|
|
|
!
-
|
|
|
|
!
| class Vector3
{
float x;
float y;
float z;
};
class Hoge
{
const Vector3 get_pos() { return mPos; }
const Vector3 foo() { return pos; }
const Vector3 zoo() { return get_pos(); }
Vector3 mPos;
};
void main()
{
Hoge h;
Vector3 vec;
vec = h.zoo(); vec = h.foo(); }
|
__Hoge_foo.txt
0
1
2
3
4
5
6
7
8
9
10
11
12
| Temps: 1
0 0 * PUSH 1
1 1 * PshV4 v0
2 2 * CALLSYS 17 (void _builtin_object_::_beh_4_())
- 11,27 -
4 1 * SUSPEND
5 1 * PSF v0
6 2 * CALLINTF 86 (const Vector3 Hoge::get_pos())
8 1 * STOREOBJ v1
9 1 * LOADOBJ v1
10 1 * 0:
10 1 * FREE v0, 11276368
12 0 * RET 1
|
__Hoge_zoo.txt
0
1
2
3
4
5
6
7
8
9
10
11
12
| Temps: 1
0 0 * PUSH 1
1 1 * PshV4 v0
2 2 * CALLSYS 17 (void _builtin_object_::_beh_4_())
- 12,27 -
4 1 * SUSPEND
5 1 * PshV4 v0
6 2 * CALLINTF 86 (const Vector3 Hoge::get_pos())
8 1 * STOREOBJ v1
9 1 * LOADOBJ v1
10 1 * 0:
10 1 * FREE v0, 11276368
12 0 * RET 1
|
opAssignを定義するとコンパイル時にAssert 
- エラーメッセージ
Assertion failed: tempVariables.GetLength() == 0, file ..\..\source\as_compiler.cpp, line 643
- コールトレース
0
1
2
3
| msvcr80d.dll!_wassert(const wchar_t * expr=0x007013cc, const wchar_t * filename=0x00701174, unsigned int lineno=643) 行 212 C
GameProject.exe!asCCompiler::CompileStatementBlock(asCScriptNode * block=0x0a8c1640, bool ownVariableScope=false, bool * hasReturn=0x0012b2e3, asCByteCode * bc=0x0012b2a0) 行 643 + 0x29 バイト C++
GameProject.exe!asCCompiler::CompileFunction(asCBuilder * builder=0x0a8e2b08, asCScriptCode * script=0x0a8e2800, asCScriptNode * func=0x0a8e2878, asCScriptFunction * outFunc=0x0a8e3c10) 行 324 C++
GameProject.exe!asCBuilder::CompileFunctions() 行 524 C++
GameProject.exe!asCBuilder::Build() 行 184 C++
|
- ASコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
-
|
|
|
-
|
!
|
|
-
|
|
!
!
-
|
!
| class Hoge
{
int mValue;
Hoge()
{
mValue = 0;
}
Hoge@ opAssign(const Hoge &in aObj)
{
mValue = aObj.mValue;
return @this;
}
};
void main()
{
Hoge a = Hoge();
}
|
デストラクタが呼ばれない 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
-
!
-
|
-
|
!
|
-
|
!
|
!
-
|
-
|
!
|
-
|
!
|
!
-
|
-
|
!
-
|
!
|
|
!
-
|
!
|
class ObjType
{
};
class HaveObj
{
HaveObj()
{
Print( "HaveObj::ctor\n" );
}
~HaveObj()
{
Print( "HaveObj::dtor\n" );
}
ObjType o;
};
class HaveHandle
{
HaveHandle()
{
Print( "HaveHandle::ctor\n" );
}
~HaveHandle()
{
Print( "HaveHandle::dtor\n" );
}
ObjType@ o;
};
void testCase1()
{
Print( "testCase1 start\n" );
{
HaveObj o;
} {
HaveHandle o;
}
Print( "testCase1 end\n" );
}
void main()
{
testCase1();
}
testCase1 start
HaveObj::ctor
HaveObj::dtor
HaveHandle::ctor
testCase1 end
|
回答
As with any language with automatic memory management, it can be hard to predict when the destructor is called.
In your case you have created a situation where the ordinary reference counting is not enough to control the life time of the objects, so the garbage collector is notified to keep a watch on the object instance. However, the garbage collector is not invoked automatically by the script engine (so that the application will have full control of when that is done). This means that unless you manually call the GC, the object will only be destroyed when you release the script engine.
配列を使った循環参照状態を記述するとメモリリーク 
- C++コード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
-
|
|
|
|
-
|
|
!
|
|
-
|
|
!
!
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
| namespace
{
int t_allocCount = 0;
void* t_alloc( const size_t aSize )
{
++t_allocCount;
return std::malloc( aSize );
}
void t_free( void* aPtr )
{
std::free( aPtr );
--t_allocCount;
}
}
int main(int argc, char **argv)
{
asSetGlobalMemoryFunctions( t_alloc , t_free );
asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE);
mod->AddScriptSection("script",
"class Hoge"
"{"
" Hoge(){}"
" Hoge(HogeManager&){}"
"};"
"class HogeManager"
"{"
" Hoge[] hoges;"
"};"
, 0);
mod->Build();
engine->Release();
assert( t_allocCount == 0 );
}
|
- 対処
1
2
3
4
5
6
7
8
|
|
GarbageCollect(asGC_FULL_CYCLE);
FreeUnusedGlobalProperties();
ClearUnusedTypes();
GarbageCollect(asGC_FULL_CYCLE);
|
enumの文字列かぶりで無限ループ 
- スクリプトコード
1
2
3
4
5
6
|
-
|
|
!
| enum Kind
{
Kind_A
, Kind_A };
|
asGC_ONE_STEPは想定した動きになっているのか 
- code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
|
-
|
!
|
|
-
|
|
|
|
|
!
|
|
|
!
|
|
|
|
!
| void main()
{
asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
RegisterScriptString(engine);
engine->RegisterGlobalFunction("void Print(string &in)", asFUNCTION(PrintString_Generic), asCALL_GENERIC);
asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE);
mod->AddScriptSection("script",
"class Obj{};"
"class Hoge"
"{"
" Hoge(){ Print(\"ctor\\n\"); }"
" ~Hoge(){ Print(\"dtor\\n\"); }"
" Obj@ obj;"
"};"
"void main()"
"{"
" Hoge hoge;"
"};"
, 0);
mod->Build();
asIScriptContext *ctx = engine->CreateContext();
while ( true )
{
printf("----- execute\n");
ctx->Prepare(mod->GetFunctionIdByDecl("void main()"));
ctx->Execute();
const int GC_STEP_COUNT_PER_FRAME = 10000;
for ( int i = 0; i < GC_STEP_COUNT_PER_FRAME; ++i )
{
engine->GarbageCollect(asGC_ONE_STEP);
}
{
asUINT currentSize = asUINT();
asUINT totalDestroyed = asUINT();
asUINT totalDetected = asUINT();
engine->GetGCStatistics(¤tSize , &totalDestroyed , &totalDetected );
printf("(%lu,%lu,%lu)\n" , currentSize , totalDestroyed , totalDetected );
}
while(!getch()){}
}
ctx->Release();
engine->Release();
}
|
- output
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| ----- execute
ctor
dtor
(8,1,0)
----- execute
ctor
(9,1,0)
----- execute
ctor
(10,1,0)
----- execute
ctor
(11,1,0)
----- execute
ctor
(12,1,0)
----- execute
ctor
(13,1,0)
----- execute
ctor
(14,1,0)
|
as_restore.cppの修正箇所 
- revision
- r589
- Not work on Big Endian Environment(L1398,L1410,L1451,L1473,L1488)
1
2
3
4
|
| asWORD w = (asWORD)(tmp[0]>>16);
asWORD w = *(((asWORD*)tmp)+1);
|
- Not work on Big Endian Environment(L1587,L1600,L1637,L1661,L1678,L1727)
1
2
3
4
|
| *bc += asDWORD(w)<<16;
*(((asWORD*)bc)+1) = w;
|
- miss spell(L1497)
1
2
3
4
5
6
|
| asDWORD dw = tmp[2];
WRITE_NUM(w);
asDWORD dw = tmp[2];
WRITE_NUM(dw);
|
- miss spell(L1642,L1666,L1683)
1
2
3
4
|
| *(WORD*)bc = w;
*(asWORD*)bc = w;
|
- miss spell(L1646)
1
2
3
4
|
| *(((WORD*)bc)+1) = w;
*(((asWORD*)bc)+1) = w;
|
テンプレートのメソッドを呼ぶスクリプトコードをSaveByteCode&LoadByteCodeすると落ちる 
- リビジョン
- r589
- script code
1
2
3
4
5
|
-
|
|
!
| void main()
{
array< int > intArray = {0,1,2};
uint tmp = intArray.length(); };
|
- freeze code pos
1
2
3
4
5
6
7
8
9
|
-
|
|
|
!
| else if( c == asBC_CALL ||
c == asBC_CALLINTF ||
c == asBC_CALLSYS )
{
int *fid = (int*)&bc[n+1];
*fid = FindFunction(*fid)->id; }
|
fix patch 
- as_restore.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
-
|
|
|
|
-
|
|
|
-
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
|
-
|
|
|
|
!
!
|
-
|
|
|
!
|
-
|
|
|
!
!
|
-
|
|
!
!
-
|
|
|
|
-
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
!
|
-
!
|
-
|
!
|
|
|
|
!
|
-
|
|
|
|
|
|
|
!
!
|
-
|
|
|
|
|
|
-
|
-
|
|
!
!
|
|
|
!
|
-
|
|
|
|
|
-
|
|
|
|
|
|
!
|
-
|
!
|
-
|
!
|
|
!
|
-
|
|
|
!
|
|
!
| void asCRestore::WriteObjectType(asCObjectType* ot)
{
char ch;
if( ot )
{
if( ot->templateSubType.GetTokenType() != ttUnrecognizedToken &&
ot != engine->defaultArrayObjectType )
{
ch = 'a';
WRITE_NUM(ch);
if( ot->templateSubType.IsObject() )
{
ch = 's';
WRITE_NUM(ch);
WriteObjectType(ot->templateSubType.GetObjectType());
if( ot->templateSubType.IsObjectHandle() )
ch = 'h';
else
ch = 'o';
WRITE_NUM(ch);
#if 1
ch = 'o';
WRITE_NUM(ch);
WriteString(&ot->name);
#endif
}
else
{
ch = 't';
WRITE_NUM(ch);
eTokenType t = ot->templateSubType.GetTokenType();
WRITE_NUM(t);
}
}
else if( ot->flags & asOBJ_TEMPLATE_SUBTYPE )
{
ch = 's';
WRITE_NUM(ch);
WriteString(&ot->name);
}
else
{
ch = 'o';
WRITE_NUM(ch);
WriteString(&ot->name);
}
}
else
{
ch = '\0';
WRITE_NUM(ch);
}
}
asCObjectType* asCRestore::ReadObjectType()
{
asCObjectType *ot;
char ch;
READ_NUM(ch);
if( ch == 'a' )
{
READ_NUM(ch);
if( ch == 's' )
{
#if 0
ot = ReadObjectType();
asCDataType dt = asCDataType::CreateObject(ot, false);
READ_NUM(ch);
if( ch == 'h' )
dt.MakeHandle(true);
dt.MakeArray(engine);
ot = dt.GetObjectType();
asASSERT(ot);
#else
asCObjectType* subOT = ReadObjectType();
asCDataType dt = asCDataType::CreateObject(subOT, false);
READ_NUM(ch);
if ( ch == 'h' )
{
dt.MakeHandle(true);
}
else if ( ch == 'o' )
{
}
else
{
assert(false);
}
asCObjectType* tmplOT = ReadObjectType();
dt.MakeTemplateInstanceType(engine,tmplOT);
ot = dt.GetObjectType();
#endif
}
else
{
eTokenType tokenType;
READ_NUM(tokenType);
asCDataType dt = asCDataType::CreatePrimitive(tokenType, false);
dt.MakeArray(engine);
ot = dt.GetObjectType();
asASSERT(ot);
}
}
else if( ch == 's' )
{
asCString typeName;
ReadString(&typeName);
for( asUINT n = 0; n < engine->templateSubTypes.GetLength(); n++ )
{
if( engine->templateSubTypes[n] && engine->templateSubTypes[n]->name == typeName )
{
ot = engine->templateSubTypes[n];
break;
}
}
asASSERT(ot);
}
else if( ch == 'o' )
{
asCString typeName;
ReadString(&typeName);
if( typeName.GetLength() && typeName != "_builtin_object_" && typeName != "_builtin_function_" )
{
ot = module->GetObjectType(typeName.AddressOf());
if( !ot )
ot = engine->GetObjectType(typeName.AddressOf());
asASSERT(ot);
}
else if( typeName == "_builtin_object_" )
{
ot = &engine->scriptTypeBehaviours;
}
else if( typeName == "_builtin_function_" )
{
ot = &engine->functionBehaviours;
}
else
assert( false );
}
else
{
assert( ch == '\0' );
ot = 0;
}
return ot;
}
|
- as_datatype.h
1
2
3
4
5
6
7
8
|
| int MakeHandle(bool b, bool acceptHandleForScope = false);
int MakeArray(asCScriptEngine *engine);
int MakeReference(bool b);
int MakeReadOnly(bool b);
int MakeHandleToConst(bool b);
#if 1
int MakeTemplateInstanceType(asCScriptEngine *engine,asCObjectType*);
#endif
|
- as_datatype.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-
|
|
|
|
|
|
|
|
|
|
|
|
!
| #if 1
int asCDataType::MakeTemplateInstanceType(asCScriptEngine *engine,asCObjectType* templateOT)
{
bool tmpIsReadOnly = isReadOnly;
isReadOnly = false;
asCObjectType *at = engine->GetTemplateInstanceType(templateOT, *this);
isReadOnly = tmpIsReadOnly;
isObjectHandle = false;
isConstHandle = false;
objectType = at;
tokenType = ttIdentifier;
return 0;
}
#endif
|
as_restore.cppの修正箇所 その2 
r594
Not work on Big Endian Environment(L1580,L1615,L1624,L1699,L1710)
1
2
3
4
5
|
| *bc++ = b;
*(asBYTE*)(bc) = b;
++bc;
|
Not work on Big Endian Environment(L1600.L1637,L1661,L1678,L1727)
1
2
3
4
|
| *bc = b;
*(asBYTE*)(bc) = b;
|
|