bool Test() { bool fail = false; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); r = engine->RegisterObjectType("GameMgr", 0, asOBJ_REF | asOBJ_NOHANDLE); assert(r >= 0); r = engine->RegisterObjectMethod("GameMgr", "void Test()", asFUNCTION(TestMethod), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalProperty("GameMgr Game", (void*)&GameMgr); assert(r >= 0); // Test registering another object globabl property after // compiling script that uses previous global property asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script, strlen(script), 0); mod->Build(); r = engine->RegisterObjectType("SoundMgr", 0, asOBJ_REF | asOBJ_NOHANDLE); assert(r >= 0); r = engine->RegisterObjectMethod("SoundMgr", "void Test()", asFUNCTION(TestMethod), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalProperty("SoundMgr SMgr", (void*)&SoundMgr); assert(r >= 0); engine->ExecuteString(0, "Game.Test()"); engine->ExecuteString(0, "SMgr.Test()"); engine->ExecuteString(0, "TestSingleton()"); engine->Release(); // Success return fail; }
bool TestHelper() { 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; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); // TODO: Add validation of return type RegisterGlobalFunction(engine, "void func1(int)", func1, asCALL_CDECL); RegisterGlobalFunction(engine, "void func2(string &in)", func2, asCALL_CDECL); RegisterGlobalFunction(engine, "void func3(string @)", func3, asCALL_CDECL); RegisterGlobalFunction(engine, "void func4(int &in)", func4, asCALL_CDECL); RegisterGlobalFunction(engine, "void func5(int &out)", func5, asCALL_CDECL); RegisterGlobalFunction(engine, "void func6(string @&out)", func6, asCALL_CDECL); RegisterGlobalFunction(engine, "void func7(string @&out)", func7, asCALL_CDECL); RegisterGlobalFunction(engine, "void func8(int, string &)", func8, asCALL_CDECL); RegisterGlobalFunction(engine, "void func9(string &, int)", func9, asCALL_CDECL); engine->Release(); return fail; }
bool TestFuncOverload() { bool fail = false; COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetCommonMessageStream(&out); RegisterScriptString(engine); engine->RegisterObjectType("Data", sizeof(void*), asOBJ_PRIMITIVE); engine->RegisterObjectType("Obj", sizeof(Obj), 0); engine->RegisterObjectMethod("Obj", "Data &Value()", asMETHOD(Obj, Value), asCALL_THISCALL); engine->RegisterObjectMethod("Obj", "void Set(string &in, Data &in)", asMETHOD(Obj, Set), asCALL_THISCALL); engine->RegisterObjectMethod("Obj", "void Set(string &in, string &in)", asMETHOD(Obj, Set), asCALL_THISCALL); engine->RegisterGlobalProperty("Obj TX", &o); engine->RegisterGlobalFunction("void func()", asFUNCTION(FuncVoid), asCALL_CDECL); engine->RegisterGlobalFunction("void func(int)", asFUNCTION(FuncInt), asCALL_CDECL); engine->AddScriptSection(0, TESTNAME, script1, strlen(script1), 0); engine->Build(0); engine->ExecuteString(0, "func(func(3));"); engine->Release(); // Success return fail; }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("AngelScript 2.15.0 : 2.513 secs\n"); printf("AngelScript 2.15.1 WIP : 2.513 secs\n"); printf("\nBuilding...\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script, strlen(script), 0); mod->Build(); asIScriptContext *ctx = engine->CreateContext(); int funcId_A = mod->GetFunctionIdByDecl("void TestCall2_A()"); int funcId_B = mod->GetFunctionIdByDecl("void TestCall2_B()"); printf("Executing AngelScript version...\n"); double time = GetSystemTimer(); int r; for( int n = 0; n < 5000000; n++ ) { ctx->Prepare(funcId_A); r = ctx->Execute(); if( r != 0 ) break; ctx->Prepare(funcId_B); r = ctx->Execute(); if( r != 0 ) break; } time = GetSystemTimer() - time; if( r != 0 ) { printf("Execution didn't terminate with asEXECUTION_FINISHED\n", TESTNAME); if( r == asEXECUTION_EXCEPTION ) { printf("Script exception\n"); asIScriptFunction *func = engine->GetFunctionDescriptorById(ctx->GetExceptionFunction()); printf("Func: %s\n", func->GetName()); printf("Line: %d\n", ctx->GetExceptionLineNumber()); printf("Desc: %s\n", ctx->GetExceptionString()); } } else printf("Time = %f secs\n", time); ctx->Release(); engine->Release(); }
bool TestFuncOverload() { if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { printf("%s: Skipped due to AS_MAX_PORTABILITY\n", TESTNAME); return false; } // TODO: Add Test2 again bool fail = false; //Test2(); COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); engine->RegisterObjectType("Data", sizeof(void*), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE); engine->RegisterObjectType("Obj", sizeof(Obj), asOBJ_REF | asOBJ_NOHANDLE); engine->RegisterObjectMethod("Obj", "Data &Value()", asMETHOD(Obj, Value), asCALL_THISCALL); engine->RegisterObjectMethod("Obj", "void Set(string &in, Data &in)", asMETHOD(Obj, Set), asCALL_THISCALL); engine->RegisterObjectMethod("Obj", "void Set(string &in, string &in)", asMETHOD(Obj, Set), asCALL_THISCALL); engine->RegisterGlobalProperty("Obj TX", &o); engine->RegisterGlobalFunction("void func()", asFUNCTION(FuncVoid), asCALL_CDECL); engine->RegisterGlobalFunction("void func(int)", asFUNCTION(FuncInt), asCALL_CDECL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); int r = mod->Build(); if( r < 0 ) fail = true; ExecuteString(engine, "func(func(3));", mod); CBufferedOutStream bout; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script2, strlen(script2), 0); r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "TestFuncOverload (1, 1) : Info : Compiling void ScriptFunc(void)\n" "TestFuncOverload (1, 17) : Error : Parameter type can't be 'void'\n" ) { printf("%s", bout.buffer.c_str()); fail = true; } // Permit void parameter list r = engine->RegisterGlobalFunction("void func2(void)", asFUNCTION(FuncVoid), asCALL_CDECL); assert( r >= 0 ); // Don't permit void parameters r = engine->RegisterGlobalFunction("void func3(void n)", asFUNCTION(FuncVoid), asCALL_CDECL); assert( r < 0 ); engine->Release(); 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; COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->SetEngineProperty(asEP_ALLOW_IMPLICIT_HANDLE_TYPES, true); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_CDECL); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script1, strlen(script1), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("Failed to compile the script\n"); } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; printf("Execution failed\n"); } if( output != "Hello!\nCreated\n---\n7\n---\n7\n" ) { TEST_FAILED; printf("Got: \n%s", output.c_str()); } // TODO: The equality operator shouldn't perform handle comparison /* r = engine->ExecuteString(0, "MyClass a; assert( a == null );"); if( r >= 0 ) { TEST_FAILED; } */ engine->Release(); // Success return fail; }
bool Test() { bool fail = false; int number = 0; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); engine->RegisterGlobalProperty("int number", &number); engine->RegisterObjectType("OBJ", sizeof(int), asOBJ_PRIMITIVE); COutStream out; engine->AddScriptSection(0, TESTNAME ":1", script1, strlen(script1), 0); engine->SetCommonMessageStream(&out); engine->Build(0); engine->AddScriptSection("DynamicModule", TESTNAME ":2", script2, strlen(script2), 0); engine->Build("DynamicModule"); // Bind all functions that the module imports r = engine->BindAllImportedFunctions(0); assert( r >= 0 ); // Save the compiled byte code CBytecodeStream stream; engine->SaveByteCode(0, &stream); // Load the compiled byte code into the same module engine->LoadByteCode(0, &stream); // Verify if handles are properly resolved int funcID = engine->GetFunctionIDByDecl(0, "void TestHandle(string @)"); if( funcID < 0 ) { printf("%s: Failed to identify function with handle\n", TESTNAME); fail = true; } // Bind the imported functions again r = engine->BindAllImportedFunctions(0); assert( r >= 0 ); engine->ExecuteString(0, "main()"); engine->Release(); if( number != 1234567890 ) { printf("%s: Failed to set the number as expected\n", TESTNAME); fail = true; } // Success 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; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); // register the factory engine->RegisterObjectType( "MyFactory", 0, asOBJ_REF | asOBJ_NOHANDLE ); engine->RegisterGlobalProperty( "MyFactory myFactory", &MyFactory::Get() ); engine->RegisterObjectMethod( "MyFactory", "void Test(const string &in, int x, int y, uint z)", asFUNCTION(TestManager), asCALL_CDECL_OBJLAST ); // test 1 MyFactory::Get().Reset(); r = ExecuteString(engine, "int x = 20; int x2 = 30; int y = 50; myFactory.Test(\"testing\", x - -x2, y, 0x000000FF);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( MyFactory::Get().IsError() ) TEST_FAILED; // test 2 - without bytecode optimization engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, false); MyFactory::Get().Reset(); r = ExecuteString(engine, "int x = 20; int x2 = 30; int y = 50; myFactory.Test(\"testing\", x + x2, y, 0x000000FF);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( MyFactory::Get().IsError() ) TEST_FAILED; // test 3 - with bytecode optimization engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, true); MyFactory::Get().Reset(); r = ExecuteString(engine, "int x = 20; int x2 = 30; int y = 50; myFactory.Test(\"testing\", x + x2, y, 0x000000FF);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( MyFactory::Get().IsError() ) TEST_FAILED; // release the engine engine->Release(); // release the factory MyFactory::Release(); return fail; }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("AngelScript 2.4.1 : 7.941 secs\n"); printf("AngelScript 2.5.0 WIP 1 : 5.788 secs\n"); printf("AngelScript 2.7.0 rev 36 : 5.727 secs\n"); printf("AngelScript 2.7.0 rev 37 : 5.736 secs\n"); printf("\nBuilding...\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetCommonMessageStream(&out); RegisterScriptString(engine); engine->AddScriptSection(0, TESTNAME, script, strlen(script), 0); engine->Build(0); asIScriptContext *ctx = engine->CreateContext(); ctx->Prepare(engine->GetFunctionIDByDecl(0, "void TestString()")); printf("Executing AngelScript version...\n"); double time = GetSystemTimer(); int r = ctx->Execute(); time = GetSystemTimer() - time; if( r != 0 ) { printf("Execution didn't terminate with asEXECUTION_FINISHED\n", TESTNAME); if( r == asEXECUTION_EXCEPTION ) { printf("Script exception\n"); printf("Func: %s\n", engine->GetFunctionName(ctx->GetExceptionFunction())); printf("Line: %d\n", ctx->GetExceptionLineNumber()); printf("Desc: %s\n", ctx->GetExceptionString()); } } else printf("Time = %f secs\n", time); ctx->Release(); engine->Release(); }
cLowLevelSystemSDL::cLowLevelSystemSDL() { mpScriptEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION); mpScriptOutput = hplNew( cScriptOutput, () ); mpScriptEngine->SetMessageCallback(asMETHOD(cScriptOutput,AddMessage), mpScriptOutput, asCALL_THISCALL); #ifdef AS_MAX_PORTABILITY RegisterScriptString(mpScriptEngine); #else RegisterStdString(mpScriptEngine); #endif mlHandleCount = 0; Log("-------- THE HPL ENGINE LOG ------------\n\n"); }
bool Test() { if( !strstr(asGetLibraryOptions(), "AS_ALLOW_UNSAFE_REFERENCES") ) { printf("%s: This test is only valid with AS_ALLOW_UNSAFE_REFERENCES\n", TESTNAME); return false; } bool fail = false; int r; COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); engine->AddScriptSection(0, TESTNAME, script1, strlen(script1), 0); r = engine->Build(0); if( r < 0 ) { fail = true; printf("%s: Failed to compile the script\n", TESTNAME); } asIScriptContext *ctx = 0; r = engine->ExecuteString(0, "Test()", &ctx); if( r != asEXECUTION_FINISHED ) { fail = true; printf("%s: Execution failed: %d\n", TESTNAME, r); } if( ctx ) ctx->Release(); engine->Release(); // Success return fail; }
bool Test2() { bool fail = false; int r; COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); const char *string = "class Jerome \n" "{ \n" " string a; \n" " string b; \n" " double c; \n" " Jerome(string A,string B,double C) \n" " { \n" " a = A; \n" " b = B; \n" " c = C; \n" " assert( a == 'Hello' ); \n" " assert( b == 'Hi' ); \n" " assert( c == 1.23456 ); \n" " } \n" "} \n" "Jerome cc('Hello','Hi',1.23456); \n"; asIScriptModule *mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", string); r = mod->Build(); if( r < 0 ) { fail = true; } engine->Release(); return fail; }
bool Test2() { if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) return false; bool fail = false; COutStream out; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->RegisterGlobalFunction("void debugCall()", asFUNCTION(DebugCall), asCALL_CDECL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "void func(string@ &out output) \n" "{ \n" " debugCall(); \n" " assert( output == 'test' ); \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "string @o; func(o); assert( o == 'test' );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); return fail; }
bool Test() { bool fail = false; int r; COutStream out; 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 ) { fail = true; printf("%s: Failed to compile the script\n", TESTNAME); } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "Test()", mod, ctx); if( r != asEXECUTION_FINISHED ) { fail = true; printf("%s: Execution failed: %d\n", TESTNAME, r); } if( ctx ) ctx->Release(); engine->Release(); // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; CBufferedOutStream bout; asIScriptEngine *engine = 0; asIScriptModule *mod = 0; // It must not be possible to declare global variables of the var type ? engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script1 = "? globvar;"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script1); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Error : Unexpected token '?'\n" ) TEST_FAILED; bout.buffer = ""; // It must not be possible to declare local variables of the var type ? const char *script2 = "void func() {? localvar;}"; mod->AddScriptSection("script", script2); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Info : Compiling void func()\n" "script (1, 14) : Error : Expected expression value\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to register global properties of the var type ? r = engine->RegisterGlobalProperty("? prop", (void*)1); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "Property (1, 1) : Error : Expected data type\n" " (0, 0) : Error : Failed in call to function 'RegisterGlobalProperty' with '? prop'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; engine->Release(); // It must not be possible to register object members of the var type ? engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = engine->RegisterObjectType("test", 0, asOBJ_REF); assert( r >= 0 ); r = engine->RegisterObjectProperty("test", "? prop", 0); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "Property (1, 1) : Error : Expected data type\n" " (0, 0) : Error : Failed in call to function 'RegisterObjectProperty' with 'test' and '? prop'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; engine->Release(); // It must not be possible to declare script class members of the var type ? engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script3 = "class c {? member;}"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script3); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 10) : Error : Expected method or property\n" "script (1, 19) : Error : Unexpected token '}'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to declare script functions that take the var type ? as parameter const char *script4 = "void func(?&in a) {}"; mod->AddScriptSection("script", script4); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 11) : Error : Expected data type\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to declare script functions that return the var type ? const char *script5 = "? func() {}"; mod->AddScriptSection("script", script5); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Error : Unexpected token '?'\n" ) TEST_FAILED; bout.buffer = ""; // It must not be possible to declare script class methods that take the var type ? as parameter const char *script6 = "class c {void method(?& in a) {}}"; mod->AddScriptSection("script", script6); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 22) : Error : Expected data type\n" "script (1, 22) : Error : Expected method or property\n" "script (1, 33) : Error : Unexpected token '}'\n" ) TEST_FAILED; bout.buffer = ""; // It must not be possible to declare script class methods that return the var type ? const char *script7 = "class c {? method() {}}"; mod->AddScriptSection("script", script7); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 10) : Error : Expected method or property\n" "script (1, 23) : Error : Unexpected token '}'\n" ) TEST_FAILED; bout.buffer = ""; // It must not be possible to declare arrays of the var type ? const char *script8 = "void func() { ?[] array; }"; mod->AddScriptSection("script", script8); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Info : Compiling void func()\n" "script (1, 15) : Error : Expected expression value\n" ) TEST_FAILED; bout.buffer = ""; // It must not be possible to declare handles of the var type ? const char *script9 = "void func() { ?@ handle; }"; mod->AddScriptSection("script", script9); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Info : Compiling void func()\n" "script (1, 15) : Error : Expected expression value\n" ) TEST_FAILED; bout.buffer = ""; // It must not be possible to register functions that return the var type ? r = engine->RegisterGlobalFunction("? testFunc()", asFUNCTION(testFuncI), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "System function (1, 1) : Error : Expected data type\n" " (0, 0) : Error : Failed in call to function 'RegisterGlobalFunction' with '? testFunc()'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; engine->Release(); // It must be possible to register functions that take the var type ? as parameter // Only when the expression is explicitly sent as @ should the type id be @ // const ? & in // ? & in engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void testFuncI(?& in)", asFUNCTION(testFuncI), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void testFuncCI(const?&in)", asFUNCTION(testFuncI), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void testFuncS(string &in)", asFUNCTION(testFuncS), asCALL_GENERIC); r = ExecuteString(engine, "int a = 42; testFuncI(a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"test\"; testFuncI(a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string @a = @\"test\"; testFuncI(@a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Both functions should receive the string by reference r = ExecuteString(engine, "string a = 'test'; testFuncI(a); testFuncS(a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to register with 'out' references // ? & out r = engine->RegisterGlobalFunction("void testFuncO(?&out)", asFUNCTION(testFuncO), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "int a; testFuncO(a); assert(a == 42);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a; testFuncO(a); assert(a == \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string @a; testFuncO(@a); assert(a == \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to mix normal parameter types with the var type ? // e.g. func(const string &in, const ?& in), or func(const ?& in, const string &in) r = engine->RegisterGlobalFunction("void testFuncIS(?& in, const string &in)", asFUNCTION(testFuncIS_generic), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void testFuncSI(const string &in, ?& in)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "int a = 42; testFuncIS(a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "int a = 42; testFuncSI(\"test\", a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"t\"; testFuncIS(a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"t\"; testFuncIS(@a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to use native functions if( !strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { r = engine->RegisterGlobalFunction("void _testFuncIS(?& in, const string &in)", asFUNCTION(testFuncIS), asCALL_CDECL); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void _testFuncSI(const string &in, ?& in)", asFUNCTION(testFuncSI), asCALL_CDECL); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "int a = 42; _testFuncIS(a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "int a = 42; _testFuncSI(\"test\", a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"t\"; _testFuncIS(a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"t\"; _testFuncIS(@a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; } // Don't give error on passing reference to const to ?&out engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script = "class C { string @a; } \n"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); mod->Build(); r = ExecuteString(engine, "const C c; testFuncO(@c.a);", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 23) : Warning : Argument cannot be assigned. Output will be discarded.\n" ) TEST_FAILED; bout.buffer = ""; // ?& with opAssign is allowed, but won't be used with the assignment operator // TODO: Support ?& with the operators as well engine->RegisterObjectType("type", sizeof(int), asOBJ_VALUE | asOBJ_APP_PRIMITIVE); r = engine->RegisterObjectMethod("type", "type &opAssign(const ?& in)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; // TODO: This is a valid class method, but should perhaps not be allowed to be used as operator /* r = engine->RegisterObjectMethod("type", "type opAdd(const ?& in)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; */ // Don't allow use of ? without being reference r = engine->RegisterGlobalFunction("void testFunc_err(const ?)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; // Don't allow use of 'inout' reference, yet // ? & [inout] // const ? & [inout] r = engine->RegisterGlobalFunction("void testFuncIO(?&)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void testFuncCIO(const?&)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; engine->Release(); { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); r = engine->RegisterObjectType("obj", sizeof(int), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE); assert( r >= 0 ); r = engine->RegisterObjectMethod("obj", "string @fmt(const string &in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule("1", asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "class App {\n" " int Run() {\n" " return 0;\n" " }\n" "}\n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->Release(); } return fail; }
bool Test() { bool fail = Test2(); int r; COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script, strlen(script)); r = mod->Build(); if( r < 0 ) fail = true; r = engine->ExecuteString(0, "uint8 newmask = 0xFF, mask = 0x15; Assert( (newmask & ~mask) == 0xEA );"); if( r != asEXECUTION_FINISHED ) fail = true; r = engine->ExecuteString(0, "uint8 newmask = 0xFF; newmask = newmask & (~mask2) & (~mask3) & (~mask5); Assert( newmask == 0xD3 );"); if( r != asEXECUTION_FINISHED ) fail = true; r = engine->ExecuteString(0, "uint8 newmask = 0XFE; Assert( (newmask & mask0) == 0 );"); if( r != asEXECUTION_FINISHED ) fail = true; r = engine->ExecuteString(0, "uint8 b = 0xFF; b &= ~mask4; BitsTest(b);"); if( r != asEXECUTION_FINISHED ) fail = true; engine->RegisterGlobalFunction("uint8 ReturnByte(uint8)", asFUNCTION(ReturnByte), asCALL_GENERIC); engine->RegisterGlobalFunction("uint16 ReturnWord(uint16)", asFUNCTION(ReturnWord), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script2, strlen(script2)); engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, false); r = mod->Build(); if( r < 0 ) fail = true; r = engine->ExecuteString(0, "Test()"); if( r != asEXECUTION_FINISHED ) fail = true; // bitwise operators should maintain signed/unsigned type of left hand operand CBufferedOutStream bout; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = engine->ExecuteString(0, "int a = 0, b = 0; bool c = (a < (b>>1));"); if( r < 0 ) fail = true; r = engine->ExecuteString(0, "uint a = 0, b = 0; bool c = (a < (b>>1));"); if( r < 0 ) fail = true; if( bout.buffer != "" ) { printf(bout.buffer.c_str()); fail = true; } engine->Release(); // Success return fail; }
bool Test2() { bool fail = false; CBufferedOutStream bout; int r; asIScriptModule *mod; asIScriptEngine *engine; const char *script; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); RegisterScriptString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); // Test that it is not possible to inherit from application registered type script = "class A : string {} \n"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 11) : Error : Can't inherit from 'string'\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to inherit from multiple script classes script = "class B {} class C {} class D {} class A : B, C, D {} \n"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 47) : Error : Can't inherit from multiple classes\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to inherit from a class that in turn inherits from this class script = "class A : C {} class B : A {} class C : B {}\n"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 41) : Error : Can't inherit from itself, or another class that inherits from this class\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to inherit from self script = "class A : A {}\n"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 11) : Error : Can't inherit from itself, or another class that inherits from this class\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that derived classes can't overload properties // TODO: In C++ it is possible to overload properties, in which case the base class property is hidden. Should we adopt this for AngelScript too? script = "class A { int a; } class B : A { double a; }\n"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; // TODO: The error should explain that the original property is from the base class if( bout.buffer != "script (1, 41) : Error : Name conflict. 'a' is an object property.\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to call super() when not deriving from any class script = "class A { A() { super(); } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; // TODO: The error message should explain that it is not possible to call super // because the class doesn't derived from another class if( bout.buffer != "script (1, 11) : Info : Compiling void A::A()\n" "script (1, 17) : Error : No matching signatures to 'A::super()'\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to call super() multiple times within the constructor script = "class A {} class B : A { B() { super(); super(); } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 26) : Info : Compiling void B::B()\n" "script (1, 41) : Error : Can't call a constructor multiple times\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to call super() in a loop script = "class A {} class B : A { B() { while(true) { super(); } } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 26) : Info : Compiling void B::B()\n" "script (1, 46) : Error : Can't call a constructor in loops\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to call super() in a switch // TODO: Should allow call in switch, but all possibilities must call it once. script = "class A {} class B : A { B() { switch(2) { case 2: super(); } } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 26) : Info : Compiling void B::B()\n" "script (1, 52) : Error : Can't call a constructor in switch\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that all (or none) control paths must call super() script = "class A {} class B : A { \n" "B(int) { if( true ) super(); } \n" "B(float) { if( true ) {} else super(); } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (2, 1) : Info : Compiling void B::B(int)\n" "script (2, 10) : Error : Both conditions must call constructor\n" "script (3, 1) : Info : Compiling void B::B(float)\n" "script (3, 12) : Error : Both conditions must call constructor\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to call super() outside of the constructor script = "class A {} class B : A { void mthd() { super(); } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 26) : Info : Compiling void B::mthd()\n" "script (1, 40) : Error : No matching signatures to 'super()'\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that a base class can't have a derived class as member (except as handle) script = "class A { B b; } class B : A {}"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; // TODO: The message could be improved to mention which member if( bout.buffer != "script (1, 24) : Error : Illegal member type\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that it is not possible to call super with any scope prefix script = "class A { } class B : A { B() { ::super(); } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 27) : Info : Compiling void B::B()\n" "script (1, 33) : Error : No matching signatures to '::super()'\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that the error message for calling missing method with scope is correct script = "class A { void method() { B::test(); A::method(2); A::method(); method(3.15); B::A::a(); } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (1, 11) : Info : Compiling void A::method()\n" "script (1, 27) : Error : No matching signatures to 'B::test()'\n" "script (1, 38) : Error : No matching signatures to 'A::method(const uint)'\n" "script (1, 65) : Error : No matching signatures to 'A::method(const double)'\n" "script (1, 83) : Error : Invalid scope resolution\n" "script (1, 79) : Error : No matching signatures to 'B::a()'\n" ) { fail = true; printf(bout.buffer.c_str()); } // Test that calling the constructor from within the constructor // using the class name will create a new object. script = "A @a1, a2; class A { A() { @a1 = this; A(1); } A(int) { @a2 = this; } }"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r < 0 ) fail = true; if( bout.buffer != "" ) { fail = true; printf(bout.buffer.c_str()); } r = engine->ExecuteString(0, "A a; assert( a1 !is a2 ); assert( a1 !is null ); assert( a2 !is null );"); if( r != asEXECUTION_FINISHED ) { fail = true; } 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(); } // 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(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; CBufferedOutStream bout; asIScriptEngine *engine = 0; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); RegisterScriptDictionary(engine); r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script, strlen(script)); r = mod->Build(); if( r < 0 ) fail = true; asIScriptContext *ctx = 0; r = engine->ExecuteString(0, "Test()", &ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); fail = true; } ctx->Release(); asUINT gcCurrentSize, gcTotalDestroyed, gcTotalDetected; engine->GetGCStatistics(&gcCurrentSize, &gcTotalDestroyed, &gcTotalDetected); engine->GarbageCollect(); engine->GetGCStatistics(&gcCurrentSize, &gcTotalDestroyed, &gcTotalDetected); if( gcCurrentSize != 0 || gcTotalDestroyed != 1 || gcTotalDetected != 1 ) fail = true; // Test circular references including a script class and the dictionary mod->AddScriptSection("script", script2, strlen(script2)); r = mod->Build(); if( r < 0 ) fail = true; r = engine->ExecuteString(0, "f()"); if( r != asEXECUTION_FINISHED ) fail = true; engine->GetGCStatistics(&gcCurrentSize, &gcTotalDestroyed, &gcTotalDetected); engine->GarbageCollect(); engine->GetGCStatistics(&gcCurrentSize, &gcTotalDestroyed, &gcTotalDetected); if( gcCurrentSize != 0 || gcTotalDestroyed != 3 || gcTotalDetected != 3 ) fail = true; // Test invalid ref cast together with the variable argument bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); r = engine->ExecuteString(0, "dictionary d; d.set('hello', cast<int>(4));"); if( r >= 0 ) fail = true; if( bout.buffer != "ExecuteString (1, 35) : Error : Illegal target type for reference cast\n" ) { fail = true; printf(bout.buffer.c_str()); } engine->Release(); //------------------------- // Test the generic interface as well engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); RegisterScriptDictionary_Generic(engine); r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script, strlen(script)); r = mod->Build(); if( r < 0 ) fail = true; r = engine->ExecuteString(0, "Test()", &ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); fail = true; } ctx->Release(); 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; COutStream out; asIScriptEngine *engine; asIScriptModule *mod; asIScriptContext *ctx; CBufferedOutStream bout; const char *script; // 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 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; 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" // TODO: Should warn about expression that doesn't do anything " 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 (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_ != null && @this._events_.f != 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; COutStream out; asIScriptEngine *engine = 0; asIScriptModule *mod = 0; int r; fail = Test2() || fail; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); RegisterScriptStringUtils(engine); engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(PrintString), asCALL_GENERIC); engine->RegisterGlobalFunction("void set(string@)", asFUNCTION(SetString), asCALL_GENERIC); engine->RegisterGlobalFunction("void set2(string@&in)", asFUNCTION(SetString2), asCALL_GENERIC); engine->RegisterGlobalFunction("const string &getconststringref()", asFUNCTION(GetConstStringRef), asCALL_GENERIC); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); // Test index operator for temp strings r = engine->ExecuteString(0, "assert('abc'[0] == 97)"); if( r != asEXECUTION_FINISHED ) fail = true; r = engine->ExecuteString(0, "assert(string('abc')[0] == 97)"); if( r != asEXECUTION_FINISHED ) fail = true; r = engine->ExecuteString(0, "string a = 'abc'; assert(a[0] == 97)"); if( r != asEXECUTION_FINISHED ) fail = true; // Test string copy constructor r = engine->ExecuteString(0, "string tst(getconststringref()); print(tst);"); if( r != asEXECUTION_FINISHED ) fail = true; if( printOutput != "test" ) fail = true; printOutput = ""; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script2, strlen(script2), 0); mod->Build(); engine->ExecuteString(0, "testString()"); if( printOutput != "hello Ida" ) { fail = true; printf("%s: Failed to print the correct string\n", TESTNAME); } engine->ExecuteString(0, "string s = \"test\\\\test\\\\\""); // Verify that it is possible to use the string in constructor parameters printOutput = ""; engine->ExecuteString(0, "string a; a = 1; print(a);"); if( printOutput != "1" ) fail = true; printOutput = ""; engine->ExecuteString(0, "string a; a += 1; print(a);"); if( printOutput != "1" ) fail = true; printOutput = ""; engine->ExecuteString(0, "string a = \"a\" + 1; print(a);"); if( printOutput != "a1" ) fail = true; printOutput = ""; engine->ExecuteString(0, "string a = 1 + \"a\"; print(a);"); if( printOutput != "1a" ) fail = true; printOutput = ""; engine->ExecuteString(0, "string a = 1; print(a);"); if( printOutput != "1" ) fail = true; printOutput = ""; engine->ExecuteString(0, "print(\"a\" + 1.2)"); if( printOutput != "a1.2") fail = true; printOutput = ""; engine->ExecuteString(0, "print(1.2 + \"a\")"); if( printOutput != "1.2a") fail = true; // Passing a handle to a function printOutput = ""; engine->ExecuteString(0, "string a; set(@a); print(a);"); if( printOutput != "Handle to a string" ) fail = true; // Implicit conversion to handle printOutput = ""; engine->ExecuteString(0, "string a; set(a); print(a);"); if( printOutput != "Handle to a string" ) fail = true; // Passing a reference to a handle to the function printOutput = ""; engine->ExecuteString(0, "string a; set2(@a); print(a);"); if( printOutput != "Handle to a string" ) fail = true; // Implicit conversion to reference to a handle printOutput = ""; engine->ExecuteString(0, "string a; set2(a); print(a);"); if( printOutput != "Handle to a string" ) fail = true; printOutput = ""; engine->ExecuteString(0, "string a = \" \"; a[0] = 65; print(a);"); if( printOutput != "A" ) fail = true; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script3, strlen(script3), 0); if( mod->Build() < 0 ) fail = true; printOutput = ""; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script4, strlen(script4), 0); if( mod->Build() < 0 ) fail = true; engine->ExecuteString(0, "test()"); if( printOutput != "Heredoc\\x20test!" ) fail = true; CScriptString *a = new CScriptString("a"); engine->RegisterGlobalProperty("string a", a); r = engine->ExecuteString(0, "print(a == 'a' ? 't' : 'f')"); if( r != asEXECUTION_FINISHED ) { fail = true; printf("%s: ExecuteString() failed\n", TESTNAME); } a->Release(); // test new mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script5, strlen(script5), 0); if( mod->Build() < 0 ) fail = true; r = engine->ExecuteString(0, "Main()"); if( r != asEXECUTION_FINISHED ) fail = true; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script6, strlen(script6), 0); if( mod->Build() < 0 ) fail = true; r = engine->ExecuteString(0, "Main()"); if( r != asEXECUTION_FINISHED ) fail = true; // Test character literals r = engine->SetEngineProperty(asEP_USE_CHARACTER_LITERALS, true); assert( r >= 0 ); printOutput = ""; r = engine->ExecuteString(0, "print(\"\" + 'a')"); if( r != asEXECUTION_FINISHED ) fail = true; if( printOutput != "97" ) fail = true; printOutput = ""; r = engine->ExecuteString(0, "print(\"\" + '\\'')"); if( r != asEXECUTION_FINISHED ) fail = true; if( printOutput != "39" ) fail = true; printOutput = ""; r = engine->ExecuteString(0, "print(\"\" + '\xFF')"); if( r != asEXECUTION_FINISHED ) fail = true; if( printOutput != "255" ) fail = true; CBufferedOutStream bout; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = engine->ExecuteString(0, "print(\"\" + '')"); if( r != -1 ) fail = true; r = engine->SetEngineProperty(asEP_USE_CHARACTER_LITERALS, false); assert( r >= 0 ); //------------------------------------- engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script7, strlen(script7), 0); mod->Build(); r = engine->ExecuteString(0, "test()"); if( r != asEXECUTION_FINISHED ) fail = true; engine->RegisterObjectType("Http", sizeof(int), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE); engine->RegisterObjectMethod("Http","bool get(const string &in,string &out)", asFUNCTION(Get),asCALL_GENERIC); r = engine->ExecuteString(0, "Http h; string str; h.get('stringtest', str); assert(str == 'output');"); if( r != asEXECUTION_FINISHED ) fail = true; r = engine->ExecuteString(0, "Http h; string a = 'test', b; h.get('string'+a, b); assert(b == 'output');"); if( r != asEXECUTION_FINISHED ) fail = true; // Test the string utils engine->ExecuteString(0, "string str = 'abcdef'; assert(findFirst(str, 'def') == 3);"); engine->ExecuteString(0, "string str = 'abcdef'; assert(findFirstOf(str, 'feb') == 1);"); engine->ExecuteString(0, "string str = 'a|b||d'; string@[]@ array = split(str, '|'); assert(array.length() == 4); assert(array[1] == 'b');"); engine->ExecuteString(0, "string@[] array = {'a', 'b', '', 'd'}; assert(join(array, '|') == 'a|b||d');"); engine->Release(); //--------------------------------------- engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); engine->RegisterGlobalFunction("void TestFunc(int, string&)", asFUNCTION(TestFunc), asCALL_GENERIC); // CHKREF was placed incorrectly r = engine->ExecuteString(0, "TestFunc(0, 'test');"); if( r != asEXECUTION_FINISHED ) fail = true; r = engine->ExecuteString(0, "string @s; TestFunc(0, s);"); if( r != asEXECUTION_EXCEPTION ) fail = true; engine->Release(); //---------------------------------------- engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script7, strlen(script7), 0); mod->Build(); r = engine->ExecuteString(0, "test()"); if( r != asEXECUTION_FINISHED ) fail = true; engine->Release(); //------------------------------------------ // Test the comparison method { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); std::string a = "a"; std::string b = "b"; int type = engine->GetTypeIdByDecl("string"); bool c; r = engine->CompareScriptObjects(c, asBEHAVE_EQUAL, &a, &b, type); assert( r >= 0 ); if( c ) fail = true; r = engine->CompareScriptObjects(c, asBEHAVE_NOTEQUAL, &a, &b, type); assert( r >= 0 ); if( !c ) fail = true; r = engine->CompareScriptObjects(c, asBEHAVE_LESSTHAN, &a, &b, type); assert( r >= 0 ); if( !c ) fail = true; r = engine->CompareScriptObjects(c, asBEHAVE_GREATERTHAN, &a, &b, type); assert( r >= 0 ); if( c ) fail = true; r = engine->CompareScriptObjects(c, asBEHAVE_LEQUAL, &a, &b, type); assert( r >= 0 ); if( !c ) fail = true; r = engine->CompareScriptObjects(c, asBEHAVE_GEQUAL, &a, &b, type); assert( r >= 0 ); if( c ) fail = true; engine->Release(); } //----- { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); engine->RegisterGlobalFunction("void Print(string &str)",asFUNCTION(PrintRef), asCALL_GENERIC); const char *script = "string str = 'Some String'; \n" "void Update() \n" "{ \n" " Print(str); \n" "} \n"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script, strlen(script)); mod->Build(); CScriptString *str = (CScriptString*)mod->GetAddressOfGlobalVar(0); UNUSED_VAR(str); r = engine->ExecuteString(0, "Update()"); if( r != asEXECUTION_FINISHED ) fail = true; engine->Release(); } //------- // Multiline strings { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); engine->SetEngineProperty(asEP_ALLOW_MULTILINE_STRINGS, true); engine->RegisterGlobalFunction("void assert(bool)",asFUNCTION(Assert), asCALL_GENERIC); const char *script = "string str1 = '1\\n' '2'; \n" "string str2 = '1\n2'; \n" "assert(str1 == str2); \n"; r = engine->ExecuteString(0, script); if( r != asEXECUTION_FINISHED ) fail = true; engine->Release(); } return fail; }
bool Test() { bool fail = false; int r; CBufferedOutStream bout; COutStream out; asIScriptModule *mod; asIScriptEngine *engine; // Test calling a function with default argument { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "void func(int b, const string &in a = 'default') \n" "{ \n" " if( b == 0 ) \n" " assert( a == 'default' ); \n" " else \n" " assert( a == 'test' ); \n" "} \n" "void main() \n" "{ \n" " func(0); \n" " func(0, 'default'); \n" " func(1, 'test'); \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(); } // Must be possible to register functions with default args as well { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = engine->RegisterGlobalFunction("void defarg(bool, int a = 34 + /* comments will be removed */ 45, int b = 23)", asFUNCTION(0), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; asIScriptFunction *func = engine->GetFunctionDescriptorById(r); string decl = func->GetDeclaration(); if( decl != "void defarg(bool, int arg1 = 34 + 45, int arg2 = 23)" ) { printf("%s\n", decl.c_str()); TEST_FAILED; } engine->Release(); } // When default arg is used, all other args after that must have default args { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = engine->RegisterGlobalFunction("void defarg(bool, int a = 34+45, int)", asFUNCTION(0), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "System function (1, 1) : Error : All subsequent parameters after the first default value must have default values in function 'void defarg(bool, int arg1 = 34 + 45, int)'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Shouldn't be possible to write default arg expressions that access local variables, globals are ok though { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "void func(int a = n) {} \n" "void main() \n" "{ \n" " int n; \n" " func(); \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; // TODO: The first line in the error message should show the real script name if( bout.buffer != "default arg (2, 1) : Info : Compiling void main()\n" "default arg (1, 1) : Error : 'n' is not declared\n" "script (5, 3) : Error : Failed while compiling default arg for parameter 0 in function 'void func(int arg0 = n)'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Default args in script class constructors { 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); const char *script = "class T \n" "{ \n" " T(int a, int b = 25) \n" " { \n" " assert(a == 10); \n" " assert(b == 25); \n" " } \n" "} \n" "T g(10); \n" "void main() \n" "{ \n" " T(10); \n" " T l(10); \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(); } // Default arg must not end up using variables that are used // in previously compiled variables as temporaries { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); RegisterStdString(engine); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "void func(uint8 a, string b = 'b') \n" "{ \n" " assert( a == 97 ); \n" " assert( b == 'b' ); \n" "} \n" "void main() \n" "{ \n" " uint8 a; \n" " func(a = 'a'[0]); \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(); } // Shouldn't crash if attempting to call incorrect function { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "void myFunc( float f, int a=0, int b ) {} \n" "void main() \n" "{ \n" " int n; \n" " myFunc( 1.2, 6 ); \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Error : All subsequent parameters after the first default value must have default values in function 'void myFunc(float, int arg1 = 0, int)'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // The test to make sure the saved bytecode keeps the default args is done in test_saveload.cpp // A test to make sure script class methods with default args work is done in test_saveload.cpp // TODO: The compilation of the default args must not add any LINE instructions in the byte code, because they wouldn't match the real script // Success 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 = 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); // TEST 1 engine->RegisterGlobalFunction("void CFunc(float, bool, bool, const string &in)", asFUNCTION(CFunc), asCALL_CDECL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("decl", declarations, strlen(declarations)); mod->AddScriptSection("script", script, strlen(script)); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "MyTest()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // TEST 2 mod->AddScriptSection("script", script2, strlen(script2)); r = mod->Build(); if( r < 0 ) TEST_FAILED; int idx = engine->GetModule(0)->GetGlobalVarIndexByName("gFlag"); bool *flag = (bool*)engine->GetModule(0)->GetAddressOfGlobalVar(idx); *(int*)flag = 0xCDCDCDCD; ExecuteString(engine, "Set()", mod); if( *flag != true ) TEST_FAILED; ExecuteString(engine, "Assert(gFlag == true)", mod); ExecuteString(engine, "gFlag = false; DoNothing()", mod); if( *flag != false ) fail = false; ExecuteString(engine, "Assert(gFlag == false)", mod); ExecuteString(engine, "gFlag = true; DoNothing()", mod); if( *flag != true ) fail = false; ExecuteString(engine, "Assert(gFlag == true)", mod); // TEST 3 // It was reported that if( t.test_f() ) would always be true, even though the method returns false // The bug was that the function didn't return 0 in the upper bytes, thus the 32bit value was not 0, even though the low byte was engine->RegisterObjectType("tst", sizeof(tst), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS); engine->RegisterObjectMethod("tst", "bool test_f(uint)", asMETHOD(tst, test_f), asCALL_THISCALL); r = ExecuteString(engine, "tst t; if( t.test_f(2000) == true ) Assert(false);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "tst t; if( !(t.test_f(2000) == false) ) Assert(false);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, 0); r = ExecuteString(engine, "tst t; if( t.test_f(2000) ) Assert(false);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->RegisterGlobalFunction("bool test_t()", asFUNCTION(test_t), asCALL_CDECL); r = ExecuteString(engine, "Assert( test_t() );"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // TEST 4 // Return a false value as out parameter. The value must be properly interpreted, even with trash in upper bytes engine->RegisterGlobalFunction("void GiveFalse(bool &out)", asFUNCTION(GiveFalse), asCALL_CDECL); r = ExecuteString(engine, "bool f; GiveFalse(f); Assert( !f );"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "bool f; GiveFalse(f); if( f ) Assert(false);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "bool f, f2 = false; GiveFalse(f); Assert( !(f || f2) );"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // TEST 5 // The same test with global variable int falseValue = 0; if( sizeof(bool) == 1 ) falseValue = 0x00FFFF00; engine->RegisterGlobalProperty("bool falseValue", &falseValue); r = ExecuteString(engine, "Assert( !falseValue );"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "if( falseValue ) Assert(false);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "bool f2 = false; Assert( !(falseValue || f2) );"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // TEST 6 // Test to make sure bools can be passed to member functions properly engine->RegisterObjectType("BoolTester", sizeof(TestBoolClass), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS); engine->RegisterObjectMethod("BoolTester", "void TestTrue(bool)", asMETHOD(TestBoolClass, TestTrue), asCALL_THISCALL); engine->RegisterObjectMethod("BoolTester", "void TestFalse(bool)", asMETHOD(TestBoolClass, TestFalse), asCALL_THISCALL); TestBoolClass testBool; r = engine->RegisterGlobalProperty("BoolTester TestBoolClass", &testBool ); if( r < 0 ) TEST_FAILED; mod->AddScriptSection("script", script3, strlen(script3)); r = mod->Build(); if( r < 0 ) { TEST_FAILED; } else { r = ExecuteString(engine, "TestBoolToMember();", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( testBool.m_fail ) TEST_FAILED; } // TEST 7 engine->RegisterGlobalFunction("void Print(const string &in)", asFUNCTION(Print), asCALL_CDECL); assert( r >= 0 ); mod->AddScriptSection("script", script4, strlen(script4)); r = mod->Build(); if( r < 0 ) { TEST_FAILED; } else { r = ExecuteString(engine, "test();", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buf != "false\ntrue\nfalse\n" ) TEST_FAILED; } // The tokenizer must not mistake '!isTrue' for '!is' + 'True' instead of '!' + 'isTrue' const char *script5 = "class CTest \n" "{ \n" " bool isTrue() { return true; } \n" " void func() { if( !isTrue() ) {} } \n" "} \n"; mod->AddScriptSection("script", script5, strlen(script5)); r = mod->Build(); if( r < 0 ) 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 /* // This warning isn't done anymore as there are times when it is valid to call a non-const method on temporary objects, for example if a stream like object is implemented bout.buffer = ""; 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 != "" ) { 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; // Test accessing property of the same name on a member object const char *script21 = "class Test { \n" " int a; \n" " Test @member; \n" " int get_a() const { return a; } \n" " void set_a(int val) {a = val; if( member !is null ) member.a = val;} \n" "} \n"; mod->AddScriptSection("script", script21); bout.buffer = ""; r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test t, s, u; @t.member = s; @s.member = u; t.set_a(3); assert( u.a == 3 );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); 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(); } // Test get property returning reference { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); engine->RegisterObjectType("LevelType", sizeof(CLevel), asOBJ_VALUE | asOBJ_POD); engine->RegisterObjectProperty("LevelType", "float attr", offsetof(CLevel, attr)); engine->RegisterGlobalFunction("LevelType &get_Level()", asFUNCTION(get_Level), asCALL_CDECL); r = ExecuteString(engine, "Level.attr = 0.5f;"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( g_level.attr != 0.5f ) TEST_FAILED; engine->Release(); } // Make sure it is possible to update properties of objects returned through getter { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); engine->RegisterObjectType("node", 0, asOBJ_REF); engine->RegisterObjectBehaviour("node", asBEHAVE_FACTORY, "node @f()", asFUNCTION(CNode::CNodeFactory), asCALL_CDECL); engine->RegisterObjectBehaviour("node", asBEHAVE_ADDREF, "void f()", asMETHOD(CNode, AddRef), asCALL_THISCALL); engine->RegisterObjectBehaviour("node", asBEHAVE_RELEASE, "void f()", asMETHOD(CNode, Release), asCALL_THISCALL); engine->RegisterObjectMethod("node", "node @+ get_child()", asMETHOD(CNode, GetChild), asCALL_THISCALL); engine->RegisterObjectMethod("node", "void set_child(node @+)", asMETHOD(CNode, SetChild), asCALL_THISCALL); engine->RegisterObjectProperty("node", "vector3 vector", offsetof(CNode, vector)); engine->RegisterObjectProperty("node", "float x", offsetof(CNode, vector)); r = ExecuteString(engine, "node @a = node(); \n" "@a.child = node(); \n" "a.child.x = 0; \n" "a.child.vector = vector3(0,0,0); \n"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } fail = Test2() || fail; // Success return fail; }
bool Test() { bool fail = false; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); int r; r = engine->RegisterObjectType("ObjectInstance", sizeof(ObjectInstance), asOBJ_VALUE | asOBJ_APP_CLASS); assert(r>=0); r = engine->RegisterObjectProperty("ObjectInstance", "int val", offsetof(ObjectInstance, val)); assert(r>=0); r = engine->RegisterObjectProperty("ObjectInstance", "int val2", offsetof(ObjectInstance, val)); assert(r>=0); r = engine->RegisterObjectProperty("ObjectInstance", "int val3", offsetof(ObjectInstance, val)); assert(r>=0); r = engine->RegisterObjectMethod("ObjectInstance", "void function()", asFUNCTION(ObjectFunction), asCALL_CDECL_OBJFIRST); assert(r>=0); r = engine->RegisterObjectMethod("ObjectInstance", "void Method()", asMETHOD(ObjectInstance,Method), asCALL_THISCALL); assert(r>=0); r = engine->RegisterObjectType("ObjectType", sizeof(ObjectType), asOBJ_VALUE | asOBJ_APP_CLASS); assert(r>=0); r = engine->RegisterGlobalFunction("ObjectType *CreateObjectType(string &in)", asFUNCTION(CreateObjectType), asCALL_CDECL); assert(r>=0); r = engine->RegisterGlobalFunction("ObjectInstance *CreateObjectInstance(ObjectType *type)", asFUNCTION(CreateObjectInstance), asCALL_CDECL); assert(r>=0); r = engine->RegisterGlobalFunction("void FunctionOnObject(ObjectInstance *)", asFUNCTION(FunctionOnObject), asCALL_CDECL); assert(r>=0); // Register an object. ObjectInstance obj; r = engine->RegisterGlobalProperty("ObjectInstance obj", &obj); assert(r>=0); // Register a pointer to object. ObjectInstance *pnt = &obj; r = engine->RegisterGlobalProperty("ObjectInstance *ptr", &pnt); assert(r>=0); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); // Function call executed fine when using an object. r = ExecuteString(engine, "obj.function(); obj.val = 23;"); if( r < 0 ) { printf("%s: ExecuteString() failed %d\n", TESTNAME, r); fail = true; } if( obj.val != 23 ) { printf("%s: failed\n", TESTNAME); fail = true; } r = ExecuteString(engine, "ptr->function(); ptr->val = 13;"); if( r < 0 ) { printf("%s: ExecuteString() failed %d\n", TESTNAME, r); fail = true; } if( obj.val != 13 ) { printf("%s: failed\n", TESTNAME); fail = true; } asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script, strlen(script)); r = mod->Build(); if( r < 0 ) { printf("%s: failed\n", TESTNAME); fail = true; } r = ExecuteString(engine, "Test()", mod); if( r < 0 ) { printf("%s: failed\n", TESTNAME); fail = true; } engine->Release(); return fail; }
bool Test() { bool fail = false; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); any = (asIScriptAny*)engine->CreateScriptObject(engine->GetTypeIdByDecl(0, "any")); engine->RegisterGlobalProperty("any g_any", any); COutStream out; engine->AddScriptSection(0, TESTNAME, script1, strlen(script1), 0, false); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = engine->Build(0); if( r < 0 ) fail = true; // Try retrieving the type Id for the structure int typeId = engine->GetTypeIdByDecl(0, "MyStruct"); if( typeId < 0 ) { printf("%s: Failed to retrieve the type id for the script struct\n", TESTNAME); fail = true; } r = engine->ExecuteString(0, "Test()"); if( r != asEXECUTION_FINISHED ) fail = true; else { asIScriptStruct *s = 0; typeId = any->GetTypeId(); any->Retrieve(&s, typeId); if( (typeId & asTYPEID_MASK_OBJECT) != asTYPEID_SCRIPTSTRUCT ) fail = true; if( strcmp(engine->GetTypeDeclaration(typeId), "MyStruct@") ) fail = true; typeId = s->GetStructTypeId(); if( strcmp(engine->GetTypeDeclaration(typeId), "MyStruct") ) fail = true; if( s->GetPropertyCount() != 3 ) fail = true; if( strcmp(s->GetPropertyName(0), "a") ) fail = true; if( s->GetPropertyTypeId(0) != engine->GetTypeIdByDecl(0, "float") ) fail = true; if( *(float*)s->GetPropertyPointer(0) != 3.141592f ) fail = true; if( strcmp(s->GetPropertyName(1), "b") ) fail = true; if( s->GetPropertyTypeId(1) != engine->GetTypeIdByDecl(0, "string") ) fail = true; if( ((asCScriptString*)s->GetPropertyPointer(1))->buffer != "test" ) fail = true; if( strcmp(s->GetPropertyName(2), "c") ) fail = true; if( s->GetPropertyTypeId(2) != engine->GetTypeIdByDecl(0, "string@") ) fail = true; if( (*(asCScriptString**)s->GetPropertyPointer(2))->buffer != "test2" ) fail = true; if( s ) s->Release(); } if( any ) any->Release(); // The type id is valid for as long as the type exists if( strcmp(engine->GetTypeDeclaration(typeId), "MyStruct") ) fail = true; // Make sure the type is not used anywhere engine->Discard(0); engine->GarbageCollect(); // The type id is no longer valid if( engine->GetTypeDeclaration(typeId) != 0 ) fail = true; engine->Release(); // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; asIScriptEngine *engine; asIScriptModule *mod; asIScriptContext *ctx; { CMyDebugger debug; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); 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" "} \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" "script:0; type@ type()\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 = \"c\"\n" "const string& d = \"d\"\n" "type@ e = {XXXXXXXX}\n" "type& f = {XXXXXXXX}\n" "type@& g = {XXXXXXXX}\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 = \"c\"\n" "const string& d = \"d\"\n" "type@ e = {XXXXXXXX}\n" "type& f = {XXXXXXXX}\n" "type@& g = {XXXXXXXX}\n" "script:3; 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" "void Func() \n" "{ \n" " CTest t; \n" " assert( t.value == 42 ); \n" "} \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:8", ctx); // Set a break point after the object has been created debug.InterpretCommand("b test:9", 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() != 8 ) 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() != 9 ) 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); } if( debug.output != "Setting break point in file 'test' at line 8\n" "Setting break point in file 'test' at line 9\n" "Reached break point 0 in file 'test' at line 8\n" "test:8; void Func()\n" "Reached break point 0 in file 'test' at line 8\n" "test:8; void Func()\n" "Reached break point 1 in file 'test' at line 9\n" "test:9; void Func()\n" "{XXXXXXXX}\n" " int value = 42\n" ) { printf("%s", debug.output.c_str()); TEST_FAILED; } ctx->Release(); engine->Release(); } return fail; }
bool Test() { RET_ON_MAX_PORT bool fail = false; int r; COutStream out; CBufferedOutStream bout; asIScriptEngine *engine = 0; asIScriptModule *mod = 0; asIScriptContext *ctx = 0; // Test behaviour of var type with unsafe references { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, true); static bool expectNullRef; static bool expectHandleType; struct Test { static void Func(void *ref, int typeId) { assert( (expectNullRef && ref == 0) || (!expectNullRef && ref != 0) ); assert( (expectHandleType && (typeId & asTYPEID_OBJHANDLE)) || (!expectHandleType && (typeId & asTYPEID_OBJHANDLE) == 0) ); } }; engine->RegisterGlobalFunction("void funcO(?& out)", asFUNCTION(Test::Func), asCALL_CDECL); engine->RegisterGlobalFunction("void funcIO(?&)", asFUNCTION(Test::Func), asCALL_CDECL); const char *script = "class ScriptClass { int a = 0; } \n"; mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); expectNullRef = false; expectHandleType = false; r = ExecuteString(engine, "ScriptClass @t = ScriptClass(); funcO(t);", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PRINTF("%s", GetExceptionInfo(ctx).c_str()); expectNullRef = true; expectHandleType = false; r = ExecuteString(engine, "ScriptClass @t; funcIO(t);", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PRINTF("%s", GetExceptionInfo(ctx).c_str()); expectNullRef = false; expectHandleType = true; r = ExecuteString(engine, "ScriptClass @t; funcO(@t);", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PRINTF("%s", GetExceptionInfo(ctx).c_str()); expectNullRef = false; expectHandleType = true; r = ExecuteString(engine, "ScriptClass @t; funcIO(@t);", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PRINTF("%s", GetExceptionInfo(ctx).c_str()); // When disallowing value assignments, only the handle is passed to var args engine->SetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE, true); expectNullRef = false; expectHandleType = true; r = ExecuteString(engine, "ScriptClass @t; funcO(t); funcO(@t); funcIO(t); funcIO(@t);", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PRINTF("%s", GetExceptionInfo(ctx).c_str()); ctx->Release(); engine->Release(); } // It must not be possible to declare global variables of the var type ? engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script1 = "? globvar;"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script1); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Error : Unexpected token '?'\n" ) TEST_FAILED; bout.buffer = ""; // It must not be possible to declare local variables of the var type ? const char *script2 = "void func() {? localvar;}"; mod->AddScriptSection("script", script2); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Info : Compiling void func()\n" "script (1, 14) : Error : Expected expression value\n" "script (1, 14) : Error : Instead found '?'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to register global properties of the var type ? r = engine->RegisterGlobalProperty("? prop", (void*)1); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "Property (1, 1) : Error : Expected data type\n" "Property (1, 1) : Error : Instead found '?'\n" " (0, 0) : Error : Failed in call to function 'RegisterGlobalProperty' with '? prop' (Code: -10)\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; engine->Release(); // It must not be possible to register object members of the var type ? engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = engine->RegisterObjectType("test", 0, asOBJ_REF); assert( r >= 0 ); r = engine->RegisterObjectProperty("test", "? prop", 0); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "Property (1, 1) : Error : Expected data type\n" "Property (1, 1) : Error : Instead found '?'\n" " (0, 0) : Error : Failed in call to function 'RegisterObjectProperty' with 'test' and '? prop' (Code: -10)\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; engine->Release(); // It must not be possible to declare script class members of the var type ? engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script3 = "class c {? member;}"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script3); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 10) : Error : Expected method or property\n" "script (1, 10) : Error : Instead found '?'\n" "script (1, 19) : Error : Unexpected token '}'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to declare script functions that take the var type ? as parameter const char *script4 = "void func(?&in a) {}"; mod->AddScriptSection("script", script4); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 11) : Error : Expected data type\n" "script (1, 11) : Error : Instead found '?'\n") { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to declare script functions that return the var type ? const char *script5 = "? func() {}"; mod->AddScriptSection("script", script5); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Error : Unexpected token '?'\n" ) TEST_FAILED; bout.buffer = ""; // It must not be possible to declare script class methods that take the var type ? as parameter const char *script6 = "class c {void method(?& in a) {}}"; mod->AddScriptSection("script", script6); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 22) : Error : Expected data type\n" "script (1, 22) : Error : Instead found '?'\n" "script (1, 33) : Error : Unexpected token '}'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to declare script class methods that return the var type ? const char *script7 = "class c {? method() {}}"; mod->AddScriptSection("script", script7); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 10) : Error : Expected method or property\n" "script (1, 10) : Error : Instead found '?'\n" "script (1, 23) : Error : Unexpected token '}'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to declare arrays of the var type ? const char *script8 = "void func() { ?[] array; }"; mod->AddScriptSection("script", script8); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Info : Compiling void func()\n" "script (1, 15) : Error : Expected expression value\n" "script (1, 15) : Error : Instead found '?'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to declare handles of the var type ? const char *script9 = "void func() { ?@ handle; }"; mod->AddScriptSection("script", script9); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (1, 1) : Info : Compiling void func()\n" "script (1, 15) : Error : Expected expression value\n" "script (1, 15) : Error : Instead found '?'\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; // It must not be possible to register functions that return the var type ? r = engine->RegisterGlobalFunction("? testFunc()", asFUNCTION(testFuncI), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "System function (1, 1) : Error : Expected data type\n" "System function (1, 1) : Error : Instead found '?'\n" " (0, 0) : Error : Failed in call to function 'RegisterGlobalFunction' with '? testFunc()' (Code: -10)\n" ) { PRINTF("%s", bout.buffer.c_str()); TEST_FAILED; } bout.buffer = ""; engine->Release(); // It must be possible to register functions that take the var type ? as parameter // Only when the expression is explicitly sent as @ should the type id be @ // const ? & in // ? & in // TODO: 2.29.0: Should have syntax to inform that only handle or only non-handle can be informed // Maybe 'const ? @ & in' for only handle // '? @- & in' for only non-handle // '? @+ & in' for only handle with auto-handle engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void testFuncI(?& in)", asFUNCTION(testFuncI), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void testFuncCI(const?&in)", asFUNCTION(testFuncI), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void testFuncS(string &in)", asFUNCTION(testFuncS), asCALL_GENERIC); r = ExecuteString(engine, "int a = 42; testFuncI(a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"test\"; testFuncI(a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string @a = @\"test\"; testFuncI(@a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "testFuncI(null);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Both functions should receive the string by reference r = ExecuteString(engine, "string a = 'test'; testFuncI(a); testFuncS(a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to register with 'out' references // ? & out // TODO: 2.29.0: Should have syntax to inform that only handle or only non-handle can be informed // Maybe '? @ & out' for only handle // '? @- & out' for non-handle // '? @+ & out' for auto handle r = engine->RegisterGlobalFunction("void testFuncO(?&out)", asFUNCTION(testFuncO), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "testFuncO(0)"); // skip out value if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "testFuncO(void)"); // skip out value if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "testFuncO(null)"); // skip out value if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "int a; testFuncO(a); assert(a == 42);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a; testFuncO(a); assert(a == \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string @a; testFuncO(@a); assert(a == \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to mix normal parameter types with the var type ? // e.g. func(const string &in, const ?& in), or func(const ?& in, const string &in) r = engine->RegisterGlobalFunction("void testFuncIS(?& in, const string &in)", asFUNCTION(testFuncIS_generic), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void testFuncSI(const string &in, ?& in)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "int a = 42; testFuncIS(a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "int a = 42; testFuncSI(\"test\", a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"t\"; testFuncIS(a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"t\"; testFuncIS(@a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to use native functions SKIP_ON_MAX_PORT { r = engine->RegisterGlobalFunction("void _testFuncIS(?& in, const string &in)", asFUNCTION(testFuncIS), asCALL_CDECL); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void _testFuncSI(const string &in, ?& in)", asFUNCTION(testFuncSI), asCALL_CDECL); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "int a = 42; _testFuncIS(a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "int a = 42; _testFuncSI(\"test\", a);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"t\"; _testFuncIS(a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "string a = \"t\"; _testFuncIS(@a, \"test\");"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; } // Don't give error on passing reference to const to ?&out engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script = "class C { string @a; } \n"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); mod->Build(); r = ExecuteString(engine, "const C c; testFuncO(@c.a);", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 23) : Warning : Argument cannot be assigned. Output will be discarded.\n" ) TEST_FAILED; bout.buffer = ""; // ?& with opAssign is allowed, but won't be used with the assignment operator // TODO: Support ?& with the operators as well engine->RegisterObjectType("type", sizeof(int), asOBJ_VALUE | asOBJ_APP_PRIMITIVE); r = engine->RegisterObjectMethod("type", "type &opAssign(const ?& in)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r < 0 ) TEST_FAILED; // TODO: This is a valid class method, but should perhaps not be allowed to be used as operator /* r = engine->RegisterObjectMethod("type", "type opAdd(const ?& in)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; */ // Don't allow use of ? without being reference r = engine->RegisterGlobalFunction("void testFunc_err(const ?)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; // Don't allow use of 'inout' reference, yet // ? & [inout] // const ? & [inout] r = engine->RegisterGlobalFunction("void testFuncIO(?&)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void testFuncCIO(const?&)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); if( r >= 0 ) TEST_FAILED; engine->Release(); { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); r = engine->RegisterObjectType("obj", sizeof(int), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE); assert( r >= 0 ); r = engine->RegisterObjectMethod("obj", "string @fmt(const string &in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in, ?&in)", asFUNCTION(testFuncSI_generic), asCALL_GENERIC); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule("1", asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "class App {\n" " int Run() {\n" " return 0;\n" " }\n" "}\n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; 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)->GetFunctionIdByDecl("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 == 10 ); // The script class types and functions are also in the gc 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; }
bool Test() { bool fail = false; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_GENERIC); engine->RegisterGlobalFunction("void Analyze(any &inout)", asFUNCTION(Analyze), asCALL_GENERIC); COutStream out; CBufferedOutStream bout; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "Test()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; } if( ctx ) ctx->Release(); // Make sure that the error message for wrong constructor name works bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class t{ s() {} };", 18, 0); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestScriptClassMethod (1, 10) : Error : The name of constructors and destructors must be the same as the class\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Make sure the default constructor can be overloaded engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script2, strlen(script2), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } asIObjectType *type = engine->GetModule("test")->GetObjectTypeByName("myclass"); asIScriptObject *s = (asIScriptObject*)engine->CreateScriptObject(type); if( s == 0 ) TEST_FAILED; else { // Validate the property int *v = 0; int n = s->GetPropertyCount(); for( int c = 0; c < n; c++ ) { std::string str = "value"; if( str == s->GetPropertyName(c) ) { v = (int*)s->GetAddressOfProperty(c); if( *v != 1 ) TEST_FAILED; } } // Call the script class method if( type->GetMethodCount() != 2 ) TEST_FAILED; asIScriptFunction *method = type->GetMethodByDecl("void method2()"); if( method == 0 ) TEST_FAILED; else { asIScriptContext *ctx = engine->CreateContext(); ctx->Prepare(method); ctx->SetObject(s); int r = ctx->Execute(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( (!v) || (*v != 3) ) TEST_FAILED; ctx->Release(); } s->Release(); } engine->Release(); //---------------------------------- engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptAny(engine); 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("test3", script3, strlen(script3), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; int typeId = engine->GetModule(0)->GetTypeIdByDecl("myclass"); type = engine->GetObjectTypeById(typeId); asIScriptFunction *mtd = type->GetMethodByDecl("void func()"); asIScriptObject *obj = (asIScriptObject *)engine->GetModule(0)->GetAddressOfGlobalVar(engine->GetModule(0)->GetGlobalVarIndexByName("c")); if( mtd == 0 || obj == 0 ) TEST_FAILED; else { asIScriptContext *ctx = engine->CreateContext(); ctx->Prepare(mtd); ctx->SetObject(obj); r = ctx->Execute(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; ctx->Release(); } type = engine->GetObjectTypeById(typeId); mtd = type->GetMethodByDecl("void func(int, int)"); if( mtd == 0 || obj == 0 ) TEST_FAILED; else { asIScriptContext *ctx = engine->CreateContext(); ctx->Prepare(mtd); ctx->SetObject(obj); ctx->SetArgDWord(0, 1); ctx->SetArgDWord(1, 1); r = ctx->Execute(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; ctx->Release(); } engine->Release(); //---------------------------- // Verify that global functions and class methods with the same name doesn't conflict engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptAny(engine); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("test4", script4, strlen(script4), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; asIScriptFunction *func = mod->GetFunctionByDecl("void func()"); if( func == 0 ) TEST_FAILED; engine->Release(); //---------------------------- // Accessing member variables without this engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptAny(engine); 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("test5", 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(); //----------------------------- // Name conflict with class method and object type engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptString(engine); engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("test6", script6, strlen(script6), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; outbuffer = ""; r = ExecuteString(engine, "Test t; t.Set(1); t.Test2();", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } if( outbuffer != "Test::Set\nTest::Set\nSet::Set\n" ) { printf("%s", outbuffer.c_str()); TEST_FAILED; } engine->Release(); //------------------------------ // The scope operator should permit calling global functions if the class has a method of the same name 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); const char *script = "class A { \n" " void func() { \n" " g = 0; \n" " testScope(); \n" " assert(g == 3); \n" " ::testScope(); \n" " assert(g == 2); \n" " } \n" " void testScope() { g = 3; } \n" "} \n" "void testScope() { g = 2; } \n" "int g; \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) { TEST_FAILED; } r = ExecuteString(engine, "A a; a.func(); assert( g == 2 );", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } engine->Release(); //--------------------------- // It should not be possible to declare a method with the same name as the class { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "class A { \n" " void A() {} \n" "} \n"; mod->AddScriptSection("script", script); bout.buffer = ""; r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (2, 3) : Error : The method cannot be named with the class name\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Success return fail; }