bool Test() { bool fail = false; int r; COutStream out; asIScriptContext *ctx; asIScriptEngine *engine; CBufferedOutStream bout; asIScriptModule *mod; // --------------------------------------------- { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void SetMyAny(any@)", asFUNCTION(SetMyAny), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("TestAny", script1, strlen(script1), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", "TestAny"); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestAny()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; PRINTF("%s: Execution failed\n", "TestAny"); } if( ctx ) ctx->Release(); engine->Release(); } //-------------------------------------------------- // Verify that the GC can handle circles with any structures { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void SetMyAny(any@)", asFUNCTION(SetMyAny), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("TestAny", script2, strlen(script2), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", "TestAny"); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestAny()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; PRINTF("%s: Execution failed\n", "TestAny"); } if( ctx ) ctx->Release(); asUINT gcCurrentSize, gcTotalDestroyed, gcTotalDetected; engine->GetGCStatistics(&gcCurrentSize, &gcTotalDestroyed, &gcTotalDetected); engine->GarbageCollect(); engine->GetGCStatistics(&gcCurrentSize, &gcTotalDestroyed, &gcTotalDetected); if( !fail ) assert( gcCurrentSize == 0 && gcTotalDestroyed == 8 && gcTotalDetected == 7 ); engine->Release(); } //------------------------------------------------------- // Don't allow const handle to retrieve() { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void SetMyAny(any@)", asFUNCTION(SetMyAny), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("TestAny", script3, strlen(script3), 0); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to Build()\n", "TestAny"); } if( bout.buffer != "TestAny (5, 1) : Info : Compiling void TestAny()\n" "TestAny (9, 15) : Warning : Argument cannot be assigned. Output will be discarded.\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } //-------------------------------------------------------- // Make sure it is possible to pass any to the application { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void SetMyAny(any@)", asFUNCTION(SetMyAny), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("TestAny", script4, strlen(script4), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile\n", "TestAny"); } r = ExecuteString(engine, "TestAny()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; PRINTF("%s: Failed to execute\n", "TestAny"); } if( myAny ) { int typeId = myAny->GetTypeId(); if( !(typeId & asTYPEID_OBJHANDLE) ) TEST_FAILED; if( (typeId & asTYPEID_MASK_OBJECT) != asTYPEID_APPOBJECT ) TEST_FAILED; const char *decl = engine->GetTypeDeclaration(typeId); if( (decl == 0) || (strcmp(decl, "string@") != 0) ) { TEST_FAILED; PRINTF("%s: Failed to return the correct type\n", "TestAny"); } int typeId2 = engine->GetTypeIdByDecl("string@"); if( typeId != typeId2 ) { TEST_FAILED; PRINTF("%s: Failed to return the correct type\n", "TestAny"); } CScriptString *str = 0; myAny->Retrieve((void*)&str, typeId); if( str->buffer != "test" ) { TEST_FAILED; PRINTF("%s: Failed to set the string correctly\n", "TestAny"); } if( str ) str->Release(); myAny->Release(); myAny = 0; } else TEST_FAILED; //-------------------------------------- // Make sure the any type can store primitives as well r = ExecuteString(engine, "any a; a.store(1); int b; a.retrieve(b); Assert(b == 1);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test storing value type { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "void main() \n" "{ \n" " any storage; \n" " storage.store(vector3(1,1,1)); \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "main()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; } ctx->Release(); engine->Release(); } // Test storing null pointer { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, "any a; a.store(null);", 0); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; CBufferedOutStream bout; asIScriptEngine *engine; // Ordinary tests { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptWeakRef(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class Test {} \n" "void main() { \n" " Test @t = Test(); \n" " weakref<Test> r(t); \n" " assert( r.get() !is null ); \n" " @t = null; \n" " assert( r.get() is null ); \n" "} \n"; asIScriptModule *mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // It shouldn't be possible to instanciate the weakref for types that do not support it { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); bout.buffer = ""; RegisterScriptWeakRef(engine); RegisterStdString(engine); RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class Test {} \n" "void main() { \n" " weakref<int> a; \n" // fail " weakref<string> b; \n" // fail " weakref<Test@> c; \n" // fail " weakref<array<Test>> d; \n" // fail "} \n"; asIScriptModule *mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "Test_Addon_WeakRef (2, 1) : Info : Compiling void main()\n" "Test_Addon_WeakRef (3, 11) : Error : Can't instantiate template 'weakref' with subtype 'int'\n" "Test_Addon_WeakRef (4, 11) : Error : Can't instantiate template 'weakref' with subtype 'string'\n" "Test_Addon_WeakRef (5, 11) : Error : Can't instantiate template 'weakref' with subtype 'Test@'\n" "weakref (0, 0) : Error : The subtype doesn't support weak references\n" "Test_Addon_WeakRef (6, 11) : Error : Can't instantiate template 'weakref' with subtype 'array<Test>'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test registering app type with weak ref #ifndef AS_MAX_PORTABILITY { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->RegisterObjectType("MyClass", 0, asOBJ_REF); engine->RegisterObjectBehaviour("MyClass", asBEHAVE_FACTORY, "MyClass @f()", asFUNCTION(MyClass::Factory), asCALL_CDECL); engine->RegisterObjectBehaviour("MyClass", asBEHAVE_ADDREF, "void f()", asMETHOD(MyClass, AddRef), asCALL_THISCALL); engine->RegisterObjectBehaviour("MyClass", asBEHAVE_RELEASE, "void f()", asMETHOD(MyClass, Release), asCALL_THISCALL); engine->RegisterObjectBehaviour("MyClass", asBEHAVE_GET_WEAKREF_FLAG, "int &f()", asMETHOD(MyClass, GetWeakRefFlag), asCALL_THISCALL); RegisterScriptWeakRef(engine); const char *script = "void main() { \n" " MyClass @t = MyClass(); \n" " weakref<MyClass> r(t); \n" " assert( r.get() !is null ); \n" " @t = null; \n" " assert( r.get() is null ); \n" "} \n"; asIScriptModule *mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } #endif // TODO: weak: It should be possible to declare a script class to not allow weak references, and as such save the memory for the internal pointer // TODO: weak: add engine property to turn off automatic support for weak references to all script classes // Success return fail; }
bool Test() { RET_ON_MAX_PORT bool fail = false; int r; COutStream out; asIScriptEngine *engine; asIScriptModule *mod; asIScriptContext *ctx; CBufferedOutStream bout; const char *script; // Test invalid use of function pointer { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "void func2() { \n" " func; \n" "} \n" "void func() {} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "test (1, 1) : Info : Compiling void func2()\n" "test (2, 3) : Error : Invalid expression: ambiguous name\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test value comparison for function pointers /* { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void CALLBACK(); \n" "void func1() {} \n" "void func2() {} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "CALLBACK@ c1 = func1, c2 = func1; \n" "assert( c1 == c2 ); \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // TODO: Test that two different function pointers give false // TODO: Test for delegate objects engine->Release(); } */ // Proper error message when trying to pass class method as function pointer directly // http://www.gamedev.net/topic/655390-is-there-a-bug-with-function-callbacks/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void CALLBACK(int); \n" "class Test { \n" " void Init() { \n" " SetCallback(MyCallback); \n" // This should fail, since delegate is necessary " } \n" " void MyCallback(int) {} \n" "} \n" "void SetCallback(CALLBACK @) {} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "test (3, 3) : Info : Compiling void Test::Init()\n" "test (4, 5) : Error : No matching signatures to 'SetCallback(Test::MyCallback)'\n" "test (4, 5) : Error : Can't pass class method as arg directly. Use a delegate object instead\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // opEquals with funcdef // http://www.gamedev.net/topic/647797-difference-between-xopequalsy-and-xy-with-funcdefs/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void CALLBACK(); \n" "class Test { \n" " bool opEquals(CALLBACK @f) { \n" " return f is func; \n" " } \n" " CALLBACK @func; \n" "} \n" "void func() {} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test t; \n" "@t.func = func; \n" "assert( t == func );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void CALLBACK(); \n" "class Test { \n" " bool opEquals(CALLBACK @f) { \n" " return f is func; \n" " } \n" " CALLBACK @func; \n" "} \n" "namespace ns { \n" "void func() {} \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test t; \n" "@t.func = ns::func; \n" "assert( t == ns::func );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test funcdefs and namespaces // http://www.gamedev.net/topic/644586-application-function-returning-a-funcdef-handle-crashes-when-called-in-as/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "bool called = false; \n" "funcdef void simpleFuncDef(); \n" "namespace foo { \n" " void simpleFunction() { called = true; } \n" "} \n" "void takeSimpleFuncDef(simpleFuncDef@ f) { f(); } \n" "void main() { \n" " takeSimpleFuncDef(foo::simpleFunction); \n" " assert( called ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; mod->AddScriptSection("test", "bool called = false; \n" "funcdef void simpleFuncDef();\n" "namespace foo {\n" " void simpleFunction() { called = true; }\n" "}\n" "void main() {\n" " simpleFuncDef@ bar = foo::simpleFunction;\n" " bar(); \n" " assert( called ); \n" "}\n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test registering global property of funcdef type // http://www.gamedev.net/topic/644586-application-function-returning-a-funcdef-handle-crashes-when-called-in-as/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); asIScriptFunction *f = 0; engine->RegisterFuncdef("void myfunc()"); r = engine->RegisterGlobalProperty("myfunc @f", &f); if( r < 0 ) TEST_FAILED; mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "void func() {} \n"); mod->Build(); r = ExecuteString(engine, "@f = func; \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( f == 0 ) TEST_FAILED; if( strcmp(f->GetName(), "func") != 0 ) TEST_FAILED; f->Release(); f = 0; engine->Release(); } // Test casting with funcdefs // http://www.gamedev.net/topic/644586-application-function-returning-a-funcdef-handle-crashes-when-called-in-as/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void myfunc1(); \n" "funcdef void myfunc2(); \n" "funcdef void myfunc3(); \n" "bool called = false; \n" "void func() { called = true; } \n" "void main() \n" "{ \n" " myfunc1 @f1 = func; \n" " myfunc2 @f2 = cast<myfunc2>(f1); \n" // explicit cast " myfunc3 @f3 = f2; \n" // implicit cast " assert( f1 is f2 ); \n" " assert( f2 is f3 ); \n" " assert( f3 is func ); \n" " f3(); \n" " assert( called ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Don't allow application to register additional behaviours to funcdefs // http://www.gamedev.net/topic/644586-application-function-returning-a-funcdef-handle-crashes-when-called-in-as/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; engine->RegisterObjectType("jjOBJ", 0, asOBJ_REF | asOBJ_NOCOUNT); engine->RegisterFuncdef("void jjBEHAVIOR(jjOBJ@)"); engine->RegisterFuncdef("void DifferentFunctionPointer()"); r = engine->RegisterObjectBehaviour("jjBEHAVIOR", asBEHAVE_IMPLICIT_REF_CAST, "DifferentFunctionPointer@ a()", asFUNCTION(0), asCALL_CDECL_OBJLAST); if( r >= 0 ) TEST_FAILED; if( bout.buffer != " (0, 0) : Error : Failed in call to function 'RegisterObjectBehaviour' with 'jjBEHAVIOR' and 'DifferentFunctionPointer@ a()' (Code: -12)\n" ) TEST_FAILED; engine->Release(); } // Test delegate function pointers for object methods { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); RegisterStdString(engine); mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "class Test { \n" " void method() {} \n" " int func(int) { return 0; } \n" " void func() { called = true; } \n" // The compiler should pick the right overload " bool called = false; \n" "} \n" "funcdef void CALLBACK(); \n" "void main() { \n" " Test t; \n" " CALLBACK @cb = CALLBACK(t.func); \n" // instanciate a delegate " cb(); \n" // invoke the delegate " assert( t.called ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Must be possible to save/load bytecode CBytecodeStream stream("test"); mod->SaveByteCode(&stream); mod = engine->GetModule("test2", asGM_ALWAYS_CREATE); r = mod->LoadByteCode(&stream); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Must be possible to create delegate from within class method, i.e. implicit this.method bout.buffer = ""; mod->AddScriptSection("test", "funcdef void CALL(); \n" "class Test { \n" " bool called = false; \n" " void callMe() { called = true; } \n" " CALL @GetCallback() { return CALL(callMe); } \n" "} \n" "void main() { \n" " Test t; \n" " CALL @cb = t.GetCallback(); \n" " cb(); \n" " assert( t.called ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // A delegate to own method held as member of class must be properly resolved by gc bout.buffer = ""; mod->AddScriptSection("test", "funcdef void CALL(); \n" "class Test { \n" " void call() {}; \n" " CALL @c; \n" "} \n" "void main() { \n" " Test t; \n" " @t.c = CALL(t.call); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->GarbageCollect(); asUINT currSize, totalDestr, totalDetect; engine->GetGCStatistics(&currSize, &totalDestr, &totalDetect); r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->GarbageCollect(); asUINT currSize2, totalDestr2, totalDetect2; engine->GetGCStatistics(&currSize2, &totalDestr2, &totalDetect2); if( totalDetect2 == totalDetect ) TEST_FAILED; // Must be possible to call delegate from application mod->AddScriptSection("test", "funcdef void CALL(); \n" "class Test { \n" " bool called = false; \n" " void call() { called = true; } \n" "} \n" "Test t; \n" "CALL @callback = CALL(t.call); \n"); r = mod->Build(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; int idx = mod->GetGlobalVarIndexByDecl("CALL @callback"); if( idx < 0 ) TEST_FAILED; asIScriptFunction *callback = *(asIScriptFunction**)mod->GetAddressOfGlobalVar(idx); if( callback == 0 ) TEST_FAILED; if( callback->GetFuncType() != asFUNC_DELEGATE ) TEST_FAILED; if( callback->GetDelegateObject() == 0 ) TEST_FAILED; if( std::string(callback->GetDelegateFunction()->GetDeclaration()) != "void Test::call()" ) TEST_FAILED; ctx = engine->CreateContext(); ctx->Prepare(callback); r = ctx->Execute(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; ctx->Release(); r = ExecuteString(engine, "assert( t.called );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Must be possible to create the delegate from the application asIScriptObject *obj = (asIScriptObject*)mod->GetAddressOfGlobalVar(mod->GetGlobalVarIndexByDecl("Test t")); asIScriptFunction *func = obj->GetObjectType()->GetMethodByName("call"); asIScriptFunction *delegate = engine->CreateDelegate(func, obj); if( delegate == 0 ) TEST_FAILED; delegate->Release(); // Must be possible to create delegate for registered type too mod->AddScriptSection("test", "funcdef bool EMPTY(); \n" "void main() { \n" " array<int> a; \n" " EMPTY @empty = EMPTY(a.isEmpty); \n" " assert( empty() == true ); \n" " a.insertLast(42); \n" " assert( empty() == false ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Must not be possible to create delegate with const object and non-const method bout.buffer = ""; mod->AddScriptSection("test", "funcdef void F(); \n" "class Test { \n" " void f() {} \n" "} \n" "void main() { \n" " const Test @t; \n" " F @f = F(t.f); \n" // t is read-only, so this delegate must not be allowed "} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; // TODO: Error message should be better, so it is understood that the error is because of const object if( bout.buffer != "test (5, 1) : Info : Compiling void main()\n" "test (7, 9) : Error : No matching signatures to 'void F()'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } // Must not be possible to create delegates for non-reference types bout.buffer = ""; mod->AddScriptSection("test", "funcdef bool CB(); \n" "string s; \n" "CB @cb = CB(s.isEmpty); \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "test (3, 5) : Info : Compiling CB@ cb\n" "test (3, 10) : Error : Can't create delegate for types that do not support handles\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test ordinary function pointers for global functions { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); // Test the declaration of new function signatures script = "funcdef void functype();\n" // It must be possible to declare variables of the funcdef type "functype @myFunc = null;\n" // It must be possible to initialize the function pointer directly "functype @myFunc1 = @func;\n" "void func() { called = true; }\n" "bool called = false;\n" // It must be possible to compare the function pointer with another "void main() { \n" " assert( myFunc1 !is null ); \n" " assert( myFunc1 is func ); \n" // It must be possible to call a function through the function pointer " myFunc1(); \n" " assert( called ); \n" // Local function pointer variables are also possible " functype @myFunc2 = @func;\n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r != 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to save the byte code with function handles CBytecodeStream bytecode(__FILE__"1"); mod->SaveByteCode(&bytecode); { asIScriptModule *mod2 = engine->GetModule("mod2", asGM_ALWAYS_CREATE); mod2->LoadByteCode(&bytecode); r = ExecuteString(engine, "main()", mod2); if( r != asEXECUTION_FINISHED ) TEST_FAILED; } // Test function pointers as members of classes. It should be possible to call the function // from within a class method. It should also be possible to call it from outside through the . operator. script = "funcdef void FUNC(); \n" "class CMyObj \n" "{ \n" " CMyObj() { @f = @func; } \n" " FUNC@ f; \n" " void test() \n" " { \n" " this.f(); \n" " f(); \n" " CMyObj o; \n" " o.f(); \n" " main(); \n" " assert( called == 4 ); \n" " } \n" "} \n" "void main() \n" "{ \n" " CMyObj o; \n" " o.f(); \n" "} \n" "int called = 0; \n" "void func() { called++; } \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "CMyObj o; o.test();", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); } ctx->Release(); // It must not be possible to declare a non-handle variable of the funcdef type engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; script = "funcdef void functype();\n" "functype myFunc;\n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (2, 1) : Error : Data type can't be 'functype'\n" "script (2, 10) : Info : Compiling functype myFunc\n" "script (2, 10) : Error : No default constructor for object of type 'functype'.\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } // It must not be possible to invoke the funcdef bout.buffer = ""; script = "funcdef void functype();\n" "void func() { functype(); } \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (2, 1) : Info : Compiling void func()\n" "script (2, 15) : Error : No matching signatures to 'functype()'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } // Test that a funcdef can't have the same name as other global entities bout.buffer = ""; script = "funcdef void test(); \n" "int test; \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (2, 5) : Error : Name conflict. 'test' is a funcdef.\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } // It is possible to take the address of class methods, but not to assign to funcdef variable bout.buffer = ""; script = "funcdef void F(); \n" "class t { \n" " void func() { \n" " @func; \n" " F @f = @func; \n" " } \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; // TODO: The error message should be better if( bout.buffer != "script (3, 3) : Info : Compiling void t::func()\n" "script (4, 5) : Error : Invalid expression: ambiguous name\n" "script (5, 12) : Error : Can't implicitly convert from 't' to 'F@&'.\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } // A more complex sample bout.buffer = ""; script = "funcdef bool CALLBACK(int, int); \n" "funcdef bool CALLBACK2(CALLBACK @); \n" "void main() \n" "{ \n" " CALLBACK @func = @myCompare; \n" " CALLBACK2 @func2 = @test; \n" " func2(func); \n" "} \n" "bool test(CALLBACK @func) \n" "{ \n" " return func(1, 2); \n" "} \n" "bool myCompare(int a, int b) \n" "{ \n" " return a > b; \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to register the function signature from the application r = engine->RegisterFuncdef("void AppCallback()"); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void ReceiveFuncPtr(AppCallback @)", asFUNCTION(ReceiveFuncPtr), asCALL_CDECL); assert( r >= 0 ); // It must be possible to use the registered funcdef // It must be possible to receive a function pointer for a registered func def bout.buffer = ""; script = "void main() \n" "{ \n" " AppCallback @func = @test; \n" " func(); \n" " ReceiveFuncPtr(func); \n" "} \n" "void test() \n" "{ \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( !receivedFuncPtrIsOK ) TEST_FAILED; mod->SaveByteCode(&bytecode); { receivedFuncPtrIsOK = false; asIScriptModule *mod2 = engine->GetModule("mod2", asGM_ALWAYS_CREATE); mod2->LoadByteCode(&bytecode); r = ExecuteString(engine, "main()", mod2); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( !receivedFuncPtrIsOK ) TEST_FAILED; } // The compiler should be able to determine the right function overload // by the destination of the function pointer bout.buffer = ""; mod->AddScriptSection("test", "funcdef void f(); \n" "f @fp = @func; \n" "bool called = false; \n" "void func() { called = true; } \n" "void func(int) {} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "fp(); assert( called );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; //---------------------------------------------------------- // TODO: Future improvements below // If the function referred to when taking a function pointer is removed from the module, // the code must not be invalidated. After removing func() from the module, it must still // be possible to execute func2() script = "funcdef void FUNC(); \n" "void func() {} \n"; "void func2() { FUNC@ f = @func; f(); } \n"; // Test that the function in a function pointer isn't released while the function // is being executed, even though the function pointer itself is cleared script = "DYNFUNC@ funcPtr; \n" "funcdef void DYNFUNC(); \n" "@funcPtr = @CompileDynFunc('void func() { @funcPtr = null; }'); \n"; // Test that it is possible to declare the function signatures out of order // This also tests the circular reference between the function signatures script = "funcdef void f1(f2@) \n" "funcdef void f2(f1@) \n"; // It must be possible to identify a function handle type from the type id // It must be possible enumerate the function definitions in the module, // and to enumerate the parameters the function accepts // A funcdef defined in multiple modules must share the id and signature so that a function implemented // in one module can be called from another module by storing the handle in the funcdef variable // An interface that takes a funcdef as parameter must still have its typeid shared if the funcdef can also be shared // If the funcdef takes an interface as parameter, it must still be shared // Must have a generic function pointer that can store any signature. The function pointer // can then be dynamically cast to the correct function signature so that the function it points // to can be invoked. engine->Release(); } // Test function pointers with virtual property accessors // http://www.gamedev.net/topic/639243-funcdef-inside-shared-interface-interface-already-implement-warning/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void funcdef1( ifuncdef1_1& i ); \n" "shared interface ifuncdef1_1 \n" "{ \n" " ifuncdef1_2@ events { get; set; } \n" " void crashme(); \n" "} \n" "shared interface ifuncdef1_2 \n" "{ \n" " funcdef1@ f { get; set; } \n" "} \n" "class cfuncdef1_1 : ifuncdef1_1 \n" "{ \n" " ifuncdef1_2@ _events_; \n" " cfuncdef1_1() { @this._events_ = cfuncdef1_2(); } \n" " ifuncdef1_2@ get_events() { return( this._events_ ); } \n" " void set_events( ifuncdef1_2@ events ) { @this._events_ = events; } \n" " void crashme() \n" " { \n" " if( this._events_ !is null && this._events_.f !is null ) \n" " { \n" " this.events.f( this ); \n" // " this.get_events().get_f()( this ); \n" // This should produce the same bytecode as the above " } \n" " } \n" "} \n" "class cfuncdef1_2 : ifuncdef1_2 \n" "{ \n" " funcdef1@ ff; \n" " cfuncdef1_2() { @ff = null; } \n" " funcdef1@ get_f() { return( @this.ff ); } \n" " void set_f( funcdef1@ _f ) { @this.ff = _f; } \n" "} \n" "void start() \n" "{ \n" " ifuncdef1_1@ i = cfuncdef1_1(); \n" " @i.events.f = end; \n" // TODO: Shouldn't this give an error? It's attempting to do an value assignment to a function pointer " i.crashme(); \n" "} \n" "bool called = false; \n" "void end( ifuncdef1_1& i ) \n" "{ \n" " called = true; \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "start(); assert( called );", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx, true); ctx->Release(); CBytecodeStream stream(__FILE__"1"); r = mod->SaveByteCode(&stream); if( r < 0 ) TEST_FAILED; engine->Release(); // Load the bytecode engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); stream.Restart(); mod = engine->GetModule("A", asGM_ALWAYS_CREATE); r = mod->LoadByteCode(&stream); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "start(); assert( called );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; ctx->Release(); stream.Restart(); mod = engine->GetModule("B", asGM_ALWAYS_CREATE); r = mod->LoadByteCode(&stream); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "start(); assert( called );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; ctx->Release(); engine->Release(); } // Test clean up with registered function definitions { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); r = engine->RegisterFuncdef("void MSG_NOTIFY_CB(const string& strCommand, const string& strTarget)"); assert(r>=0); engine->Release(); } // Test registering function pointer as property { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); r = engine->RegisterFuncdef("void fptr()"); r = engine->RegisterGlobalProperty("fptr f", 0); if( r >= 0 ) TEST_FAILED; engine->RegisterObjectType("obj", 0, asOBJ_REF); r = engine->RegisterObjectProperty("obj", "fptr f", 0); if( r >= 0 ) TEST_FAILED; engine->Release(); } // Test passing handle to function pointer { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "class CTempObj \n" "{ \n" " int Temp; \n" "} \n" "funcdef void FUNC2(CTempObj@);\n" "class CMyObj \n" "{ \n" " CMyObj() { @f2= @func2; }\n" " FUNC2@ f2; \n" "} \n" "void main() \n" "{ \n" " CMyObj o; \n" " CTempObj t; \n" " o.f2(t); \n" " assert( called == 1 ); \n" "} \n" "int called = 0; \n" "void func2(CTempObj@ Obj) \n" "{ called++; } \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test out of order declaration with function pointers { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "funcdef void FUNC2(CTempObj@);\n" "class CTempObj {} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->Release(); } // It must be possible calling system functions through pointers too { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterFuncdef("bool fun(bool)"); engine->RegisterGlobalFunction("bool assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, "fun @f = assert; f(true);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // It should be possible to call functions through function pointers returned by an expression // http://www.gamedev.net/topic/627386-bug-with-parsing-of-callable-expressions/ { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("Test", "funcdef void F(int); \n" "array<F@> arr(1); \n" "F@ g() \n" "{ \n" " return test; \n" "} \n" "void test(int a) \n" "{ \n" " assert(a == 42); \n" " called++; \n" "} \n" "int called = 0; \n" "void f() \n" "{ \n" " @arr[0] = test; \n" " arr[0](42); \n" " g()(42); \n" " F@ p; \n" " (@p = arr[0])(42); \n" " (@p = g())(42); \n" "} \n"); // engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, false); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "f()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; int idx = mod->GetGlobalVarIndexByName("called"); int *called = (int*)mod->GetAddressOfGlobalVar(idx); if( *called != 4 ) TEST_FAILED; engine->Release(); } // Global function pointers must not overload local class methods // Local variables take precedence over class methods // http://www.gamedev.net/topic/626746-function-call-operators-in-the-future/ { const char *script = "funcdef void FUNC(); \n" "FUNC @func; \n" "class Class \n" "{ \n" " void func() {} \n" " void method() \n" " { \n" " func(); \n" // Should call Class::func() " } \n" " void func2() {} \n" " void method2() \n" " { \n" " FUNC @func2; \n" " func2(); \n" // Should call variable func2 " } \n" "} \n"; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Class c; c.method();", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "Class c; c.method2();", mod); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; asIScriptEngine *engine; asIScriptModule *mod; asIScriptContext *ctx; // Simulate a step-by-step execution with variable inspection { CMyDebugger debug; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterStdString(engine); RegisterScriptArray(engine, true); debug.RegisterToStringCallback(engine->GetObjectTypeByName("string"), StringToString); debug.RegisterToStringCallback(engine->GetObjectTypeByName("array"), ArrayToString); const char *script = "void func(int a, const int &in b, string c, const string &in d, type @e, type &f, type @&in g) \n" "{ \n" " array<int> arr = {1,2,3}; \n" "} \n" "string glob = 'test';\n" "class type {} \n"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); ctx->SetLineCallback(asMETHOD(CMyDebugger, LineCallback), &debug, asCALL_THISCALL); debug.InterpretCommand("s", ctx); r = ExecuteString(engine, "type t; func(1, 2, 'c', 'd', t, t, t)", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( debug.output != "ExecuteString:1; void ExecuteString()\n" "ExecuteString:1; void ExecuteString()\n" "string glob = (len=4) \"test\"\n" "script:0; type@ type()\n" "string glob = (len=4) \"test\"\n" "script:0; type::type()\n" "type t = {XXXXXXXX}\n" "ExecuteString:1; void ExecuteString()\n" "int a = 1\n" "const int& b = 2\n" "string c = (len=1) \"c\"\n" "const string& d = (len=1) \"d\"\n" "type@ e = {XXXXXXXX}\n" "type& f = {XXXXXXXX}\n" "type@& g = {XXXXXXXX}\n" "string glob = (len=4) \"test\"\n" "script:3; void func(int, const int&in, string, const string&in, type@, type&inout, type@&in)\n" "int a = 1\n" "const int& b = 2\n" "string c = (len=1) \"c\"\n" "const string& d = (len=1) \"d\"\n" "type@ e = {XXXXXXXX}\n" "type& f = {XXXXXXXX}\n" "type@& g = {XXXXXXXX}\n" "string glob = (len=4) \"test\"\n" "script:3; void func(int, const int&in, string, const string&in, type@, type&inout, type@&in)\n" "{unnamed}:0; int[]@ factstub(int&in) { repeat int }\n" "int a = 1\n" "const int& b = 2\n" "string c = (len=1) \"c\"\n" "const string& d = (len=1) \"d\"\n" "type@ e = {XXXXXXXX}\n" "type& f = {XXXXXXXX}\n" "type@& g = {XXXXXXXX}\n" "int[] arr = {YYYYYYYY}\n" "(len=3) [1, 2, 3]\n" "string glob = (len=4) \"test\"\n" "script:4; void func(int, const int&in, string, const string&in, type@, type&inout, type@&in)\n" "type t = {XXXXXXXX}\n" "ExecuteString:2; void ExecuteString()\n" ) { PRINTF("%s", debug.output.c_str()); TEST_FAILED; } ctx->Release(); engine->Release(); } // Test inspecting a script object // http://www.gamedev.net/topic/627854-debugger-crashes-when-evaluating-uninitialized-object-expression/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "class CTest \n" "{ \n" " CTest() { value = 42; } \n" " int value; \n" "} \n" "int t = 24; \n" "void Func() \n" "{ \n" " CTest t; \n" " assert( t.value == 42 ); \n" "} \n" "namespace foo { float pi = 3.14f; } \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; CMyDebugger2 debug; ctx = engine->CreateContext(); ctx->SetLineCallback(asMETHOD(CMyDebugger, LineCallback), &debug, asCALL_THISCALL); // Set a break point on the line where the object will be created debug.InterpretCommand("b test:9", ctx); // Set a break point after the object has been created debug.InterpretCommand("b test:10", ctx); ctx->Prepare(mod->GetFunctionByName("Func")); // Before the function is actually executed the variables don't exist if( ctx->IsVarInScope(0) ) TEST_FAILED; if( ctx->GetAddressOfVar(0) ) TEST_FAILED; // It will break twice on line 8. Once when setting up the function stack frame, and then on the first line that is executed // TODO: The first SUSPEND in the bytecode should be optimized away as it is unnecessary for( int n = 0; n < 2; n++ ) { r = ctx->Execute(); if( r != asEXECUTION_SUSPENDED ) TEST_FAILED; // Now we should be on the line where the object will be created created if( ctx->GetLineNumber() != 9 ) TEST_FAILED; else { // The address should be null asIScriptObject *obj = (asIScriptObject*)ctx->GetAddressOfVar(0); if( obj != 0 ) TEST_FAILED; debug.PrintValue("t", ctx); } } r = ctx->Execute(); if( r != asEXECUTION_SUSPENDED ) TEST_FAILED; // Now we should be on the line after the object has been created if( ctx->GetLineNumber() != 10 ) TEST_FAILED; else { if( !ctx->IsVarInScope(0) ) TEST_FAILED; asIScriptObject *obj = (asIScriptObject*)ctx->GetAddressOfVar(0); if( obj == 0 ) TEST_FAILED; if( *(int*)obj->GetAddressOfProperty(0) != 42 ) TEST_FAILED; debug.PrintValue("t", ctx); // Print the value of the local variable debug.PrintValue("::t", ctx); // Print the value of the global variable debug.PrintValue("foo::pi", ctx); // Print the value of the global variable in namespace } if( debug.output != "Setting break point in file 'test' at line 9\n" "Setting break point in file 'test' at line 10\n" "Reached break point 0 in file 'test' at line 9\n" "test:9; void Func()\n" "24\n" "Reached break point 0 in file 'test' at line 9\n" "test:9; void Func()\n" "24\n" "Reached break point 1 in file 'test' at line 10\n" "test:10; void Func()\n" "{XXXXXXXX}\n" " int value = 42\n" "24\n" "3.14\n") { PRINTF("%s", debug.output.c_str()); TEST_FAILED; } ctx->Release(); engine->Release(); } return fail; }
bool Test() { bool fail = false; int r; COutStream out; CBufferedOutStream bout; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "Test()", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; printf("%s: Execution failed: %d\n", TESTNAME, r); } if( ctx ) ctx->Release(); engine->Release(); // Test value class with unsafe ref { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class Good \n" "{ \n" " vector3 _val; \n" " Good(const vector3& in val) \n" " { \n" " _val = val; \n" " } \n" "}; \n" "class Bad \n" "{ \n" " vector3 _val; \n" " Bad(const vector3& val) \n" " { \n" " _val = val; \n" " } \n" "}; \n" "void test() \n" "{ \n" " // runs fine \n" " for (int i = 0; i < 2; i++) \n" " Good(vector3(1, 2, 3)); \n" " // causes vm stack corruption \n" " for (int i = 0; i < 2; i++) \n" " Bad(vector3(1, 2, 3)); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "test()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test ref to primitives { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "void func(){ \n" " float a; \n" " uint8 b; \n" " int c; \n" " funcA(c, a, b); \n" "} \n" "void funcA(float& a, uint8& b, int& c) {} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestUnsafeRef (1, 1) : Info : Compiling void func()\n" "TestUnsafeRef (5, 3) : Error : No matching signatures to 'funcA(int, float, uint8)'\n" "TestUnsafeRef (5, 3) : Info : Candidates are:\n" "TestUnsafeRef (5, 3) : Info : void funcA(float&inout, uint8&inout, int&inout)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test problem found by TheAtom // Passing an inout reference to a handle to a function wasn't working properly { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class T { int a; } \n" "void f(T@& p) { \n" " T t; \n" " t.a = 42; \n" " @p = t; \n" // or p=t; in which case t is copied "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "T @t; f(t); assert( t.a == 42 );\n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; const char *script = "void Test() {} \n" "class A : I { void i(float) {} void a(int) {} float f; } \n" "class B : A { B(int) {} } \n" "interface I { void i(float); } \n" "float a; \n" "const float aConst = 3.141592f; \n" "I@ i; \n" "enum E { eval = 0, eval2 = 2 } \n" "E e; \n" "typedef float real; \n" "real pi = 3.14f; \n" "import void ImpFunc() from \"mod\"; \n"; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); float f; engine->RegisterTypedef("myFloat", "float"); engine->RegisterGlobalProperty("myFloat f", &f); engine->RegisterGlobalProperty("const float myConst", &f); engine->RegisterGlobalFunction("void func(int &in)", asFUNCTION(0), asCALL_GENERIC); engine->BeginConfigGroup("test"); engine->RegisterGlobalFunction("void func2()", asFUNCTION(0), asCALL_GENERIC); engine->EndConfigGroup(); engine->RegisterEnum("myEnum"); engine->RegisterEnumValue("myEnum", "value1", 1); engine->RegisterEnumValue("myEnum", "value2", 2); engine->RegisterFuncdef("void Callback(int a, int b)"); engine->RegisterInterface("MyIntf"); engine->RegisterInterfaceMethod("MyIntf", "void func() const"); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; WriteConfigToFile(engine, "AS_DEBUG/config.txt"); DumpModule(mod); // Save/Restore the bytecode and then test again for the loaded bytecode CBytecodeStream stream(__FILE__"1"); mod->SaveByteCode(&stream); mod = engine->GetModule("2", asGM_ALWAYS_CREATE); mod->LoadByteCode(&stream); DumpModule(mod); engine->Release(); return fail; }
bool Test() { bool fail = false; asIScriptEngine *engine; int r; COutStream out; CBufferedOutStream bout; { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "int func() { return var; } \n" "int func2() { return var; } \n" "int var = 0; \n" "class cl { cl() {v = 0;} int v; } \n" "interface i {} \n" "enum e { e1 = 0 } \n" "funcdef void fd(); \n" // Namespaces allow same entities to be declared again "namespace a { \n" " int func() { return var; } \n" // Should find the global var in the same namespace " int func2() { return func(); } \n" // Should find the global function in the same namespace " int var = 1; \n" " class cl { cl() {v = 1;} int v; } \n" " interface i {} \n" " enum e { e1 = 1 } \n" " funcdef void fd(); \n" " ::e MyFunc() { return ::e1; } \n" // Nested namespaces are allowed " namespace b { \n" " int var = 2; \n" " void funcParams(int a, float b) { a+b; } \n" " } \n" // Accessing global variables from within a namespace is also possible " int getglobalvar() { return ::var; } \n" "} \n" // The class should be able to declare methods to return and take types from other namespaces "class MyClass { \n" " a::e func(a::e val) { return val; } \n" " ::e func(::e val) { return val; } \n" "} \n" // Global functions must also be able to return and take types from other namespaces "a::e MyFunc(a::e val) { return val; } \n" // It's possible to specify exactly which one is wanted "cl gc; \n" "a::cl gca; \n" "void main() \n" "{ \n" " assert(var == 0); \n" " assert(::var == 0); \n" " assert(a::var == 1); \n" " assert(a::b::var == 2); \n" " assert(func() == 0); \n" " assert(a::func() == 1); \n" " assert(func2() == 0); \n" " assert(a::func2() == 1); \n" " assert(e1 == 0); \n" " assert(::e1 == 0); \n" " assert(e::e1 == 0); \n" " assert(::e::e1 == 0); \n" " assert(a::e1 == 1); \n" " assert(a::e::e1 == 1); \n" " cl c; \n" " a::cl ca; \n" " assert(c.v == 0); \n" " assert(ca.v == 1); \n" " assert(gc.v == 0); \n" " assert(gca.v == 1); \n" " assert(a::getglobalvar() == 0); \n" "} \n"; asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Retrieving entities should work properly with namespace mod->SetDefaultNamespace("a"); asIScriptFunction *f1 = mod->GetFunctionByDecl("int func()"); asIScriptFunction *f2 = mod->GetFunctionByDecl("int a::func()"); asIScriptFunction *f3 = mod->GetFunctionByName("func"); if( f1 == 0 || f1 != f2 || f1 != f3 ) TEST_FAILED; int v1 = mod->GetGlobalVarIndexByName("var"); int v2 = mod->GetGlobalVarIndexByDecl("int var"); int v3 = mod->GetGlobalVarIndexByDecl("int a::var"); if( v1 < 0 || v1 != v2 || v1 != v3 ) TEST_FAILED; int t1 = mod->GetTypeIdByDecl("cl"); int t2 = mod->GetTypeIdByDecl("a::cl"); if( t1 < 0 || t1 != t2 ) TEST_FAILED; // Functions with parameters must work too asIScriptFunction *f = mod->GetFunctionByDecl("void a::b::funcParams(int, float)"); if( f == 0 || std::string(f->GetDeclaration(true, true)) != "void a::b::funcParams(int, float)" ) TEST_FAILED; // Test saving and loading CBytecodeStream s(""); mod->SaveByteCode(&s); asIScriptModule *mod2 = engine->GetModule("mod2", asGM_ALWAYS_CREATE); r = mod2->LoadByteCode(&s); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod2); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test registering interface with namespace { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); r = engine->SetDefaultNamespace("test"); assert( r >= 0 ); r = engine->RegisterObjectType("t", 0, asOBJ_REF); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("t", asBEHAVE_ADDREF, "void f()", asFUNCTION(0), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectMethod("t", "void f()", asFUNCTION(0), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectProperty("t", "int a", 0); assert( r >= 0 ); int t1 = engine->GetTypeIdByDecl("t"); int t2 = engine->GetTypeIdByDecl("test::t"); if( t1 < 0 || t1 != t2 ) TEST_FAILED; r = engine->RegisterInterface("i"); assert( r >= 0 ); r = engine->RegisterInterfaceMethod("i", "void f()"); assert( r >= 0 ); t1 = engine->GetTypeIdByDecl("test::i"); if( t1 < 0 ) TEST_FAILED; r = engine->RegisterEnum("e"); assert( r >= 0 ); r = engine->RegisterEnumValue("e", "e1", 0); assert( r >= 0 ); t1 = engine->GetTypeIdByDecl("test::e"); if( t1 < 0 ) TEST_FAILED; r = engine->RegisterFuncdef("void f()"); assert( r >= 0 ); t1 = engine->GetTypeIdByDecl("test::f"); if( t1 < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void gf()", asFUNCTION(0), asCALL_GENERIC); assert( r >= 0 ); asIScriptFunction *f1 = engine->GetGlobalFunctionByDecl("void test::gf()"); asIScriptFunction *f2 = engine->GetGlobalFunctionByDecl("void gf()"); if( f1 == 0 || f1 != f2 ) TEST_FAILED; r = engine->RegisterGlobalProperty("int gp", (void*)1); assert( r >= 0 ); int g1 = engine->GetGlobalPropertyIndexByName("gp"); int g2 = engine->GetGlobalPropertyIndexByDecl("int gp"); int g3 = engine->GetGlobalPropertyIndexByDecl("int test::gp"); if( g1 < 0 || g1 != g2 || g1 != g3 ) TEST_FAILED; r = engine->RegisterTypedef("td", "int"); assert( r >= 0 ); t1 = engine->GetTypeIdByDecl("test::td"); if( t1 < 0 ) TEST_FAILED; engine->Release(); } // Test accessing registered properties in different namespaces from within function in another namespace // http://www.gamedev.net/topic/624376-accessing-global-property-from-within-script-namespace/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); int a = 0, b = 0; r = engine->RegisterGlobalProperty("int a", &a); r = engine->SetDefaultNamespace("test"); r = engine->RegisterGlobalProperty("int b", &b); asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "namespace script { \n" "void func() \n" "{ \n" " a = 1; \n" "} \n" "} \n"); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) TEST_FAILED; // TODO: Should have better error message. Perhaps show variables declared in other scopes if( bout.buffer != "test (2, 1) : Info : Compiling void func()\n" "test (4, 3) : Error : 'a' is not declared\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } mod->AddScriptSection("test", "namespace script { \n" "void func() \n" "{ \n" " ::a = 1; \n" " test::b = 2; \n" "} \n" "} \n"); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "script::func()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( a != 1 || b != 2 ) TEST_FAILED; engine->Release(); } // Test registering enum with the same name in two different namespaces // http://www.gamedev.net/topic/625214-enum-collision-across-namespaces/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->SetDefaultNamespace("A"); r = engine->RegisterEnum("ENUM"); assert( r >= 0 ); r = engine->RegisterEnumValue("ENUM", "VALUE", 1); assert( r >= 0 ); engine->SetDefaultNamespace("B"); r = engine->RegisterEnum("ENUM"); assert( r >= 0 ); r = engine->RegisterEnumValue("ENUM", "VALUE", 2); assert( r >= 0 ); engine->SetDefaultNamespace(""); r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); r = ExecuteString(engine, "int a = A::ENUM::VALUE; assert(a == 1)", 0, 0); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "int b = B::ENUM::VALUE; assert(b == 2)", 0, 0); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It shouldn't be necessary to inform the name of the enum // type as the engine property is not set to enforce that r = ExecuteString(engine, "int a = A::VALUE; assert(a == 1)", 0, 0); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "int b = B::VALUE; assert(b == 2)", 0, 0); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, true); r = ExecuteString(engine, "int a = A::VALUE; assert(a == 1)", 0, 0); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 9) : Error : 'A::VALUE' is not declared\n" "ExecuteString (1, 28) : Warning : 'a' is not initialized.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // http://www.gamedev.net/topic/626970-2240-cannot-instantiate-a-class-outside-of-its-namespace/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "namespace TestNamespace \n" "{ \n" " class MyHappyClass \n" " { \n" " MyHappyClass () \n" " { \n" " } \n" " void DoSomething () \n" " { \n" " } \n" " } \n" "} \n" "void main () \n" "{ \n" " TestNamespace::MyHappyClass ClassInstance; \n" " \n" " ClassInstance.DoSomething (); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->Release(); } // http://www.gamedev.net/topic/626314-compiler-error-when-using-namespace-with-the-array-addon/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "namespace A \n" "{ \n" " class Test {} \n" "} \n" "void main () \n" "{ \n" " array<A::Test@> a; \n" " A::Test@[] b; \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->Release(); } // It should be possible to register types with the same name in different namespaces { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); r = engine->RegisterObjectType("TestObj", 0, asOBJ_REF); if( r < 0 ) TEST_FAILED; engine->SetDefaultNamespace("A"); r = engine->RegisterObjectType("TestObj", 0, asOBJ_REF); if( r < 0 ) TEST_FAILED; r = engine->RegisterObjectType("TestObj", 0, asOBJ_REF); if( r != asALREADY_REGISTERED ) TEST_FAILED; engine->SetDefaultNamespace(""); asIObjectType *o1 = engine->GetObjectTypeByName("TestObj"); engine->SetDefaultNamespace("A"); asIObjectType *o2 = engine->GetObjectTypeByName("TestObj"); if( o1 == 0 || o2 == 0 ) TEST_FAILED; if( o1 == o2 ) TEST_FAILED; engine->Release(); } // Dynamically adding functions/variables to modules should also support namespaces { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule("test", asGM_ALWAYS_CREATE); asIScriptFunction *f1; r = mod->CompileFunction("", "void func() {}", 0, asCOMP_ADD_TO_MODULE, &f1); if( r < 0 ) TEST_FAILED; mod->SetDefaultNamespace("A"); asIScriptFunction *f2; r = mod->CompileFunction("", "void func() {}", 0, asCOMP_ADD_TO_MODULE, &f2); if( r < 0 ) TEST_FAILED; mod->SetDefaultNamespace(""); asIScriptFunction *f3 = mod->GetFunctionByName("func"); mod->SetDefaultNamespace("A"); asIScriptFunction *f4 = mod->GetFunctionByName("func"); if( f1 != f3 ) TEST_FAILED; if( f2 != f4 ) TEST_FAILED; if( f1 == f2 ) TEST_FAILED; // The functions received from CompileFunction must be released if( f1 ) f1->Release(); if( f2 ) f2->Release(); mod->SetDefaultNamespace(""); r = mod->CompileGlobalVar("", "int var;", 0); if( r < 0 ) TEST_FAILED; mod->SetDefaultNamespace("A"); r = mod->CompileGlobalVar("", "int var;", 0); if( r < 0 ) TEST_FAILED; mod->SetDefaultNamespace(""); int v1 = mod->GetGlobalVarIndexByName("var"); mod->SetDefaultNamespace("A"); int v2 = mod->GetGlobalVarIndexByName("var"); if( v1 < 0 || v2 < 0 ) TEST_FAILED; if( v1 == v2 ) TEST_FAILED; engine->Release(); } // Inheritance from class in a different namespace // http://www.gamedev.net/topic/626970-2240-cannot-instantiate-a-class-outside-of-its-namespace/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "namespace A \n" "{ \n" " interface i {} \n" " class a {} \n" " class a2 : a, i {} \n" "} \n" "class b : A::a, A::i {} \n" "namespace C \n" "{ \n" " class c : ::b {} \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; asIObjectType *type = mod->GetObjectTypeByName("b"); if( type == 0 ) TEST_FAILED; else { mod->SetDefaultNamespace("A"); if( !type->DerivesFrom(mod->GetObjectTypeByName("a")) ) TEST_FAILED; if( !type->Implements(mod->GetObjectTypeByName("i")) ) TEST_FAILED; } engine->Release(); } // Registering types with namespaces // http://www.gamedev.net/topic/628401-problem-binding-two-similarly-named-objects-in-different-namespaces/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->SetDefaultNamespace("A"); engine->RegisterEnum("ETest"); engine->RegisterObjectType("CTest", 0, asOBJ_REF| asOBJ_NOCOUNT); r = engine->RegisterObjectProperty("CTest", "ETest e", 0); if( r < 0 ) TEST_FAILED; engine->RegisterObjectType("CTest2", 0, asOBJ_REF| asOBJ_NOCOUNT); if( r < 0 ) TEST_FAILED; r = engine->RegisterObjectMethod("CTest2", "ETest Method(ETest)", asFUNCTION(0), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; // Make sure it's possible to retrieve the enum again int typeId; const char *ns; const char *e = engine->GetEnumByIndex(0, &typeId, &ns); if( std::string(e) != "ETest" ) TEST_FAILED; if( std::string(ns) != "A" ) TEST_FAILED; if( typeId != engine->GetTypeIdByDecl("ETest") ) TEST_FAILED; engine->SetDefaultNamespace(""); e = engine->GetEnumByIndex(0, &typeId, &ns); if( std::string(e) != "ETest" ) TEST_FAILED; if( std::string(ns) != "A" ) TEST_FAILED; if( typeId != engine->GetTypeIdByDecl("A::ETest") ) TEST_FAILED; engine->Release(); } // Registering properties using types from other namespaces // http://www.gamedev.net/topic/629088-looks-like-bug-with-namespaces-in-revision-1380/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterStdString(engine); engine->SetDefaultNamespace("mynamespace"); r = engine->RegisterGlobalProperty("::string val", (void*)1); // TODO: Once recursive searches in the namespaces is implemented the scope operator is not needed if( r < 0 ) TEST_FAILED; engine->Release(); } // Test problem reported by Andrew Ackermann { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); const char *script = "namespace Test { \n" " class A { \n" " } \n" "}; \n" "void init() { \n" " Test::A@ a = Test::A(); \n" "} \n"; asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->Release(); } // Test property accessors with namespaces // http://www.gamedev.net/topic/635042-indexed-property-accessors-and-namespaces/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "int get_foo() { return 42; } \n" "namespace nm { int get_foo2() { return 42; } } \n" "void test() { \n" " assert( foo == 42 ); \n" // ok " assert( ::foo == 42 ); \n" // ok " assert( nm::foo == 42 ); \n" // should fail to compile " assert( nm::foo2 == 42 ); \n" // ok " assert( foo2 == 42 ); \n" // should fail to compile "} \n" "namespace nm { \n" "void test2() { \n" " ::assert( foo == 42 ); \n" // should fail to compile " ::assert( ::foo == 42 ); \n" // ok " ::assert( nm::foo == 42 ); \n" // should fail to compile " ::assert( nm::foo2 == 42 ); \n" // ok " ::assert( foo2 == 42 ); \n" // ok "} \n" "} \n"; bout.buffer = ""; asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "test (3, 1) : Info : Compiling void test()\n" "test (6, 11) : Error : 'nm::foo' is not declared\n" "test (8, 11) : Error : 'foo2' is not declared\n" "test (11, 1) : Info : Compiling void test2()\n" "test (12, 13) : Error : 'foo' is not declared\n" "test (14, 13) : Error : 'nm::foo' is not declared\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Indexed property accessors script = "int get_foo(uint) { return 42; } \n" "namespace nm { int get_foo2(uint) { return 42; } } \n" "void test() { \n" " assert( foo[0] == 42 ); \n" // ok " assert( ::foo[0] == 42 ); \n" // ok " assert( nm::foo[0] == 42 ); \n" // should fail to compile " assert( nm::foo2[0] == 42 ); \n" // ok " assert( foo2[0] == 42 ); \n" // should fail to compile "} \n" "namespace nm { \n" "void test2() { \n" " ::assert( foo[0] == 42 ); \n" // should fail to compile " ::assert( ::foo[0] == 42 ); \n" // ok " ::assert( nm::foo[0] == 42 ); \n" // should fail to compile " ::assert( nm::foo2[0] == 42 ); \n" // ok " ::assert( foo2[0] == 42 ); \n" // ok "} \n" "} \n"; bout.buffer = ""; mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "test (3, 1) : Info : Compiling void test()\n" "test (6, 11) : Error : 'nm::foo' is not declared\n" "test (8, 11) : Error : 'foo2' is not declared\n" "test (11, 1) : Info : Compiling void test2()\n" "test (12, 13) : Error : 'foo' is not declared\n" "test (14, 13) : Error : 'nm::foo' is not declared\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test types in namespace // http://www.gamedev.net/topic/636336-member-function-chaining/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "namespace Default { \n" " void func(ButtonRenderer @) {} \n" " void Init() \n" " { \n" " func(ButtonRenderer()); \n" " } \n" " class ButtonRenderer {} \n" "} \n"; bout.buffer = ""; asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterScriptString(engine); engine->RegisterGlobalProperty("float[] @floatArray", &floatArray); engine->RegisterGlobalProperty("string[] @stringArray", &stringArray); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } else { if( (floatArray->GetArrayTypeId() & asTYPEID_MASK_OBJECT) != asTYPEID_TEMPLATE ) TEST_FAILED; if( floatArray->GetArrayTypeId() != engine->GetTypeIdByDecl("float[]") ) TEST_FAILED; if( floatArray->GetElementTypeId() != engine->GetTypeIdByDecl("float") ) TEST_FAILED; if( floatArray->GetSize() != 2 ) TEST_FAILED; if( *(float*)floatArray->At(0) != 1.1f ) TEST_FAILED; if( *(float*)floatArray->At(1) != 1.2f ) TEST_FAILED; if( stringArray->GetArrayTypeId() != engine->GetTypeIdByDecl("string[]") ) TEST_FAILED; if( stringArray->GetElementTypeId() != engine->GetTypeIdByDecl("string") ) TEST_FAILED; if( stringArray->GetSize() != 1 ) TEST_FAILED; if( ((CScriptString*)stringArray->At(0))->buffer != "test" ) TEST_FAILED; stringArray->Resize(2); } if( floatArray ) floatArray->Release(); if( stringArray ) stringArray->Release(); engine->Release(); // Success return fail; }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("AngelScript 2.25.0 WIP 7: 16.17 secs\n"); printf("AngelScript 2.25.0 WIP 8: 15.22 secs (hint for discarding temp variables)\n"); printf("AngelScript 2.25.0 WIP 9: 15.07 secs (improved bytecode optimization)\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); // engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, false); //////////////////////////////////////////// printf("\nGenerating...\n"); string script; script += "int var; \n"; script += "int func(int, int, int, int, int, int, int, int) { return 0; } \n"; script += "void main() { \n"; script += buildSwitch(numLevels); script += "} \n"; int lines = 0; for( unsigned int n = 0; n < script.size(); n++ ) if( script[n] == '\n' ) lines++; printf("Number of lines: %d\n", lines); //////////////////////////////////////////// printf("\nBuilding...\n"); double time = GetSystemTimer(); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script.c_str(), script.size(), 0); int r = mod->Build(); time = GetSystemTimer() - time; if( r != 0 ) printf("Build failed\n", TESTNAME); else printf("Time = %f secs\n", time); //////////////////////////////////////////// printf("\nSaving...\n"); time = GetSystemTimer(); CBytecodeStream stream(""); mod->SaveByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); printf("Size = %d\n", int(stream.buffer.size())); //////////////////////////////////////////// printf("\nLoading...\n"); time = GetSystemTimer(); asIScriptModule *mod2 = engine->GetModule(0, asGM_ALWAYS_CREATE); mod2->LoadByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); engine->Release(); }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("AngelScript 2.25.1 WIP 0: 1.59 secs\n"); printf("AngelScript 2.25.1 WIP 1: 1.66 secs (local bytecode optimizations)\n"); printf("AngelScript 2.25.1 WIP 2: 1.60 secs (reversed order)\n"); printf("AngelScript 2.28.1 WIP: 1.08 secs\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_CDECL); //////////////////////////////////////////// printf("\nGenerating...\n"); const int numArrays = 2; #ifdef _DEBUG const int numElements = 10; #else const int numElements = 100000; #endif string script; std::stringstream script_buffer; for (unsigned i = 0; i < numArrays; i++) { script_buffer << "int[] array_" << i << " = {"; if (numElements > 0) { script_buffer << 0; } for (unsigned j = 1; j < numElements; j++) { script_buffer << ", " << j; } script_buffer << "};"; } script_buffer << std::endl << "int main() { print (\"elem 999 = \" + array_0[999] + \"\\n\"); return 0; }"; script = script_buffer.str(); //////////////////////////////////////////// printf("\nBuilding...\n"); double time = GetSystemTimer(); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script.c_str(), script.size(), 0); int r = mod->Build(); time = GetSystemTimer() - time; if( r != 0 ) printf("Build failed\n", TESTNAME); else printf("Time = %f secs\n", time); //////////////////////////////////////////// printf("\nSaving...\n"); time = GetSystemTimer(); CBytecodeStream stream(""); mod->SaveByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); printf("Size = %d\n", int(stream.buffer.size())); //////////////////////////////////////////// printf("\nLoading...\n"); time = GetSystemTimer(); asIScriptModule *mod2 = engine->GetModule(0, asGM_ALWAYS_CREATE); mod2->LoadByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); engine->Release(); }
bool Test() { bool fail = false; int r; CBufferedOutStream bout; COutStream out; asIScriptModule *mod; float val; // Test access masks for delegates without the first bit set // http://www.gamedev.net/topic/673050-bug-with-access-masks/ { asIScriptEngine *engine = asCreateScriptEngine(); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); engine->RegisterGlobalFunction("void Print(const string& in s)", asFUNCTION(0), asCALL_GENERIC); mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->SetAccessMask(2); mod->AddScriptSection("test", "funcdef void Foo();\n" "Foo@ g_pFoo = null;\n" "class Bar\n" "{\n" " Bar()\n" " {\n" " @g_pFoo = Foo(this.bar);\n" " }\n" " void bar()\n" " {\n" " }\n" "}\n" "void test()\n" "{\n" " Bar bar;\n" " g_pFoo();\n" "}\n"); r = mod->Build(); if (r < 0) TEST_FAILED; engine->ShutDownAndRelease(); } //------------ // Test global properties asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetDefaultAccessMask(1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = engine->BeginConfigGroup("group"); assert( r >= 0 ); r = engine->RegisterGlobalProperty("float val", &val); assert( r >= 0 ); r = engine->EndConfigGroup(); assert( r >= 0 ); // Make sure the default access is granted r = ExecuteString(engine, "val = 1.3f"); if( r < 0 ) TEST_FAILED; // Make sure the default access can be turned off engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule("ExecuteString", asGM_ALWAYS_CREATE); mod->SetAccessMask(2); r = ExecuteString(engine, "val = 1.0f", mod); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 1) : Error : No matching symbol 'val'\n") TEST_FAILED; // Make sure the default access can be overridden engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); mod->SetAccessMask(1); r = ExecuteString(engine, "val = 1.0f", mod); if( r < 0 ) TEST_FAILED; engine->Release(); //---------- // Test global functions engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->SetDefaultAccessMask(2); r = engine->BeginConfigGroup("group"); assert( r >= 0 ); r = engine->RegisterGlobalFunction("void Func()", asFUNCTION(Func), asCALL_GENERIC); assert( r >= 0 ); r = engine->EndConfigGroup(); assert( r >= 0 ); mod = engine->GetModule("ExecuteString", asGM_ALWAYS_CREATE); mod->SetAccessMask(1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); bout.buffer = ""; r = ExecuteString(engine, "Func()", mod); if( r >= 0 ) TEST_FAILED; if (bout.buffer != "ExecuteString (1, 1) : Error : No matching symbol 'Func'\n") { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } mod->SetAccessMask(2); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = ExecuteString(engine, "Func()", mod); if( r < 0 ) TEST_FAILED; engine->Release(); //------------ // Test object types engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetDefaultAccessMask(1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = engine->BeginConfigGroup("group"); assert( r >= 0 ); r = engine->RegisterObjectType("mytype", sizeof(int), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE); assert( r >= 0 ); r = engine->EndConfigGroup(); assert( r >= 0 ); mod = engine->GetModule("ExecuteString", asGM_ALWAYS_CREATE); mod->SetAccessMask(2); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); bout.buffer = ""; r = ExecuteString(engine, "mytype a", mod); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 1) : Error : Type 'mytype' is not available for this module\n") TEST_FAILED; engine->Release(); //------------ // Test class members in different config groups engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = engine->RegisterObjectType("mytype", sizeof(int), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE); assert( r >= 0 ); r = engine->SetDefaultAccessMask(2); r = engine->RegisterObjectMethod("mytype", "mytype opAdd(mytype &in)", asFUNCTION(TypeAdd), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectProperty("mytype", "int val", 0); assert( r >= 0 ); mod = engine->GetModule("ExecuteString", asGM_ALWAYS_CREATE); mod->SetAccessMask(1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); bout.buffer = ""; r = ExecuteString(engine, "mytype a; a + a; a.val + a.val; a.opAdd(a);", mod); // It should be possible to disallow individual class methods if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 13) : Error : No matching operator that takes the types 'mytype' and 'mytype' found\n" "ExecuteString (1, 19) : Error : 'val' is not a member of 'mytype'\n" "ExecuteString (1, 35) : Error : No matching symbol 'opAdd'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); // Success return fail; }
bool Test() { RET_ON_MAX_PORT bool fail = false; COutStream out; CBufferedOutStream bout; int r; asIScriptEngine *engine = 0; { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterStdString(engine); RegisterScriptArray(engine, false); RegisterScriptFile(engine); RegisterScriptFileSystem(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "void main() { \n" " filesystem fs; \n" // starts in applications working dir " array<string> dirs = fs.getDirs(); \n" " assert( dirs.find('scripts') >= 0 ); \n" " fs.changeCurrentPath('scripts'); \n" // move to the sub directory " array<string> files = fs.getFiles(); \n" // get the script files in the directory " assert( files.length() == 2 ); \n" " file f; \n" " f.open('scripts/include.as', 'r'); \n" " string str = f.readLine(); \n" " str = str.substr(3, 25); \n" " assert( str == 'void MyIncludedFunction()' ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "main()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION && ctx->GetExceptionLineNumber() == 4 ) PRINTF("Failed to find the sub directory 'scripts'. Are you running the test from the correct folder?\n"); else TEST_FAILED; } ctx->Release(); if( bout.buffer != "" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); RegisterStdString(engine); RegisterScriptFile(engine); const char *script = "file f; \n" "int r = f.open(\"scripts/TestExecuteScript.as\", \"r\"); \n" "if( r >= 0 ) { \n" " assert( f.getSize() > 0 ); \n" " string s1 = f.readString(10000); \n" " assert( s1.length() == uint(f.getSize()) ); \n" " f.close(); \n" " f.open('scripts/TestExecuteScript.as', 'r'); \n" " string s2; \n" " while( !f.isEndOfFile() ) \n" " { \n" " string s3 = f.readLine(); \n" " s2 += s3; \n" " } \n" " assert( s1 == s2 ); \n" " f.close(); \n" "} \n"; r = ExecuteString(engine, script); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } engine->Release(); } return fail; }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("AngelScript 2.25.1 WIP 0: 0.34 secs\n"); printf("AngelScript 2.25.1 WIP 1: 0.33 secs (local bytecode optimizations)\n"); printf("AngelScript 2.25.1 WIP 2: 0.32 secs (reversed order)\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); //////////////////////////////////////////// printf("\nGenerating...\n"); const int numSymbols = 10000; string script; script.reserve(numSymbols * 30); for( int i = 0; i < numSymbols; i++ ) { char buf[500]; sprintf(buf, "const int const_%d = %d;\n", i, i); script += buf; } //////////////////////////////////////////// printf("\nBuilding...\n"); double time = GetSystemTimer(); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script.c_str(), script.size(), 0); int r = mod->Build(); time = GetSystemTimer() - time; if( r != 0 ) printf("Build failed\n", TESTNAME); else printf("Time = %f secs\n", time); //////////////////////////////////////////// printf("\nSaving...\n"); time = GetSystemTimer(); CBytecodeStream stream(""); mod->SaveByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); printf("Size = %d\n", int(stream.buffer.size())); //////////////////////////////////////////// printf("\nLoading...\n"); time = GetSystemTimer(); asIScriptModule *mod2 = engine->GetModule(0, asGM_ALWAYS_CREATE); mod2->LoadByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); engine->Release(); }
bool Test() { RET_ON_MAX_PORT int r; bool fail = false; COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterScriptString(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); // We'll test two things with this function // 1. The native interface is able to pass byte parameters correctly // 2. The native interface is able to return byte values correctly engine->RegisterGlobalFunction("int8 RetInt8(int8)", asFUNCTION(RetInt8), asCALL_CDECL); char var = 0; engine->RegisterGlobalProperty("int8 gvar", &var); ExecuteString(engine, "gvar = RetInt8(1)"); if( var != 1 ) { PRINTF("failed to return value correctly\n"); TEST_FAILED; } ExecuteString(engine, "Assert(RetInt8(1) == 1)"); // Test to make sure bools can be passed to member functions properly engine->RegisterObjectType("Int8Tester", 0, asOBJ_REF | asOBJ_NOHANDLE); engine->RegisterObjectMethod("Int8Tester", "void Test1(int8)", asMETHOD(TestInt8Class, Test1), asCALL_THISCALL); engine->RegisterObjectMethod("Int8Tester", "void Test0(int8)", asMETHOD(TestInt8Class, Test0), asCALL_THISCALL); TestInt8Class testInt8; r = engine->RegisterGlobalProperty("Int8Tester TestInt8Class", &testInt8 ); if( r < 0 ) TEST_FAILED; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script3, strlen(script3)); r = mod->Build(); if( r < 0 ) { TEST_FAILED; } else { r = ExecuteString(engine, "TestInt8ToMember();", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( testInt8.m_fail ) TEST_FAILED; } // Shift operations with int8 should result in int32 r = ExecuteString(engine, "uint8[] buf={1,2,3,4,5,6}; " "uint32 ver; " "ver = buf[0]; " "ver |= buf[1]<<8; " "ver |= buf[2]<<16; " "ver |= buf[3]<<24; " "Assert(ver == 0x04030201);"); // If this is changed to 0x01020304 Avira accuses the compiled obj file as virus if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } engine->Release(); return fail; }
bool Test() { if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { printf("Skipped due to AS_MAX_PORTABILITY\n"); return false; } bool fail = false; int r; CBufferedOutStream bout; COutStream out; asIScriptModule *mod; asIScriptEngine *engine; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterScriptString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); // The getter can return a handle while the setter takes a reference { const char *script = "class Test \n" "{ \n" " string @get_s() { return 'test'; } \n" " void set_s(const string &in) {} \n" "} \n" "void func() \n" "{ \n" " Test t; \n" " string s = t.s; \n" " t.s = s; \n" "} \n"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } r = ExecuteString(engine, "Test t; @t.s = 'test';", mod); if( r >= 0 ) { TEST_FAILED; printf("Shouldn't be allowed\n"); } if( bout.buffer != "ExecuteString (1, 14) : Error : It is not allowed to perform a handle assignment on a non-handle property\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } } // main1 and main2 should produce the same bytecode const char *script1 = "class Test \n" "{ \n" " int get_prop() { return _prop; } \n" " void set_prop(int v) { _prop = v; } \n" " int _prop; \n" "} \n" "void main1() \n" "{ \n" " Test t; \n" " t.set_prop(42); \n" " assert( t.get_prop() == 42 ); \n" "} \n" "void main2() \n" "{ \n" " Test t; \n" " t.prop = 42; \n" " assert( t.prop == 42 ); \n" "} \n"; mod->AddScriptSection("script", script1); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main1()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } r = ExecuteString(engine, "main2()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test compound assignment with accessors (not allowed) const char *script2 = "class Test \n" "{ \n" " void set_prop(int v) { _prop = v; } \n" " int _prop; \n" "} \n" "void main1() \n" "{ \n" " Test t; \n" " t.prop += 42; \n" "} \n"; mod->AddScriptSection("script", script2); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) { TEST_FAILED; } if( bout.buffer != "script (6, 1) : Info : Compiling void main1()\n" "script (9, 10) : Error : Compound assignments with property accessors are not allowed\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test get accessor with boolean operators const char *script3 = "class Test \n" "{ \n" " bool get_boolProp() { return true; } \n" "} \n" "void main1() \n" "{ \n" " Test t; \n" " if( t.boolProp ) {} \n" " if( t.boolProp && true ) {} \n" " if( false || t.boolProp ) {} \n" " if( t.boolProp ^^ t.boolProp ) {} \n" " if( !t.boolProp ) {} \n" " t.boolProp ? t.boolProp : t.boolProp; \n" "} \n"; mod->AddScriptSection("script", script3); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test get accessor with math operators const char *script4 = "class Test \n" "{ \n" " float get_prop() { return 1.0f; } \n" "} \n" "void main1() \n" "{ \n" " Test t; \n" " float f = t.prop * 1; \n" " f = (t.prop) + 1; \n" " 10 / t.prop; \n" " -t.prop; \n" "} \n"; mod->AddScriptSection("script", script4); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test get accessor with bitwise operators const char *script5 = "class Test \n" "{ \n" " uint get_prop() { return 1; } \n" "} \n" "void main1() \n" "{ \n" " Test t; \n" " t.prop << t.prop; \n" " t.prop & t.prop; \n" " ~t.prop; \n" "} \n"; mod->AddScriptSection("script", script5); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test multiple get accessors for same property. Should give error // Test multiple set accessors for same property. Should give error const char *script6 = "class Test \n" "{ \n" " uint get_p() {return 0;} \n" " float get_p() {return 0;} \n" " void set_s(float) {} \n" " void set_s(uint) {} \n" "} \n" "void main() \n" "{ \n" " Test t; \n" " t.p; \n" " t.s = 0; \n" "} \n"; mod->AddScriptSection("script", script6); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "script (8, 1) : Info : Compiling void main()\n" "script (11, 4) : Error : Found multiple get accessors for property 'p'\n" "script (11, 4) : Info : uint Test::get_p()\n" "script (11, 4) : Info : float Test::get_p()\n" "script (12, 4) : Error : Found multiple set accessors for property 's'\n" "script (12, 4) : Info : void Test::set_s(float)\n" "script (12, 4) : Info : void Test::set_s(uint)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test mismatching type between get accessor and set accessor. Should give error const char *script7 = "class Test \n" "{ \n" " uint get_p() {return 0;} \n" " void set_p(float) {} \n" "} \n" "void main() \n" "{ \n" " Test t; \n" " t.p; \n" "} \n"; mod->AddScriptSection("script", script7); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "script (6, 1) : Info : Compiling void main()\n" "script (9, 4) : Error : The property 'p' has mismatching types for the get and set accessors\n" "script (9, 4) : Info : uint Test::get_p()\n" "script (9, 4) : Info : void Test::set_p(float)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test only set accessor for read expression // Test only get accessor for write expression const char *script8 = "class Test \n" "{ \n" " uint get_g() {return 0;} \n" " void set_s(float) {} \n" "} \n" "void main() \n" "{ \n" " Test t; \n" " t.g = 0; \n" " t.s + 1; \n" "} \n"; mod->AddScriptSection("script", script8); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "script (6, 1) : Info : Compiling void main()\n" "script (9, 7) : Error : The property has no set accessor\n" "script (10, 7) : Error : The property has no get accessor\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test pre and post ++. Should fail, since the expression is not a variable const char *script9 = "class Test \n" "{ \n" " uint get_p() {return 0;} \n" " void set_p(uint) {} \n" "} \n" "void main() \n" "{ \n" " Test t; \n" " t.p++; \n" " --t.p; \n" "} \n"; mod->AddScriptSection("script", script9); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) { TEST_FAILED; printf("Didn't fail to compile the script\n"); } if( bout.buffer != "script (6, 1) : Info : Compiling void main()\n" "script (9, 6) : Error : Invalid reference. Property accessors cannot be used in combined read/write operations\n" "script (10, 3) : Error : Invalid reference. Property accessors cannot be used in combined read/write operations\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test using property accessors from within class methods without 'this' // Test accessor where the object is a handle const char *script10 = "class Test \n" "{ \n" " uint get_p() {return 0;} \n" " void set_p(uint) {} \n" " void test() \n" " { \n" " p = 0; \n" " int a = p; \n" " } \n" "} \n" "void func() \n" "{ \n" " Test @a = Test(); \n" " a.p = 1; \n" " int b = a.p; \n" "} \n"; mod->AddScriptSection("script", script10); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "func()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test accessors with function arguments (by value, in ref, out ref) const char *script11 = "class Test \n" "{ \n" " uint get_p() {return 0;} \n" " void set_p(uint) {} \n" "} \n" "void func() \n" "{ \n" " Test a(); \n" " byVal(a.p); \n" " inArg(a.p); \n" " outArg(a.p); \n" "} \n" "void byVal(int v) {} \n" "void inArg(int &in v) {} \n" "void outArg(int &out v) {} \n"; mod->AddScriptSection("script", script11); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "func()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // When the property is an object type, then the set accessor should be // used instead of the overloaded assignment operator to set the value. // Properties of object properties, must allow having different // types for get and set. IsEqualExceptConstAndRef should be used. const char *script12 = "class Test \n" "{ \n" " string get_s() {return _s;} \n" " void set_s(const string &in n) {_s = n;} \n" " string _s; \n" "} \n" "void func() \n" "{ \n" " Test t; \n" " t.s = 'hello'; \n" " assert(t.s == 'hello'); \n" "} \n"; mod->AddScriptSection("script", script12); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "func()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Compound assignments for object properties will not be allowed r = ExecuteString(engine, "Test t; t.s += 'hello';", mod); if( r >= 0 ) { TEST_FAILED; } if( bout.buffer != "ExecuteString (1, 13) : Error : Compound assignments with property accessors are not allowed\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test @t.prop = @obj; Property is a handle, and the property is assigned a new handle. Should work const char *script13 = "class Test \n" "{ \n" " string@ get_s() {return _s;} \n" " void set_s(string @n) {@_s = @n;} \n" " string@ _s; \n" "} \n" "void func() \n" "{ \n" " Test t; \n" " string s = 'hello'; \n" " @t.s = @s; \n" // handle assignment " assert(t.s is s); \n" " t.s = 'other'; \n" // value assignment " assert(s == 'other'); \n" "} \n"; mod->AddScriptSection("script", script13); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "func()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test accessing members of an object property const char *script14 = "class Test \n" "{ \n" " string get_s() {return _s;} \n" " void set_s(string n) {_s = n;} \n" " string _s; \n" "} \n" "void func() \n" "{ \n" " Test t; \n" " t.s = 'hello'; \n" // value assignment " assert(t.s == 'hello'); \n" " assert(t.s.length() == 5); \n" // this should work as length is const "} \n"; mod->AddScriptSection("script", script14); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "func()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test accessing a non-const method on an object through a get accessor // Should at least warn since the object is just a temporary one bout.buffer.c_str(); r = ExecuteString(engine, "Test t; t.s.resize(4);", mod); if( r < 0 ) TEST_FAILED; if( (sizeof(void*) == 4 && bout.buffer != "ExecuteString (1, 13) : Warning : A non-const method is called on temporary object. Changes to the object may be lost.\n" "ExecuteString (1, 13) : Info : void string::resize(uint)\n") || (sizeof(void*) == 8 && bout.buffer != "ExecuteString (1, 13) : Warning : A non-const method is called on temporary object. Changes to the object may be lost.\n" "ExecuteString (1, 13) : Info : void string::resize(uint64)\n") ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test opNeg for object through get accessor const char *script15 = "class Val { int opNeg() const { return -1; } } \n" "class Test \n" "{ \n" " Val get_s() const {return Val();} \n" "} \n" "void func() \n" "{ \n" " Test t; \n" " assert( -t.s == -1 ); \n" "} \n"; mod->AddScriptSection("script", script15); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "func()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test index operator for object through get accessor const char *script16 = "class Test \n" "{ \n" " int[] get_s() const { int[] a(1); a[0] = 42; return a; } \n" "} \n" "void func() \n" "{ \n" " Test t; \n" " assert( t.s[0] == 42 ); \n" "} \n"; mod->AddScriptSection("script", script16); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "script (5, 1) : Info : Compiling void func()\n" "script (8, 14) : Warning : A non-const method is called on temporary object. Changes to the object may be lost.\n" "script (8, 14) : Info : int& array::opIndex(uint)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "func()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test accessing normal properties for object through get accessor const char *script17 = "class Val { int val; } \n" "class Test \n" "{ \n" " Val get_s() const { Val v; v.val = 42; return v;} \n" "} \n" "void func() \n" "{ \n" " Test t; \n" " assert( t.s.val == 42 ); \n" "} \n"; mod->AddScriptSection("script", script17); bout.buffer = ""; r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "func()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test const/non-const get and set accessors const char *script18 = "class Test \n" "{ \n" " int get_p() { return 42; } \n" " int get_c() const { return 42; } \n" " void set_s(int) {} \n" "} \n" "void func() \n" "{ \n" " const Test @t = @Test(); \n" " assert( t.p == 42 ); \n" // Fail " assert( t.c == 42 ); \n" // Success " t.s = 42; \n" // Fail "} \n"; mod->AddScriptSection("script", script18); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (7, 1) : Info : Compiling void func()\n" "script (10, 15) : Error : Non-const method call on read-only object reference\n" "script (10, 15) : Info : int Test::get_p()\n" "script (12, 7) : Error : Non-const method call on read-only object reference\n" "script (12, 7) : Info : void Test::set_s(int)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test accessor with property of the same name const char *script19 = "int direction; \n" "void set_direction(int val) { direction = val; } \n" "void test_set() \n" "{ \n" " direction = 9; \n" // calls the set_direction property accessor "} \n" "void test_get() \n" "{ \n" " assert( direction == 9 ); \n" // fails, since there is no get accessor "} \n"; mod->AddScriptSection("script", script19); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (7, 1) : Info : Compiling void test_get()\n" "script (9, 21) : Error : The property has no get accessor\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } const char *script20 = "class Test { \n" " int direction; \n" " void set_direction(int val) { direction = val; } \n" "} \n"; mod->AddScriptSection("script", script20); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "Test t; t.set_direction(3);", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // TODO: Test non-const get accessor for object type with const overloaded dual operator // TODO: Test get accessor that returns a reference (only from application func to start with) // TODO: Test property accessor with inout references. Shouldn't be allowed as the value is not a valid reference // TODO: Test set accessor with parameter declared as out ref (shouldn't be found) // TODO: What should be done with expressions like t.prop; Should the get accessor be called even though // the value is never used? // TODO: Accessing a class member from within the property accessor with the same name as the property // shouldn't call the accessor again. Instead it should access the real member. FindPropertyAccessor() // shouldn't find any if the function being compiler is the property accessor itself engine->Release(); // Test property accessor on temporary object handle { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterStdString(engine); const char *script = "class Obj { void set_opacity(float v) {} }\n" "Obj @GetObject() { return @Obj(); } \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "GetObject().opacity = 1.0f;", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test bug reported by Scarabus2 // The bug was an incorrect reusage of temporary variable by the // property get accessor when compiling a binary operator { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class Object { \n" " Object() {rot = 0;} \n" " void set_rotation(float r) {rot = r;} \n" " float get_rotation() const {return rot;} \n" " float rot; } \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Object obj; \n" "float elapsed = 1.0f; \n" "float temp = obj.rotation + elapsed * 1.0f; \n" "obj.rotation = obj.rotation + elapsed * 1.0f; \n" "assert( obj.rot == 1 ); \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test global property accessor { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "int _s = 0; \n" "int get_s() { return _s; } \n" "void set_s(int v) { _s = v; } \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "s = 10; assert( s == 10 );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); // The global property accessors are available to initialize global // variables, but can possibly throw an exception if used inappropriately. // This test also verifies that circular references between global // properties and functions is properly resolved by the GC. engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); RegisterStdString(engine); bout.buffer = ""; script = "string _s = s; \n" "string get_s() { return _s; } \n"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r != asINIT_GLOBAL_VARS_FAILED ) TEST_FAILED; if( bout.buffer != "script (1, 13) : Error : Failed to initialize global variable '_s'\n" "script (2, 0) : Info : Exception 'Null pointer access' in 'string get_s()'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test property accessor for object in array { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class MyObj { bool get_Active() { return true; } } \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "MyObj[] a(1); if( a[0].Active == true ) { } if( a[0].get_Active() == true ) { }", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test property accessor from within class method { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); const char *script = "class Vector3 \n" "{ \n" " float x; \n" " float y; \n" " float z; \n" "}; \n" "class Hoge \n" "{ \n" " const Vector3 get_pos() { return mPos; } \n" " const Vector3 foo() { return pos; } \n" " const Vector3 zoo() { return get_pos(); } \n" " Vector3 mPos; \n" "}; \n" "void main() \n" "{ \n" " Hoge h; \n" " Vector3 vec; \n" " vec = h.zoo(); \n" // ok " vec = h.foo(); \n" // runtime exception "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test property accessor in type conversion { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); const char *script = "class sound \n" "{ \n" " int get_pitch() { return 1; } \n" " void set_pitch(int p) {} \n" "} \n" "void main() \n" "{ \n" " sound[] sounds(1) ; \n" " sounds[0].pitch = int(sounds[0].pitch)/2; \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test property accessor in type conversion (2) { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); const char *script = "class sound \n" "{ \n" " const int &get_id() const { return i; } \n" " int i; \n" "} \n" "void main() \n" "{ \n" " sound s; \n" " if( s.id == 1 ) \n" " return; \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test property accessors for opIndex { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, false); RegisterScriptString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class CTest \n" "{ \n" " CTest() { arr.resize(5); } \n" " int get_opIndex(int i) const { return arr[i]; } \n" " void set_opIndex(int i, int v) { arr[i] = v; } \n" " array<int> arr; \n" "} \n" "void main() \n" "{ \n" " CTest s; \n" " s[0] = 42; \n" " assert( s[0] == 42 ); \n" " s[1] = 24; \n" " assert( s[1] == 24 ); \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test global property accessors with index argument { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, false); RegisterScriptString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = " int get_arr(int i) { arr.resize(5); return arr[i]; } \n" " void set_arr(int i, int v) { arr.resize(5); arr[i] = v; } \n" " array<int> arr; \n" "void main() \n" "{ \n" " arr[0] = 42; \n" " assert( arr[0] == 42 ); \n" " arr[1] = 24; \n" " assert( arr[1] == 24 ); \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test member property accessors with index argument { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, false); RegisterScriptString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class CTest \n" "{ \n" " CTest() { arr.resize(5); } \n" " int get_arr(int i) { return arr[i]; } \n" " void set_arr(int i, int v) { arr[i] = v; } \n" " private array<int> arr; \n" " void test() \n" " { \n" " arr[0] = 42; \n" " assert( arr[0] == 42 ); \n" " arr[1] = 24; \n" " assert( arr[1] == 24 ); \n" " } \n" "} \n" "void main() \n" "{ \n" " CTest s; \n" " s.arr[0] = 42; \n" " assert( s.arr[0] == 42 ); \n" " s.arr[1] = 24; \n" " assert( s.arr[1] == 24 ); \n" " s.test(); \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test member property accessors with ++ where the set accessor takes a reference { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; const char *script = "class CTest \n" "{ \n" " double _vol; \n" " double get_vol() const { return _vol; } \n" " void set_vol(double &in v) { _vol = v; } \n" "} \n" "CTest t; \n" "void main() \n" "{ \n" " for( t.vol = 0; t.vol < 10; t.vol++ ); \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (8, 1) : Info : Compiling void main()\n" "script (10, 36) : Error : Invalid reference. Property accessors cannot be used in combined read/write operations\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } fail = Test2() || fail; // Success return fail; }
bool Test() { bool fail = false; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->RegisterGlobalFunction("double atof(const string &in)",asFUNCTION(StringToDouble),asCALL_GENERIC); COutStream out; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayHandle()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); PRINTF("%s: Failed to execute script\n", TESTNAME); TEST_FAILED; } if( ctx ) ctx->Release(); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script2, strlen(script2), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayHandle2()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); PRINTF("%s: Failed to execute script\n", TESTNAME); TEST_FAILED; } if( ctx ) ctx->Release(); engine->Release(); // { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, false); const char *script = "class Node \n" "{ \n" " Node@[]@ GetSubnodes() { return subNodes; } \n" " Node@[] subNodes; \n" " int member; \n" "} \n" "void TestFunc(Node@ input) \n" "{ \n" " Node@[]@ nodearray; \n" " Node@ subnode; \n" " // Case 1. Works as expected \n" " @nodearray = @input.GetSubnodes(); \n" " @subnode = @nodearray[0]; \n" " int value1 = subnode.member; // <- ok \n" " assert( value1 == 42 ); \n" " // Case 2. Wrong address sent to following operations on 'subnode' \n" " @subnode = @input.GetSubnodes()[0]; \n" " int value2 = subnode.member; // <- weird behavior \n" " assert( value2 == 42 ); \n" "} \n"; mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) { TEST_FAILED; } else { asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "Node n; \n" "n.subNodes.resize(1); \n" "@n.subNodes[0] = @Node(); \n" "n.subNodes[0].member = 42; \n" "TestFunc(n); \n", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); } ctx->Release(); } engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; fail = Test2() || fail; int r; COutStream out; CBufferedOutStream bout; asIScriptContext *ctx; asIScriptEngine *engine; // Compile array with default value in list { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); // ref type RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, "array<string> a = {'a', , 'c', , 'e'}; assert( a[1] == '' );"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Compile array with default value in list { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterStdString(engine); // value type RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, "array<string> a = {'a', , 'c', , 'e'}; assert( a[1] == '' );"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Compile nested array init list { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, false); r = ExecuteString(engine, "array<array<int>> a = {{1,2},{3,4}};"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); // Test sorting on array of handles mod->AddScriptSection(TESTNAME, "class Test { \n" " Test(int v) {val = v;} \n" " int opCmp(const Test & o) const { return val - o.val; } \n" " int val; \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "array<Test @> a = { Test(1), Test(4), Test(2), null, Test(3) }; \n" "a.sortAsc(); \n" "Assert( a[0] is null ); \n" "Assert( a[1].val == 1 ); \n" "Assert( a[2].val == 2 ); \n" "Assert( a[3].val == 3 ); \n" "Assert( a[4].val == 4 ); \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); ctx->Release(); // Test different signatures on opCmp and opEquals mod->AddScriptSection(TESTNAME, "class C \n" "{ \n" " C(int i) {i_ = i;} \n" // " bool opEquals (const C &in other) const\n" // " { \n" // " return i_ == other.i_; \n" // " } \n" // " int opCmp (const C &in other) const\n" // " { \n" // " return i_ - other.i_; \n" // " } \n" " bool opEquals (const C @ other) const\n" " { \n" " return i_ == other.i_; \n" " } \n" " int opCmp (const C @ other) const\n" " { \n" " return i_ - other.i_; \n" " } \n" " int i_; \n" "} \n" "void main (void) \n" "{ \n" " array<const C @> a2; \n" " a2.insertLast(@C(2)); \n" " a2.insertLast(@C(1)); \n" " a2.sortAsc(); \n" " Assert( a2[0].i_ == 1 ); \n" " Assert( a2[1].i_ == 2 ); \n" " C f(2); \n" " Assert( a2.find(f) == 1 ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "main()", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); ctx->Release(); // Multiple tests in one mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArray()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); printf("%s: Failed to execute script\n", TESTNAME); TEST_FAILED; } if( ctx ) ctx->Release(); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script2, strlen(script2), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } r = ExecuteString(engine, "TestArrayException()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: No exception\n", TESTNAME); TEST_FAILED; } // Must be possible to declare array of arrays mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script3, strlen(script3), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayMulti()", mod, ctx); if( r != asEXECUTION_FINISHED ) { printf("%s: Failure\n", TESTNAME); TEST_FAILED; } if( r == asEXECUTION_EXCEPTION ) { PrintException(ctx); } if( ctx ) ctx->Release(); ctx = 0; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script4, strlen(script4), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayChar()", mod, ctx); if( r != asEXECUTION_FINISHED ) { printf("%s: Failure\n", TESTNAME); TEST_FAILED; } if( r == asEXECUTION_EXCEPTION ) { PrintException(ctx); } if( ctx ) ctx->Release(); // Initialization lists must work for array template mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script5, strlen(script5), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayInitList()", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); if( ctx ) ctx->Release(); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script6, strlen(script6), 0); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "Test_Addon_ScriptArray (1, 1) : Info : Compiling void Test()\n" "Test_Addon_ScriptArray (3, 20) : Error : Initialization lists cannot be used with 'array<int>@'\n" "Test_Addon_ScriptArray (4, 21) : Error : Initialization lists cannot be used with 'int'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Array object must call default constructor of the script classes engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script7, strlen(script7), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Test bool[] on Mac OS X with PPC CPU // Submitted by Edward Rudd const char *script8 = "array<bool> f(10); \n" "for (int i=0; i<10; i++) { \n" " f[i] = false; \n" "} \n" "Assert(f[0] == false); \n" "Assert(f[1] == false); \n" "f[0] = true; \n" "Assert(f[0] == true); \n" "Assert(f[1] == false); \n"; r = ExecuteString(engine, script8, mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Test reserve() { const char *script = "array<int> f; \n" "f.reserve(10); \n" "for( uint n = 0; n < 10; n++ ) \n" " f.insertAt(n, n); \n" "Assert( f.length() == 10 ); \n"; r = ExecuteString(engine, script, mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; } // Make sure it is possible to do multiple assignments with the array type r = ExecuteString(engine, "array<int> a, b, c; a = b = c;"); if( r < 0 ) TEST_FAILED; // Must support syntax as: array<array<int>>, i.e. without white space between the closing angled brackets. r = ExecuteString(engine, "array<array<int>> a(2); Assert( a.length() == 2 );"); if( r < 0 ) TEST_FAILED; // Must support arrays of handles r = ExecuteString(engine, "array<array<int>@> a(1); @a[0] = @array<int>(4);"); if( r < 0 ) TEST_FAILED; // Do not allow the instantiation of a template with a subtype that cannot be created bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); engine->RegisterObjectType("single", 0, asOBJ_REF | asOBJ_NOHANDLE); r = ExecuteString(engine, "array<single> a;"); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 7) : Error : Can't instanciate template 'array' with subtype 'single'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test too large arrays { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, false); ctx = engine->CreateContext(); r = ExecuteString(engine, "array<int> a; a.resize(0xFFFFFFFF);", 0, ctx); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; else if( strcmp(ctx->GetExceptionString(), "Too large array size") != 0 ) TEST_FAILED; r = ExecuteString(engine, "array<int> a(0xFFFFFFFF);", 0, ctx); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; else if( strcmp(ctx->GetExceptionString(), "Too large array size") != 0 ) TEST_FAILED; r = ExecuteString(engine, "array<int> list;\n" "list.resize(3);\n" "list.reserve(-1);\n", 0, ctx); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; else if( strcmp(ctx->GetExceptionString(), "Too large array size") != 0 ) TEST_FAILED; ctx->Release(); engine->Release(); } // Test garbage collect with script class that holds array member { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, false); asIScriptModule *mod = engine->GetModule("module", asGM_ALWAYS_CREATE); const char *script = "class MyTest \n" "{ \n" " array<int> myList; \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; asIScriptObject *obj = (asIScriptObject*)engine->CreateScriptObject(mod->GetObjectTypeByName("MyTest")); obj->Release(); engine->Release(); } // Test the default value constructor { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule("module", asGM_ALWAYS_CREATE); engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, false); const char *script = "void main() \n" "{ \n" " array<int> arr(2, 42); \n" " assert(arr[0] == 42); \n" " assert(arr[1] == 42); \n" " array<array<int>> arr2(2, array<int>(2)); \n" " assert(arr2[0].length() == 2); \n" " assert(arr2[1].length() == 2); \n" " array<array<int>@> arr3(2, arr); \n" " assert(arr3[0] is arr); \n" " assert(arr3[1] is arr); \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test potential memory leak situation with circular reference between types { // Create the script engine asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); // Register array class RegisterScriptArray(engine, false); // Compile asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "class Hoge" "{" " HogeManager@ hogeManager;" "};" "class HogeManager" "{" " array< Hoge >@ hoges;" "};" , 0); mod->Build(); // Release engine engine->Release(); } // Test creating script array from application { if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { printf("Subtest: Skipped due to AS_MAX_PORTABILITY\n"); } else { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, false); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("array<string@>@ CreateArrayOfStrings()", asFUNCTION(CreateArrayOfStrings), asCALL_CDECL); assert( r >= 0 ); r = ExecuteString(engine, "array<string@>@ arr = CreateArrayOfStrings()"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Release engine engine->Release(); } } // Test insertAt, removeAt { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, false); RegisterScriptString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, "array<string> arr = {'1','2','3'}; \n" "arr.insertAt(1, 'test'); \n" "assert( arr[1] == 'test' );\n" "arr.insertAt(4, '4'); \n" "assert( arr[4] == '4' );\n" "arr.removeAt(0); \n" "assert( arr[0] == 'test' );\n" "assert( arr[3] == '4' );\n"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // This test was failing on XBOX 360 { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); const char *script = "class ArrayOf \n" "{ \n" " uint8[] _boolList; \n" " int _numOfStockedObject; \n" " ArrayOf(int arraySizeMax) \n" " { \n" " _boolList.resize(arraySizeMax); \n" " _numOfStockedObject = 0; \n" " for(int i = 0; i < arraySizeMax; ++i) \n" " { \n" " _boolList[i] = 0; \n" " } \n" " } \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "ArrayOf(100)", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Array should call subtypes' opAssign when it exists { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "int calls = 0; \n" "class Value \n" "{ \n" " int val; \n" " Value(int v) {val = v;} \n" " Value() {} \n" " Value &opAssign(const Value &in o) { calls++; return this; } \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "array<Value> arr = {Value(2), Value(3), Value(0)}; \n" "assert( calls == 0 ); \n" "array<Value> arr2; \n" "arr2 = arr; \n" "assert( calls == 3 ); \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // test sorting { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class Value \n" "{ \n" " int val; \n" " Value(int v) {val = v;} \n" " Value() {} \n" " int opCmp(const Value &in o) {return val - o.val;} \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Value[] arr = {Value(2), Value(3), Value(0)}; \n" "arr.sortAsc(); \n" "assert(arr[0].val == 0); \n" "assert(arr[1].val == 2); \n" "assert(arr[2].val == 3);", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "bool TestSort() \n" "{ \n" " array<int> A = {1, 5, 2, 4, 3}; \n" " array<int> B = {1, 5, 2, 4, 3}; \n" " A.sortAsc(); \n" " B.sortDesc(); \n" " return \n" " A[0] == 1 && A[1] == 2 && A[2] == 3 && A[3] == 4 && A[4] == 5 && \n" " B[0] == 5 && B[1] == 4 && B[2] == 3 && B[3] == 2 && B[4] == 1; \n" "} \n" "bool TestReverse() \n" "{ \n" " array<int> A = {5, 4, 3, 2, 1}; \n" " A.reverse(); \n" " return A[0] == 1 && A[1] == 2 && A[2] == 3 && A[3] == 4 && A[4] == 5; \n" "} \n" "class cOpCmp \n" "{ \n" " cOpCmp() \n" " { \n" " a = 0; \n" " b = 0.0; \n" " } \n" " cOpCmp(int _a, float _b) \n" " { \n" " a = _a; \n" " b = _b; \n" " } \n" " void set(int _a, float _b) \n" " { \n" " a = _a; \n" " b = _b; \n" " } \n" " int opCmp(cOpCmp &in other) \n" " { \n" " return a - other.a; \n" " } \n" " int a; \n" " float b; \n" "} \n" "class cOpEquals \n" "{ \n" " cOpEquals() \n" " { \n" " a = 0; \n" " b = 0.0; \n" " } \n" " cOpEquals(int _a, float _b) \n" " { \n" " a = _a; \n" " b = _b; \n" " } \n" " void set(int _a, float _b) \n" " { \n" " a = _a; \n" " b = _b; \n" " } \n" " bool opEquals(cOpEquals &in other) \n" " { \n" " return a == other.a; \n" " } \n" " int a; \n" " float b; \n" "} \n" "bool TestFind() \n" "{ \n" " array<int> A = {5, 8, 3, 2, 0, 0, 2, 1}; \n" " if (A.find(10) != -1) \n" " return false; \n" " if (A.find(0) != 4) \n" " return false; \n" " if (A.find(1, 8) != 1) \n" " return false; \n" " if (A.find(2, 8) != -1) \n" " return false; \n" " array<cOpCmp> CMP(5); \n" " CMP[0].set(0, 0.0); \n" " CMP[1].set(1, 0.0); \n" " CMP[2].set(2, 0.0); \n" " CMP[3].set(3, 0.0); \n" " CMP[4].set(4, 0.0); \n" " if (CMP.find(cOpCmp(5, 0.0)) != -1) \n" " return false; \n" " if (CMP.find(2, cOpCmp(2, 1.0)) != 2) \n" " return false; \n" " if (CMP.find(3, cOpCmp(2, 1.0)) != -1) \n" " return false; \n" " array<cOpEquals> EQ(5); \n" " EQ[0].set(0, 0.0); \n" " EQ[1].set(1, 0.0); \n" " EQ[2].set(2, 0.0); \n" " EQ[3].set(3, 0.0); \n" " EQ[4].set(4, 0.0); \n" " if (EQ.find(cOpEquals(5, 0.0)) != -1) \n" " return false; \n" " if (EQ.find(2, cOpEquals(2, 1.0)) != 2) \n" " return false; \n" " if (EQ.find(3, cOpEquals(2, 1.0)) != -1) \n" " return false; \n" " return true; \n" "} \n" "int main() \n" "{ \n" " assert( TestSort() ); \n" " assert( TestReverse() ); \n" " assert( TestFind() ); \n" " return 789; \n" "} \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "assert( main() == 789 ); \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test array, with objects that don't have default constructor/factory { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class CTest \n" "{ \n" " CTest(int v) {} \n" // With an explicit non-default constructor the compiler won't create the default constructor "} \n" "array<CTest> arr; \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r > 0 ) TEST_FAILED; if( bout.buffer != "script (5, 7) : Error : Can't instanciate template 'array' with subtype 'CTest'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); RegisterScriptArray(engine, true); const char *script = "class T { }; \n" "array<T> arr; \n"; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "array<T> arr(1); \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test problem with arrays of enums reported by Philip Bennefall { const char *script = "enum fruit \n" "{ \n" " APPLE, ORANGE, BANANA \n" "} \n" "void main() \n" "{ \n" " fruit[] basket; \n" " basket.insertLast(APPLE); \n" " basket.insertLast(ORANGE); \n" " basket.sortDesc(); \n" " int index = basket.find(APPLE); \n" " assert( index == 1 ); \n" "} \n"; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test problem with arrays and opEquals reported by Philip Bennefall { const char *script = "class fish \n" "{ \n" " bool opEquals(fish@ other) \n" // handles should be supported too " { \n" " return false; \n" " } \n" "} \n" "void main() \n" "{ \n" " fish[] ocean(100); \n" " fish nemo; \n" " int index = ocean.find(nemo); \n" "} \n"; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); RegisterScriptArray(engine, true); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test problem with arrays and opEquals reported by Philip Bennefall { const char *script = "class fish \n" "{ \n" " bool opEquals(fish@ other) \n" " { \n" " return false; \n" " } \n" "} \n" "void main() \n" "{ \n" " fish@[] ocean(100); \n" " for(uint i=0; i<ocean.length(); i++) \n" " { \n" " fish fred; \n" " @(ocean[i]) = fred; \n" " } \n" " fish nemo; \n" " int index = ocean.find(nemo); \n" "} \n"; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); RegisterScriptArray(engine, true); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test problem with arrays and opAssign reported by Philip Bennefall { const char *script = "array<uint> a, b = {0,1,2,3}; \n" "a.reserve(10); \n" "a = b; \n" "assert( a.length() == b.length() ); \n" "assert( a.length() == 4 ); \n" "for( uint n = 0; n < a.length(); n++ ) \n" " assert( a[n] == n ); \n"; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, script); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test findByRef { const char *script = "class Obj {} \n" "array<int> ia = {1,2,3}; \n" "array<Obj> oa = {Obj(), Obj()}; \n" "array<Obj@> ha = {Obj(), Obj()}; \n"; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "assert( ia.findByRef(ia[1]) == -1 ); \n" "Obj @obj = oa[1]; assert( oa.findByRef(obj) == 1 ); \n" "@obj = ha[1]; assert( ha.findByRef(obj) == 1 ); \n" "ha.insertLast(null); assert( ha.findByRef(null) == 2 ); \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Success return fail; }
bool Test() { if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { // Skipping this due to not supporting native calling conventions printf("Skipped due to AS_MAX_PORTABILITY\n"); return false; } bool fail = false; int r; int suspendId, exceptionId; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterObjectType("Object", sizeof(CObject), asOBJ_VALUE | asOBJ_APP_CLASS_CD); r = engine->RegisterObjectType("RefObj", sizeof(CRefObject), asOBJ_REF); assert(r >= 0); if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { engine->RegisterObjectBehaviour("Object", asBEHAVE_CONSTRUCT, "void f(int)", asFUNCTION(Construct2), asCALL_GENERIC); engine->RegisterObjectBehaviour("Object", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct_gen), asCALL_GENERIC); engine->RegisterObjectBehaviour("Object", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Destruct_gen), asCALL_GENERIC); engine->RegisterObjectMethod("Object", "Object &opAssign(const Object &in)", asFUNCTION(Assign_gen), asCALL_GENERIC); r = engine->RegisterObjectBehaviour("RefObj", asBEHAVE_FACTORY, "RefObj@ f()", asFUNCTION(RefObjFactory_gen), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("RefObj", asBEHAVE_ADDREF, "void f()", asFUNCTION(AddRef_gen), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectBehaviour("RefObj", asBEHAVE_RELEASE, "void f()", asFUNCTION(Release_gen), asCALL_GENERIC); assert(r >= 0); engine->RegisterGlobalFunction("void RaiseException()", asFUNCTION(RaiseException), asCALL_GENERIC); suspendId = engine->RegisterGlobalFunction("Object SuspendObj()", asFUNCTION(SuspendObj_gen), asCALL_GENERIC); exceptionId = engine->RegisterGlobalFunction("Object ExceptionObj()", asFUNCTION(ExceptionObj_gen), asCALL_GENERIC); engine->RegisterGlobalFunction("RefObj@ ExceptionHandle()", asFUNCTION(ExceptionHandle_gen), asCALL_GENERIC); } else { engine->RegisterObjectBehaviour("Object", asBEHAVE_CONSTRUCT, "void f(int)", asFUNCTION(Construct2), asCALL_GENERIC); engine->RegisterObjectBehaviour("Object", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct), asCALL_CDECL_OBJLAST); engine->RegisterObjectBehaviour("Object", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Destruct), asCALL_CDECL_OBJLAST); engine->RegisterObjectMethod("Object", "Object &opAssign(const Object &in)", asFUNCTION(Assign_gen), asCALL_GENERIC); r = engine->RegisterObjectBehaviour("RefObj", asBEHAVE_FACTORY, "RefObj@ f()", asFUNCTION(RefObjFactory), asCALL_CDECL); assert(r >= 0); r = engine->RegisterObjectBehaviour("RefObj", asBEHAVE_ADDREF, "void f()", asMETHOD(CRefObject, AddRef), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("RefObj", asBEHAVE_RELEASE, "void f()", asMETHOD(CRefObject, Release), asCALL_THISCALL); assert(r >= 0); engine->RegisterGlobalFunction("void RaiseException()", asFUNCTION(RaiseException), asCALL_CDECL); suspendId = engine->RegisterGlobalFunction("Object SuspendObj()", asFUNCTION(SuspendObj), asCALL_CDECL); exceptionId = engine->RegisterGlobalFunction("Object ExceptionObj()", asFUNCTION(ExceptionObj), asCALL_CDECL); engine->RegisterGlobalFunction("RefObj@ ExceptionHandle()", asFUNCTION(ExceptionHandle), asCALL_CDECL); } COutStream out; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); mod->AddScriptSection(TESTNAME, script2, strlen(script2), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } // The object has been initialized r = ExecuteString(engine, "Test1()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } // The object has not yet been initialized r = ExecuteString(engine, "Test2()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } // An object has been initialized and passed by value to function that throws exception r = ExecuteString(engine, "Test3()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } // An object has been initialized and passed by value to a function, but // the function cannot be called due to the stack being full engine->SetEngineProperty(asEP_MAX_STACK_SIZE, sizeof(void*)); r = ExecuteString(engine, "Test3()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } // An object is allocated and initialized with a call to // a function that returns an object by value. The function // suspends the thread. The context is then aborted. asIScriptContext *ctx = engine->CreateContext(); engine->SetEngineProperty(asEP_MAX_STACK_SIZE, 0); r = ExecuteString(engine, "Test4()", mod, ctx); if( r != asEXECUTION_SUSPENDED ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } ctx->Abort(); ctx->Release(); // An object is allocated and initialized with a call to // a function that returns an object by value. The function // sets a script exception. r = ExecuteString(engine, "Test5()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } // The object constructor sets the exception r = ExecuteString(engine, "Test6()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } // A function that is supposed to return a handle sets an exception r = ExecuteString(engine, "Test7()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } // Attempt to call method on null class pointer mod->AddScriptSection("script", script3, strlen(script3)); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "calc()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: Failed\n", TESTNAME); TEST_FAILED; } // Exception happens after value object has already been destroyed r = ExecuteString(engine, "{\n" " Object o;\n" "}\n" "RaiseException();"); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; // Exception happens after the value object has been destroyed and, // the same position would also be used again after the exception r = ExecuteString(engine, "{ Object o; } \n" "RaiseException(); \n" "Object o; \n"); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; // The code has two places where the object is destroyed, one in the if case, and // and one at the end of the function. If the code doesn't go in to the if case, // and the exception happens afterwards, the exception handler must not think the // object was already destroyed. r = ExecuteString(engine, "Object o; bool a = false; \n" "if( a ) return; \n" "RaiseException(); \n"); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; // Calling a function that returns an object directly must release the object upon releasing the context ctx = engine->CreateContext(); ctx->Prepare(engine->GetFunctionById(suspendId)); ctx->Execute(); ctx->Release(); // Calling a function that returns an object but raised an exception shouldn't try to destroy the object ctx = engine->CreateContext(); ctx->Prepare(engine->GetFunctionById(exceptionId)); ctx->Execute(); ctx->Release(); engine->Release(); // Test proper release of function pointers on exception { COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void RaiseException()", asFUNCTION(RaiseException), asCALL_CDECL); asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "funcdef void func_t(); \n" "void main() \n" "{ \n" " func_t @f = main; \n" " RaiseException(); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; engine->Release(); } // Test problem reported by FDsagizi // http://www.gamedev.net/topic/631801-pod-type-and-null-pointer-exception-bug-with-call-destructor/ { COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterObjectType("TestLink", sizeof(CObject), asOBJ_VALUE | asOBJ_APP_CLASS_CD); engine->RegisterObjectBehaviour("TestLink", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct), asCALL_CDECL_OBJLAST); engine->RegisterObjectBehaviour("TestLink", asBEHAVE_CONSTRUCT, "void f(const TestLink &in)", asFUNCTION(CopyConstruct), asCALL_CDECL_OBJLAST); engine->RegisterObjectBehaviour("TestLink", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Destruct), asCALL_CDECL_OBJLAST); asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "class Object3 \n" "{ \n" " Object3( TestLink str ) \n" " { \n" " Object3 @null_object = null; \n" " null_object.Do(); \n" " } \n" " void Do() {} \n" "} \n" "void Main() \n" "{ \n" " Object3 @oo = Object3( TestLink() ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; CObject_constructCount = 0; CObject_destructCount = 0; asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "Main()", mod, ctx); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; if( CObject_constructCount != 2 || CObject_destructCount != 1 ) TEST_FAILED; ctx->Release(); if( CObject_constructCount != 2 || CObject_destructCount != 2 ) TEST_FAILED; CBytecodeStream stream(__FILE__"1"); r = mod->SaveByteCode(&stream); if( r < 0 ) TEST_FAILED; r = mod->LoadByteCode(&stream); if( r < 0 ) TEST_FAILED; CObject_constructCount = 0; CObject_destructCount = 0; ctx = engine->CreateContext(); r = ExecuteString(engine, "Main()", mod, ctx); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; if( CObject_constructCount != 2 || CObject_destructCount != 1 ) TEST_FAILED; ctx->Release(); engine->Release(); } // Test failure when allocating very large array { COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "class test\n" "{\n" " double x;\n" " double y;\n" " double z;\n" " double other;\n" "}\n" "void main()\n" "{\n" " test[] list(1000000000);\n" "}\n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; CBufferedOutStream bout; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "Test()", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; printf("%s: Execution failed: %d\n", TESTNAME, r); } if( ctx ) ctx->Release(); engine->Release(); // Test value class with unsafe ref { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class Good \n" "{ \n" " vector3 _val; \n" " Good(const vector3& in val) \n" " { \n" " _val = val; \n" " } \n" "}; \n" "class Bad \n" "{ \n" " vector3 _val; \n" " Bad(const vector3& val) \n" " { \n" " _val = val; \n" " } \n" "}; \n" "void test() \n" "{ \n" " // runs fine \n" " for (int i = 0; i < 2; i++) \n" " Good(vector3(1, 2, 3)); \n" " // causes vm stack corruption \n" " for (int i = 0; i < 2; i++) \n" " Bad(vector3(1, 2, 3)); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "test()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test ref to primitives { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "void func(){ \n" " float a; \n" " uint8 b; \n" " int c; \n" " funcA(c, a, b); \n" "} \n" "void funcA(float& a, uint8& b, int& c) {} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestUnsafeRef (1, 1) : Info : Compiling void func()\n" "TestUnsafeRef (5, 3) : Error : No matching signatures to 'funcA(int, float, uint8)'\n" "TestUnsafeRef (5, 3) : Info : Candidates are:\n" "TestUnsafeRef (5, 3) : Info : void funcA(float&inout, uint8&inout, int&inout)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test problem found by TheAtom // Passing an inout reference to a handle to a function wasn't working properly { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class T { int a; } \n" "void f(T@& p) { \n" " T t; \n" " t.a = 42; \n" " @p = t; \n" // or p=t; in which case t is copied "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "T @t; f(t); assert( t.a == 42 );\n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // http://www.gamedev.net/topic/624722-bug-with/ { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class T { T() { val = 123; } int val; } \n" "T g_t; \n" "T &GetTest() { return g_t; } \n" "void f(T@& t) { \n" " assert( t.val == 123 ); \n" "} \n" "void func() { \n" " f(GetTest()); \n" " f(@GetTest()); \n" " T @t = GetTest(); \n" " f(t); \n" "} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestUnsafeRef (7, 1) : Info : Compiling void func()\n" "TestUnsafeRef (8, 3) : Error : No matching signatures to 'f(T)'\n" "TestUnsafeRef (8, 3) : Info : Candidates are:\n" "TestUnsafeRef (8, 3) : Info : void f(T@&inout)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // http://www.gamedev.net/topic/624722-bug-with/ { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class T { T() { val = 123; } int val; } \n" "T g_t; \n" "T &GetTest() { return g_t; } \n" "void f(T@& t) { \n" " assert( t.val == 123 ); \n" "} \n" "void func() { \n" " f(cast<T>(GetTest())); \n" " f(@GetTest()); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "func()", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx, true); } ctx->Release(); engine->Release(); } // http://www.gamedev.net/topic/636443-there-is-no-copy-operator-for-the-type-val-available/ { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->RegisterObjectType("Val", sizeof(int), asOBJ_VALUE | asOBJ_APP_PRIMITIVE); engine->RegisterObjectBehaviour("Val", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(0), asCALL_GENERIC); // With unsafe references the copy constructor doesn't have to be in, it can be inout too engine->RegisterObjectBehaviour("Val", asBEHAVE_CONSTRUCT, "void f(const Val &)", asFUNCTION(0), asCALL_GENERIC); engine->RegisterObjectBehaviour("Val", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(0), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "Val GetVal() \n" "{ \n" " Val ret; \n" " return ret; \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test with copy constructor that takes unsafe reference // http://www.gamedev.net/topic/638613-asassert-in-file-as-compillercpp-line-675/ { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterObjectType("string", sizeof(Str), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 ); r = engine->RegisterStringFactory("string", asFUNCTION(Str::StringFactory), asCALL_CDECL); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Str::StringConstruct), asCALL_CDECL_OBJLAST); assert( r >= 0 ); // Copy constructor takes an unsafe reference r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f(const string &)", asFUNCTION(Str::StringCopyConstruct), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Str::StringDestruct), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in)", asMETHOD(Str, opEquals), asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "void SetTexture( string txt ) { assert( txt == 'test' ); } \n" "void startGame( ) \n" "{ \n" " SetTexture('test'); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "startGame();", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test with assignment operator that takes unsafe reference { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterObjectType("string", sizeof(Str), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 ); r = engine->RegisterStringFactory("string", asFUNCTION(Str::StringFactory), asCALL_CDECL); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Str::StringConstruct), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Str::StringDestruct), asCALL_CDECL_OBJLAST); assert( r >= 0 ); // Assignment operator takes an unsafe reference r = engine->RegisterObjectMethod("string", "string &opAssign(const string &)", asMETHOD(Str, opAssign), asCALL_THISCALL); assert( r >= 0 ); r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in)", asMETHOD(Str, opEquals), asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "void SetTexture( string txt ) { assert( txt == 'test' ); } \n" "void startGame( ) \n" "{ \n" " SetTexture('test'); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "startGame();", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Success return fail; }
bool Test() { bool fail = Test2(); int r; asIScriptModule *mod = 0; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); COutStream out; CBufferedOutStream bout; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); // Test declaring multiple properties in same declaration separated by , { mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class A { \n" " int a, b, c; \n" " void f() { a = b = c; } \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; } mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1); r = mod->Build(); if( r < 0 ) TEST_FAILED; // Verify that GetObjectTypeByIndex recognizes the script class if( mod->GetObjectTypeCount() != 1 ) TEST_FAILED; asIObjectType *type = mod->GetObjectTypeByIndex(0); if( strcmp(type->GetName(), "Test") != 0 ) TEST_FAILED; asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "TestStruct()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; } if( ctx ) ctx->Release(); bout.buffer = ""; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script2); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = mod->Build(); if( r >= 0 || bout.buffer != "TestScriptStruct (3, 4) : Error : Class properties cannot be declared as const\n" ) TEST_FAILED; mod->AddScriptSection(TESTNAME, script3); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "TestArrayInStruct()", mod); if( r != 0 ) TEST_FAILED; mod->AddScriptSection(TESTNAME, script4, strlen(script4), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test()", mod); if( r != 0 ) TEST_FAILED; bout.buffer = ""; mod->AddScriptSection(TESTNAME, script5, strlen(script5), 0); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = mod->Build(); if( r >= 0 || bout.buffer != "TestScriptStruct (2, 7) : Error : Name conflict. 'A' is a class.\n" "TestScriptStruct (6, 9) : Error : Name conflict. 'a' is an object property.\n" ) TEST_FAILED; bout.buffer = ""; mod->AddScriptSection(TESTNAME, script6, strlen(script6), 0); r = mod->Build(); if( r >= 0 || bout.buffer != "TestScriptStruct (1, 7) : Error : Illegal member type\n" "TestScriptStruct (5, 7) : Error : Illegal member type\n" ) TEST_FAILED; mod->AddScriptSection(TESTNAME, script7, strlen(script7), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "TestHandleInStruct()", mod, ctx); if( r != 0 ) { if( r == asEXECUTION_EXCEPTION ) { printf("%s\n", ctx->GetExceptionString()); } TEST_FAILED; } if( ctx ) ctx->Release(); mod->AddScriptSection(TESTNAME, script8, strlen(script8), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "TestHandleInStruct2()", mod); if( r != 0 ) TEST_FAILED; mod->AddScriptSection(TESTNAME, script9, strlen(script9), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test()", mod); if( r != 0 ) TEST_FAILED; bout.buffer = ""; mod->AddScriptSection(TESTNAME, script10, strlen(script10), 0); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestScriptStruct (1, 7) : Error : Illegal member type\n" ) TEST_FAILED; bout.buffer = ""; mod->AddScriptSection(TESTNAME, script11, strlen(script11), 0); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestScriptStruct (5, 1) : Info : Compiling void Test()\nTestScriptStruct (9, 11) : Error : Reference is read-only\n" ) TEST_FAILED; mod->AddScriptSection(TESTNAME, script12, strlen(script12), 0); mod->AddScriptSection(TESTNAME, script13, strlen(script13), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) TEST_FAILED; // The garbage collection doesn't have to be invoked immediately. Modules // can even be discarded before calling the garbage collector. engine->GarbageCollect(); // Make sure it is possible to copy a script class that contains an object handle mod->AddScriptSection(TESTNAME, script14, strlen(script14), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "A a; B b; @a.b = @b; b.val = 1; A a2; a2 = a; Assert(a2.b.val == 1);", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Make sure it is possible to copy a script class that contains an array const char *script15 = "class Some \n" "{ \n" " int[] i; // need be array \n" "} \n" "void main() \n" "{ \n" " Some e; \n" " e=some(e); // crash \n" "} \n" "Some@ some(Some@ e) \n" "{ \n" " return e; \n" "} \n"; mod->AddScriptSection(TESTNAME, script15); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); // A script class must be able to have a registered ref type as a local member { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); RegisterScriptString(engine); const char *script = "class C { string s; }"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("s", script); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } if( !fail ) { r = ExecuteString(engine, "C c; c.s = 'test';", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } } engine->Release(); } // Test private properties { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script = "class C { \n" "private int a; \n" "void func() { a = 1; } }\n" "void main() { C c; c.a = 2; }"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("s", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "s (4, 1) : Info : Compiling void main()\n" "s (4, 21) : Error : Illegal access to private property 'a'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test private methods { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); const char *script = "class C { \n" " private void func() {} \n" " void func2() { func(); } } \n" "void main() { C c; c.func(); } \n"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("s", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "s (4, 1) : Info : Compiling void main()\n" "s (4, 20) : Error : Illegal call to private method 'void C::func()'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test private methods that return a value { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "class C { \n" " int a(int i) { return ABS(i); } \n" " private int ABS(int i) \n" " { \n" " if(i <= 0) return (-1 * i); \n" " else return i; \n" " } \n" "} \n"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("s", script); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "C c; assert( c.a(-10) == 10 );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test private methods with inheritance { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); const char *script = "bool alreadyCalled = false; \n" "class CBar \n" "{ \n" " CBar() \n" " { \n" " assert(alreadyCalled == false); \n" " alreadyCalled = true; \n" " } \n" " void Foo() \n" " { \n" " } \n" "}; \n" "class CDerivedBar : CBar \n" "{ \n" " CDerivedBar() \n" " { \n" " } \n" " private void ImNotAnOverrideOfTheBaseClass() \n" " { \n" " } \n" " private void Foo() \n" " { \n" " } \n" "}; \n"; asIScriptModule *mod = engine->GetModule("t", asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); mod->Build(); asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "CDerivedBar bar; bar.Foo(); ", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx, true); TEST_FAILED; } ctx->Release(); engine->Release(); } // Default constructor and opAssign shouldn't be provided if a non-default constructor is implemented { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); const char *script = "class CBar \n" "{ \n" " CBar(int a) {}\n" "}; \n" "void func() \n" "{ \n" " CBar a; \n" // not ok " CBar b(1); \n" // ok " CBar c = CBar(1); \n" // not ok " b = b; \n" // not ok " CBar d(CBar()); \n" // not ok "}; \n"; asIScriptModule *mod = engine->GetModule("t", asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (5, 1) : Info : Compiling void func()\n" "script (7, 8) : Error : No default constructor for object of type 'CBar'.\n" "script (9, 8) : Error : No default constructor for object of type 'CBar'.\n" "script (9, 8) : Error : There is no copy operator for the type 'CBar' available.\n" "script (10, 5) : Error : There is no copy operator for the type 'CBar' available.\n" "script (11, 10) : Error : No matching signatures to 'CBar()'\n" "script (11, 10) : Info : Candidates are:\n" "script (11, 10) : Info : CBar@ CBar(int)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Success return fail; }
bool Test() { bool fail = Test2(); int r; COutStream out; asIScriptContext *ctx; // Test to make sure opIndex properly catches null pointer access // http://www.gamedev.net/topic/676729-crash-instead-of-null-pointer-exception-on-opindex/ { asIScriptEngine *engine = asCreateScriptEngine(); RegisterScriptArray(engine, true); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); ctx = engine->CreateContext(); r = ExecuteString(engine, "array<array<int>@> a = {null}; a[0][0] = 1;", 0, ctx); if (r != asEXECUTION_EXCEPTION) TEST_FAILED; else if( GetExceptionInfo(ctx) != "func: void ExecuteString()\n" "modl: \n" "sect: ExecuteString\n" "line: 1\n" "desc: Null pointer access\n" ) { PRINTF("%s", GetExceptionInfo(ctx).c_str()); TEST_FAILED; } ctx->Release(); engine->ShutDownAndRelease(); } // Test GetTypeDeclaration with arrays // http://www.gamedev.net/topic/663428-simplest-way-to-get-variable-type/ { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); int typeId = engine->GetTypeIdByDecl("array<array<int>@>"); if( typeId < 0 ) TEST_FAILED; std::string typeDecl = engine->GetTypeDeclaration(typeId, true); if( typeDecl != "int[]@[]" ) { PRINTF("%s\n", typeDecl.c_str()); TEST_FAILED; } engine->SetDefaultNamespace("foo"); engine->RegisterEnum("MyEnum"); engine->SetDefaultNamespace(""); typeId = engine->GetTypeIdByDecl("array<foo::MyEnum>"); if( typeId < 0 ) TEST_FAILED; typeDecl = engine->GetTypeDeclaration(typeId, true); if( typeDecl != "foo::MyEnum[]" ) { PRINTF("%s\n", typeDecl.c_str()); TEST_FAILED; } engine->ShutDownAndRelease(); } asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArray()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PRINTF("%s", GetExceptionInfo(ctx).c_str()); PRINTF("%s: Failed to execute script\n", TESTNAME); TEST_FAILED; } if( ctx ) ctx->Release(); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script2, strlen(script2), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } r = ExecuteString(engine, "TestArrayException()", mod); if( r != asEXECUTION_EXCEPTION ) { PRINTF("%s: No exception\n", TESTNAME); TEST_FAILED; } mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script3, strlen(script3), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayMulti()", mod, ctx); if( r != asEXECUTION_FINISHED ) { PRINTF("%s: Failure\n", TESTNAME); TEST_FAILED; } if( r == asEXECUTION_EXCEPTION ) { PRINTF("%s", GetExceptionInfo(ctx).c_str()); } if( ctx ) ctx->Release(); ctx = 0; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script4, strlen(script4), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayChar()", mod, ctx); if( r != asEXECUTION_FINISHED ) { PRINTF("%s: Failure\n", TESTNAME); TEST_FAILED; } if( r == asEXECUTION_EXCEPTION ) { PRINTF("%s", GetExceptionInfo(ctx).c_str()); } if( ctx ) ctx->Release(); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script5, strlen(script5), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayInitList()", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PRINTF("%s", GetExceptionInfo(ctx).c_str()); if( ctx ) ctx->Release(); CBufferedOutStream bout; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script6, strlen(script6), 0); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestArray (1, 1) : Info : Compiling void Test()\n" "TestArray (4, 16) : Error : Initialization lists cannot be used with 'int'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } // Array object must call default constructor of the script classes engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script7, strlen(script7), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Test bool[] on Mac OS X with PPC CPU // Submitted by Edward Rudd const char *script8 = "bool[] f(10); \n" "for (int i=0; i<10; i++) { \n" " f[i] = false; \n" "} \n" "Assert(f[0] == false); \n" "Assert(f[1] == false); \n" "f[0] = true; \n" "Assert(f[0] == true); \n" "Assert(f[1] == false); \n"; r = ExecuteString(engine, script8); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Make sure it is possible to do multiple assignments with the array type r = ExecuteString(engine, "int[] a, b, c; a = b = c;"); if( r < 0 ) TEST_FAILED; engine->Release(); // Test circular reference between types { // Create the script engine asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); // Compile asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "class Hoge" "{" " Hoge(){}" " Hoge(HogeManager&){}" "};" "class HogeManager" "{" " Hoge[] hoges;" "};" , 0); mod->Build(); // Release engine engine->Release(); } // Test multidimensional array initialization { // Create the script engine asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); RegisterScriptArray(engine, true); r = ExecuteString(engine, "int[][] a(2, int[](2)); assert(a[1].length() == 2);\n"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Release engine engine->Release(); } // Test array of void { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); CBufferedOutStream bout; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); RegisterScriptArray(engine, false); r = ExecuteString(engine, "array<void> a;"); if( r != -1 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 7) : Error : Attempting to instantiate invalid template type 'array<void>'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Success return fail; }
bool Test() { bool fail = Test2(); int r; COutStream out; CBufferedOutStream bout; asIScriptContext *ctx; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); RegisterScriptArray(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); r = mod->Build(); if( r < 0 ) { fail = true; printf("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArray()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); printf("%s: Failed to execute script\n", TESTNAME); fail = true; } if( ctx ) ctx->Release(); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script2, strlen(script2), 0); r = mod->Build(); if( r < 0 ) { fail = true; printf("%s: Failed to compile the script\n", TESTNAME); } r = ExecuteString(engine, "TestArrayException()", mod); if( r != asEXECUTION_EXCEPTION ) { printf("%s: No exception\n", TESTNAME); fail = true; } // Must be possible to declare array of arrays mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script3, strlen(script3), 0); r = mod->Build(); if( r < 0 ) { fail = true; printf("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayMulti()", mod, ctx); if( r != asEXECUTION_FINISHED ) { printf("%s: Failure\n", TESTNAME); fail = true; } if( r == asEXECUTION_EXCEPTION ) { PrintException(ctx); } if( ctx ) ctx->Release(); ctx = 0; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script4, strlen(script4), 0); r = mod->Build(); if( r < 0 ) { fail = true; printf("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayChar()", mod, ctx); if( r != asEXECUTION_FINISHED ) { printf("%s: Failure\n", TESTNAME); fail = true; } if( r == asEXECUTION_EXCEPTION ) { PrintException(ctx); } if( ctx ) ctx->Release(); // Initialization lists must work for array template mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script5, strlen(script5), 0); r = mod->Build(); if( r < 0 ) fail = true; ctx = engine->CreateContext(); r = ExecuteString(engine, "TestArrayInitList()", mod, ctx); if( r != asEXECUTION_FINISHED ) fail = true; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); if( ctx ) ctx->Release(); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script6, strlen(script6), 0); r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "Test_Addon_ScriptArray (1, 1) : Info : Compiling void Test()\n" "Test_Addon_ScriptArray (3, 20) : Error : Initialization lists cannot be used with 'array<int>@'\n" "Test_Addon_ScriptArray (4, 21) : Error : Initialization lists cannot be used with 'int'\n" ) { printf(bout.buffer.c_str()); fail = true; } // Array object must call default constructor of the script classes engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script7, strlen(script7), 0); r = mod->Build(); if( r < 0 ) fail = true; r = ExecuteString(engine, "Test()", mod); if( r != asEXECUTION_FINISHED ) fail = true; // Test bool[] on Mac OS X with PPC CPU // Submitted by Edward Rudd const char *script8 = "array<bool> f(10); \n" "for (int i=0; i<10; i++) { \n" " f[i] = false; \n" "} \n" "Assert(f[0] == false); \n" "Assert(f[1] == false); \n" "f[0] = true; \n" "Assert(f[0] == true); \n" "Assert(f[1] == false); \n"; r = ExecuteString(engine, script8, mod); if( r != asEXECUTION_FINISHED ) fail = true; // Make sure it is possible to do multiple assignments with the array type r = ExecuteString(engine, "array<int> a, b, c; a = b = c;"); if( r < 0 ) fail = true; // Must support syntax as: array<array<int>>, i.e. without white space between the closing angled brackets. r = ExecuteString(engine, "array<array<int>> a(2); Assert( a.length() == 2 );"); if( r < 0 ) fail = true; // Must support arrays of handles r = ExecuteString(engine, "array<array<int>@> a(1); @a[0] = @array<int>(4);"); if( r < 0 ) fail = true; // Do not allow the instantiation of a template with a subtype that cannot be created bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); engine->RegisterObjectType("single", 0, asOBJ_REF | asOBJ_NOHANDLE); r = ExecuteString(engine, "array<single> a;"); if( r >= 0 ) fail = true; if( bout.buffer != "ExecuteString (1, 7) : Error : Can't instanciate template 'array' with subtype 'single'\n" ) { printf(bout.buffer.c_str()); fail = true; } engine->Release(); // Test too large arrays { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine); ctx = engine->CreateContext(); r = ExecuteString(engine, "array<int> a; a.resize(0xFFFFFFFF);", 0, ctx); if( r != asEXECUTION_EXCEPTION ) { fail = true; } else if( strcmp(ctx->GetExceptionString(), "Too large array size") != 0 ) { fail = true; } r = ExecuteString(engine, "array<int> a(0xFFFFFFFF);", 0, ctx); if( r != asEXECUTION_EXCEPTION ) { fail = true; } else if( strcmp(ctx->GetExceptionString(), "Too large array size") != 0 ) { fail = true; } ctx->Release(); engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; asIScriptEngine *engine; CBufferedOutStream bout; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->ClearMessageCallback(); // Make sure this works engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); RegisterScriptArray(engine, true); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptFunction *func = engine->GetGlobalFunctionByDecl("void assert(bool)"); if( func == 0 ) TEST_FAILED; if( std::string(func->GetDeclaration()) != "void assert(bool)" ) TEST_FAILED; r = engine->RegisterGlobalFunction("void func(mytype)", asFUNCTION(0), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void func(int &)", asFUNCTION(0), asCALL_GENERIC); if( !engine->GetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES) ) { if( r >= 0 ) TEST_FAILED; } else { if( r < 0 ) TEST_FAILED; } r = engine->RegisterObjectType("mytype", 0, asOBJ_REF); if( r < 0 ) TEST_FAILED; r = engine->RegisterObjectBehaviour("mytype", asBEHAVE_CONSTRUCT, "void f(othertype)", asFUNCTION(0), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; r = engine->RegisterObjectMethod("mytype", "type opAdd(int) const", asFUNCTION(0), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; r = engine->RegisterGlobalProperty("type a", (void*)1); if( r >= 0 ) TEST_FAILED; r = engine->RegisterObjectMethod("mytype", "void method(int &)", asFUNCTION(0), asCALL_GENERIC); if( !engine->GetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES) ) { if( r >= 0 ) TEST_FAILED; } else { if( r < 0 ) TEST_FAILED; } r = engine->RegisterObjectProperty("mytype", "type a", 0); if( r >= 0 ) TEST_FAILED; r = engine->RegisterStringFactory("type", asFUNCTION(0), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; // Verify the output messages if( !engine->GetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES) ) { if( bout.buffer != "System function (1, 11) : Error : Identifier 'mytype' is not a data type in global namespace\n" " (0, 0) : Error : Failed in call to function 'RegisterGlobalFunction' with 'void func(mytype)' (Code: -10)\n" "System function (1, 15) : Error : Only object types that support object handles can use &inout. Use &in or &out instead\n" " (0, 0) : Error : Failed in call to function 'RegisterGlobalFunction' with 'void func(int &)' (Code: -10)\n" "System function (1, 8) : Error : Identifier 'othertype' is not a data type in global namespace\n" " (0, 0) : Error : Failed in call to function 'RegisterObjectBehaviour' with 'mytype' and 'void f(othertype)' (Code: -10)\n" "System function (1, 1) : Error : Identifier 'type' is not a data type in global namespace\n" " (0, 0) : Error : Failed in call to function 'RegisterObjectMethod' with 'mytype' and 'type opAdd(int) const' (Code: -10)\n" "Property (1, 1) : Error : Identifier 'type' is not a data type in global namespace\n" " (0, 0) : Error : Failed in call to function 'RegisterGlobalProperty' with 'type a' (Code: -10)\n" "System function (1, 17) : Error : Only object types that support object handles can use &inout. Use &in or &out instead\n" " (0, 0) : Error : Failed in call to function 'RegisterObjectMethod' with 'mytype' and 'void method(int &)' (Code: -10)\n" "Property (1, 1) : Error : Identifier 'type' is not a data type in global namespace\n" " (0, 0) : Error : Failed in call to function 'RegisterObjectProperty' with 'mytype' and 'type a' (Code: -10)\n" " (1, 1) : Error : Identifier 'type' is not a data type in global namespace\n" " (0, 0) : Error : Failed in call to function 'RegisterStringFactory' with 'type' (Code: -12)\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } } else { if( bout.buffer != "System function (1, 11) : Error : Identifier 'mytype' is not a data type\n" "System function (1, 8) : Error : Identifier 'othertype' is not a data type\n" "System function (1, 1) : Error : Identifier 'type' is not a data type\n" "System function (1, 8) : Error : Identifier 'type' is not a data type\n" "Property (1, 1) : Error : Identifier 'type' is not a data type\n" "Property (1, 1) : Error : Identifier 'type' is not a data type\n" " (1, 1) : Error : Identifier 'type' is not a data type\n") TEST_FAILED; } engine->Release(); // Success return fail; }
bool Test() { bool fail = false; // Create the script engine asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); // Register Function RegisterScriptString(engine); engine->RegisterGlobalFunction("void Print(string &in)", asFUNCTION(PrintString_Generic), asCALL_GENERIC); // Compile 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(); // Context Create asIScriptContext *ctx = engine->CreateContext(); // Loop for( asUINT n = 0; n < 3; n++ ) { // Execute //printf("----- execute\n"); ctx->Prepare(mod->GetFunctionIdByDecl("void main()")); ctx->Execute(); // GC const int GC_STEP_COUNT_PER_FRAME = 100; for ( int i = 0; i < GC_STEP_COUNT_PER_FRAME; ++i ) { engine->GarbageCollect(asGC_ONE_STEP); } // Check status { asUINT currentSize = asUINT(); asUINT totalDestroyed = asUINT(); asUINT totalDetected = asUINT(); engine->GetGCStatistics(¤tSize , &totalDestroyed , &totalDetected ); if( currentSize != 8 || totalDestroyed != n+1 || totalDetected != 0 ) TEST_FAILED; //printf("(%lu,%lu,%lu)\n" , currentSize , totalDestroyed , totalDetected ); } } // Release ctx->Release(); engine->Release(); // Test { COutStream out; int r; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(0, "interface ITest\n" "{\n" "}\n" "class Test : ITest\n" "{\n" " ITest@[] arr;\n" " void Set(ITest@ e)\n" " {\n" " arr.resize(1);\n" " @arr[0]=e;\n" " }\n" "}\n" "void main()\n" "{\n" " Test@ t=Test();\n" " t.Set(t);\n" "}\n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; asUINT currentSize; engine->GetGCStatistics(¤tSize); r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->GetGCStatistics(¤tSize); engine->Release(); } // Test attempted access of global variable after it has been destroyed { COutStream out; int r; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); engine->RegisterGlobalFunction("void Log(const string &in)", asFUNCTION(PrintString_Generic), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(0, "class Big \n" "{ \n" " Big() \n" " { \n" " Log('Big instance created\\n'); \n" " } \n" " ~Big() \n" " { \n" " Log('Big instance being destroyed\\n'); \n" " } \n" " void exec() \n" " { \n" " Log('executed\\n'); \n" " } \n" "} \n" "Big big; \n" // Global object "class SomeClass \n" "{ \n" " SomeClass@ handle; \n" // Make sure SomeClass is garbage collected " SomeClass() {} \n" " ~SomeClass() \n" " { \n" " Log('Before attempting access to global var\\n'); \n" " big.exec(); \n" // As the module has already been destroyed, the global variable won't exist anymore, thus raising a null pointer exception here " Log('SomeClass instance being destroyed\\n'); \n" // This won't be called " } \n" "} \n" "void test_main() \n" "{ \n" " SomeClass @something = @SomeClass(); \n" // Instanciate the object. It will only be destroyed by the GC "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "test_main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // The global variables in the module will be destroyed first. The objects in the GC that // tries to access them should throw exception, but should not cause the application to crash called = 0; engine->Release(); if( called != 2 ) TEST_FAILED; } /* { // This test forces a memory leak due to not registering the GC behaviours for the CFoo class COutStream out; int r; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterInterface("IMyInterface"); engine->RegisterObjectType("CFoo", sizeof(CFoo), asOBJ_REF); engine->RegisterObjectBehaviour("CFoo", asBEHAVE_ADDREF, "void f()", asMETHOD(CFoo, AddRef), asCALL_THISCALL); engine->RegisterObjectBehaviour("CFoo", asBEHAVE_RELEASE, "void f()", asMETHOD(CFoo, Release), asCALL_THISCALL); engine->RegisterObjectBehaviour("CFoo", asBEHAVE_FACTORY, "CFoo@ f()", asFUNCTION(&CFoo::CreateObject), asCALL_CDECL); engine->RegisterObjectMethod("CFoo", "void SetObject(IMyInterface@)", asMETHOD(CFoo, SetScriptObject), asCALL_THISCALL); const char *script = "CBar test; \n" "class CBase : IMyInterface \n" "{ \n" " IMyInterface@ m_dummy; \n" // Comment only this and everything is ok "} \n" "class CBar : CBase \n" "{ \n" " CBar() \n" " { \n" " m_foo.SetObject(this); \n" // Comment only this and everything is ok " } \n" " CFoo m_foo; \n" "}; "; asIScriptModule *mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); engine->Release(); } */ return fail; }
bool Test() { if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { printf("%s: Skipped due to AS_MAX_PORTABILITY\n", TESTNAME); return false; } bool fail = false; int r; COutStream out; asIScriptEngine *engine; asIScriptModule *mod; asIScriptContext *ctx; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); r = engine->RegisterObjectType("refclass", sizeof(CRefClass), asOBJ_REF); assert(r >= 0); r = engine->RegisterObjectBehaviour("refclass", asBEHAVE_FACTORY, "refclass@ f()", asFUNCTION(Factory), asCALL_CDECL); assert(r >= 0); r = engine->RegisterObjectBehaviour("refclass", asBEHAVE_ADDREF, "void f()", asMETHOD(CRefClass, AddRef), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("refclass", asBEHAVE_RELEASE, "void f()", asMETHOD(CRefClass, Release), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("refclass", "refclass &opAssign(refclass &in)", asMETHOD(CRefClass, operator=), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("refclass", "refclass &Do()", asMETHOD(CRefClass,Do), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("refclass", "refclass &opAdd(refclass &in)", asFUNCTION(CRefClass::Add), asCALL_CDECL_OBJFIRST); assert(r >= 0); r = engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestObjHandle()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; printf("%s: Execution failed\n", TESTNAME); } if( ctx ) ctx->Release(); // Call TestObjReturnHandle() from the application to verify that references are updated as necessary ctx = engine->CreateContext(); ctx->Prepare(engine->GetModule(0)->GetFunctionByDecl("refclass@ TestObjReturnHandle(refclass@)")); CRefClass *refclass = new CRefClass(); ctx->SetArgObject(0, refclass); r = ctx->Execute(); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; printf("%s: Execution failed\n", TESTNAME); } if( refclass->refCount != 2 ) { TEST_FAILED; printf("%s: Ref count is wrong\n", TESTNAME); } refclass->Release(); if( ctx ) ctx->Release(); // Test returning a reference to the object from an object method r = engine->GarbageCollect(); asUINT gcCurrentSize; engine->GetGCStatistics(&gcCurrentSize, 0, 0); assert( gcCurrentSize == 0 ); r = ExecuteString(engine, "refclass ref; ref.Do()"); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } engine->Release(); //-------------------- engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); r = engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script5, strlen(script5), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); //---------------------- // It should be allowed to have a global function return a handle to a const object engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); const char *scriptC = "class T {} const T@ func() {return T();}"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", scriptC, strlen(scriptC)); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->Release(); //--------------------- // These tests are designed to make sure ambiguities with handles is avoided CBufferedOutStream bout; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); r = engine->RegisterObjectType("A", sizeof(CRefClass), asOBJ_REF); assert(r >= 0); r = engine->RegisterObjectBehaviour("A", asBEHAVE_FACTORY, "A@ f()", asFUNCTION(Factory), asCALL_CDECL); assert(r >= 0); r = engine->RegisterObjectBehaviour("A", asBEHAVE_ADDREF, "void f()", asMETHOD(CRefClass, AddRef), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("A", asBEHAVE_RELEASE, "void f()", asMETHOD(CRefClass, Release), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("A", "A &opAssign(const A &in)", asMETHOD(CRefClass, operator=), asCALL_THISCALL); assert(r >= 0); bout.buffer = ""; r = ExecuteString(engine, "A a; a == null;"); // Should give warning if( r < 0 || bout.buffer == "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; null == a;"); // Should give warning if( r < 0 || bout.buffer == "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; @a == null;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; null == @a;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; @a == a;"); // Should give warning if( r < 0 || bout.buffer == "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; a == @a;"); // Should give warning if( r < 0 || bout.buffer == "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; @a == @a;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A @a = null;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; A @b = a;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; A @b = @a;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; A b = @b;"); // Should give error if( r >= 0 || bout.buffer == "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A @a, b; @a = @b;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A @a, b; @a = b;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A @a, b; a = @b;"); // Should give error if( r >= 0 || bout.buffer == "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; null is a;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } bout.buffer = ""; r = ExecuteString(engine, "A a; a !is null;"); // OK if( r < 0 || bout.buffer != "" ) { TEST_FAILED; } engine->Release(); // Success return fail; }