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. engine->RegisterGlobalFunction("void Log(const string&inout)", asFUNCTION(Log), asCALL_CDECL); 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" " Log(t.s); \n" // &inout parameter wasn't working " Log(t.get_s()); \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(CBufferedOutStream, Callback), &bout, 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" "class CTest2 \n" "{ \n" " CTest2() { arr.resize(1); } \n" " CTest @get_opIndex(int i) const { return arr[i]; } \n" " void set_opIndex(int i, CTest @v) { @arr[i] = v; } \n" " array<CTest@> 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" " CTest2 t; \n" " @t[0] = s; \n" " assert( t[0] is s ); \n" "} \n"; bout.buffer = ""; 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; // Test error script = "class CTest \n" "{ \n" " CTest() { } \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" "class CTest2 \n" "{ \n" " CTest2() { } \n" " CTest get_opIndex(int i) const { return arr[i]; } \n" " void set_opIndex(int i, CTest v) { @arr[i] = v; } \n" " array<CTest@> arr; \n" "} \n" "void main() \n" "{ \n" " CTest s; \n" " s[0] += 42; \n" // compound assignment is not allowed " CTest2 t; \n" " @t[0] = s; \n" // handle assign is not allowed for non-handle property "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r > 0 ) TEST_FAILED; if( bout.buffer != "script (15, 1) : Info : Compiling void main()\n" "script (18, 8) : Error : Compound assignments with property accessors are not allowed\n" "script (20, 9) : Error : It is not allowed to perform a handle assignment on a non-handle property\n" ) { printf("%s", bout.buffer.c_str()); 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", asOFFSET(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 by reference 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", asOFFSET(CNode, vector)); engine->RegisterObjectProperty("node", "float x", asOFFSET(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(); } // Make sure it is not possible to update properties of objects returned by value through getter { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, 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", "vector3 get_vector() const", asMETHOD(CNode, GetVector), asCALL_THISCALL); engine->RegisterObjectMethod("node", "void set_vector(const vector3 &in)", asMETHOD(CNode, SetVector), asCALL_THISCALL); r = ExecuteString(engine, "node @a = node(); \n" "a.vector.x = 1; \n" // Not OK "a.vector = vector3(1,0,0); \n"); // OK if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (2, 1) : Error : Expression is not an l-value\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } fail = Test2() || fail; // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; asIScriptContext *ctx; asIScriptEngine *engine; CBufferedOutStream bout; // --------------------------------------------- engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void SetMyAny(any@)", asFUNCTION(SetMyAny), asCALL_GENERIC); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("TestAny", script1, strlen(script1), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", "TestAny"); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestAny()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; printf("%s: Execution failed\n", "TestAny"); } if( ctx ) ctx->Release(); engine->Release(); //-------------------------------------------------- // Verify that the GC can handle circles with any structures engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void SetMyAny(any@)", asFUNCTION(SetMyAny), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("TestAny", script2, strlen(script2), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", "TestAny"); } ctx = engine->CreateContext(); r = ExecuteString(engine, "TestAny()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; printf("%s: Execution failed\n", "TestAny"); } if( ctx ) ctx->Release(); asUINT gcCurrentSize, gcTotalDestroyed, gcTotalDetected; engine->GetGCStatistics(&gcCurrentSize, &gcTotalDestroyed, &gcTotalDetected); engine->GarbageCollect(); engine->GetGCStatistics(&gcCurrentSize, &gcTotalDestroyed, &gcTotalDetected); if( !fail ) assert( gcCurrentSize == 8 && gcTotalDestroyed == 8 && gcTotalDetected == 7 ); engine->Release(); //------------------------------------------------------- // Don't allow const handle to retrieve() engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void SetMyAny(any@)", asFUNCTION(SetMyAny), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("TestAny", script3, strlen(script3), 0); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to Build()\n", "TestAny"); } if( bout.buffer != "TestAny (5, 1) : Info : Compiling void TestAny()\n" "TestAny (9, 15) : Warning : Argument cannot be assigned. Output will be discarded.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); //-------------------------------------------------------- // Make sure it is possible to pass any to the application engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptArray(engine, true); RegisterScriptString_Generic(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = engine->RegisterGlobalFunction("void SetMyAny(any@)", asFUNCTION(SetMyAny), asCALL_GENERIC); assert( r >= 0 ); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("TestAny", script4, strlen(script4), 0); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile\n", "TestAny"); } r = ExecuteString(engine, "TestAny()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; printf("%s: Failed to execute\n", "TestAny"); } if( myAny ) { int typeId = myAny->GetTypeId(); if( !(typeId & asTYPEID_OBJHANDLE) ) TEST_FAILED; if( (typeId & asTYPEID_MASK_OBJECT) != asTYPEID_APPOBJECT ) TEST_FAILED; const char *decl = engine->GetTypeDeclaration(typeId); if( (decl == 0) || (strcmp(decl, "string@") != 0) ) { TEST_FAILED; printf("%s: Failed to return the correct type\n", "TestAny"); } int typeId2 = engine->GetTypeIdByDecl("string@"); if( typeId != typeId2 ) { TEST_FAILED; printf("%s: Failed to return the correct type\n", "TestAny"); } CScriptString *str = 0; myAny->Retrieve((void*)&str, typeId); if( str->buffer != "test" ) { TEST_FAILED; printf("%s: Failed to set the string correctly\n", "TestAny"); } if( str ) str->Release(); myAny->Release(); myAny = 0; } else TEST_FAILED; //-------------------------------------- // Make sure the any type can store primitives as well r = ExecuteString(engine, "any a; a.store(1); int b; a.retrieve(b); Assert(b == 1);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); RegisterScriptAny(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "void main() \n" "{ \n" " any storage; \n" " storage.store(vector3(1,1,1)); \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "main()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); TEST_FAILED; } ctx->Release(); engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; CBufferedOutStream bout; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "Test()", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; printf("%s: Execution failed: %d\n", TESTNAME, r); } if( ctx ) ctx->Release(); engine->Release(); // Test value class with unsafe ref { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class Good \n" "{ \n" " vector3 _val; \n" " Good(const vector3& in val) \n" " { \n" " _val = val; \n" " } \n" "}; \n" "class Bad \n" "{ \n" " vector3 _val; \n" " Bad(const vector3& val) \n" " { \n" " _val = val; \n" " } \n" "}; \n" "void test() \n" "{ \n" " // runs fine \n" " for (int i = 0; i < 2; i++) \n" " Good(vector3(1, 2, 3)); \n" " // causes vm stack corruption \n" " for (int i = 0; i < 2; i++) \n" " Bad(vector3(1, 2, 3)); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "test()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test ref to primitives { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "void func(){ \n" " float a; \n" " uint8 b; \n" " int c; \n" " funcA(c, a, b); \n" "} \n" "void funcA(float& a, uint8& b, int& c) {} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestUnsafeRef (1, 1) : Info : Compiling void func()\n" "TestUnsafeRef (5, 3) : Error : No matching signatures to 'funcA(int, float, uint8)'\n" "TestUnsafeRef (5, 3) : Info : Candidates are:\n" "TestUnsafeRef (5, 3) : Info : void funcA(float&inout, uint8&inout, int&inout)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test problem found by TheAtom // Passing an inout reference to a handle to a function wasn't working properly { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class T { int a; } \n" "void f(T@& p) { \n" " T t; \n" " t.a = 42; \n" " @p = t; \n" // or p=t; in which case t is copied "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "T @t; f(t); assert( t.a == 42 );\n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; CBufferedOutStream bout; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterScriptString(engine); r = engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); assert( r >= 0 ); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1); r = mod->Build(); if( r < 0 ) { TEST_FAILED; printf("%s: Failed to compile the script\n", TESTNAME); } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "Test()", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; printf("%s: Execution failed: %d\n", TESTNAME, r); } if( ctx ) ctx->Release(); engine->Release(); // Test value class with unsafe ref { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class Good \n" "{ \n" " vector3 _val; \n" " Good(const vector3& in val) \n" " { \n" " _val = val; \n" " } \n" "}; \n" "class Bad \n" "{ \n" " vector3 _val; \n" " Bad(const vector3& val) \n" " { \n" " _val = val; \n" " } \n" "}; \n" "void test() \n" "{ \n" " // runs fine \n" " for (int i = 0; i < 2; i++) \n" " Good(vector3(1, 2, 3)); \n" " // causes vm stack corruption \n" " for (int i = 0; i < 2; i++) \n" " Bad(vector3(1, 2, 3)); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "test()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test ref to primitives { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "void func(){ \n" " float a; \n" " uint8 b; \n" " int c; \n" " funcA(c, a, b); \n" "} \n" "void funcA(float& a, uint8& b, int& c) {} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestUnsafeRef (1, 1) : Info : Compiling void func()\n" "TestUnsafeRef (5, 3) : Error : No matching signatures to 'funcA(int, float, uint8)'\n" "TestUnsafeRef (5, 3) : Info : Candidates are:\n" "TestUnsafeRef (5, 3) : Info : void funcA(float&inout, uint8&inout, int&inout)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test problem found by TheAtom // Passing an inout reference to a handle to a function wasn't working properly { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class T { int a; } \n" "void f(T@& p) { \n" " T t; \n" " t.a = 42; \n" " @p = t; \n" // or p=t; in which case t is copied "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "T @t; f(t); assert( t.a == 42 );\n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // http://www.gamedev.net/topic/624722-bug-with/ { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class T { T() { val = 123; } int val; } \n" "T g_t; \n" "T &GetTest() { return g_t; } \n" "void f(T@& t) { \n" " assert( t.val == 123 ); \n" "} \n" "void func() { \n" " f(GetTest()); \n" " f(@GetTest()); \n" " T @t = GetTest(); \n" " f(t); \n" "} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestUnsafeRef (7, 1) : Info : Compiling void func()\n" "TestUnsafeRef (8, 3) : Error : No matching signatures to 'f(T)'\n" "TestUnsafeRef (8, 3) : Info : Candidates are:\n" "TestUnsafeRef (8, 3) : Info : void f(T@&inout)\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // http://www.gamedev.net/topic/624722-bug-with/ { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "class T { T() { val = 123; } int val; } \n" "T g_t; \n" "T &GetTest() { return g_t; } \n" "void f(T@& t) { \n" " assert( t.val == 123 ); \n" "} \n" "void func() { \n" " f(cast<T>(GetTest())); \n" " f(@GetTest()); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "func()", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx, true); } ctx->Release(); engine->Release(); } // http://www.gamedev.net/topic/636443-there-is-no-copy-operator-for-the-type-val-available/ { bout.buffer = ""; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->RegisterObjectType("Val", sizeof(int), asOBJ_VALUE | asOBJ_APP_PRIMITIVE); engine->RegisterObjectBehaviour("Val", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(0), asCALL_GENERIC); // With unsafe references the copy constructor doesn't have to be in, it can be inout too engine->RegisterObjectBehaviour("Val", asBEHAVE_CONSTRUCT, "void f(const Val &)", asFUNCTION(0), asCALL_GENERIC); engine->RegisterObjectBehaviour("Val", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(0), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, "Val GetVal() \n" "{ \n" " Val ret; \n" " return ret; \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Success return fail; }
bool TestVector3() { bool fail = false; COutStream out; CBufferedOutStream bout; int r; asIScriptEngine *engine = 0; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); Vector3 v; engine->RegisterGlobalProperty("vector3 v", &v); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r < 0 ) { printf("%s: Failed to build\n", TESTNAME); TEST_FAILED; } else { // Internal return r = ExecuteString(engine, "v = TestVector3();", mod); if( r < 0 ) { printf("%s: ExecuteString() failed %d\n", TESTNAME, r); TEST_FAILED; } if( v.x != 1 || v.y != 2 || v.z != 3 ) { printf("%s: Failed to assign correct Vector3\n", TESTNAME); TEST_FAILED; } // Manual return v.x = 0; v.y = 0; v.z = 0; asIScriptContext *ctx = engine->CreateContext(); ctx->Prepare(mod->GetFunctionIdByDecl("vector3 TestVector3()")); ctx->Execute(); Vector3 *ret = (Vector3*)ctx->GetReturnObject(); if( ret->x != 1 || ret->y != 2 || ret->z != 3 ) { printf("%s: Failed to assign correct Vector3\n", TESTNAME); TEST_FAILED; } ctx->Prepare(mod->GetFunctionIdByDecl("vector3 TestVector3Val(vector3)")); v.x = 3; v.y = 2; v.z = 1; ctx->SetArgObject(0, &v); ctx->Execute(); ret = (Vector3*)ctx->GetReturnObject(); if( ret->x != 3 || ret->y != 2 || ret->z != 1 ) { printf("%s: Failed to pass Vector3 by val\n", TESTNAME); TEST_FAILED; } ctx->Prepare(mod->GetFunctionIdByDecl("void TestVector3Ref(vector3 &out)")); ctx->SetArgObject(0, &v); ctx->Execute(); if( v.x != 1 || v.y != 2 || v.z != 3 ) { printf("%s: Failed to pass Vector3 by ref\n", TESTNAME); TEST_FAILED; } ctx->Release(); } // Assignment of temporary object r = ExecuteString(engine, "vector3 v; float x = (v = vector3(10.0f,7,8)).x; assert( x > 9.9999f && x < 10.0001f );"); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test some operator overloads r = ExecuteString(engine, "vector3 v(1,0,0); assert( (v*2).length() == 2 );"); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } r = ExecuteString(engine, "vector3 v(1,0,0); assert( (2*v).length() == 2 );"); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } r = ExecuteString(engine, "vector3 v(1,0,0); assert( (v+v).length() == 2 );"); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } r = ExecuteString(engine, "vector3 v(1,0,0); assert( v == vector3(1,0,0) );"); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } r = ExecuteString(engine, "vector3 v(1,0,0); assert( (v *= 2).length() == 2 );"); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // Test error message when constructor is not found bout.buffer = ""; engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = ExecuteString(engine, "vector3 v = vector3(4,3);"); if( r >= 0 ) { TEST_FAILED; } // TODO: the function signature for the constructors/factories should carry the name of the object if( bout.buffer != "ExecuteString (1, 13) : Error : No matching signatures to 'vector3(const uint, const uint)'\n" "ExecuteString (1, 13) : Info : Candidates are:\n" "ExecuteString (1, 13) : Info : void vector3::_beh_0_()\n" "ExecuteString (1, 13) : Info : void vector3::_beh_0_(const vector3&in)\n" "ExecuteString (1, 13) : Info : void vector3::_beh_0_(float, float, float)\n" "ExecuteString (1, 13) : Error : Can't implicitly convert from 'const int' to 'vector3'.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); // Test allocation of value types on stack { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptMath3D(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "void func() { vector3 v; v.x = 1; assert( v.x == 1 ); assert( v.y == 0 ); }"); r = mod->Build(); if( r < 0 ) TEST_FAILED; int func = mod->GetFunctionIdByName("func"); if( func < 0 ) TEST_FAILED; asIScriptContext *ctx = engine->CreateContext(); ctx->Prepare(func); // During the execution of the function there should not be any // new allocations, since the vector is allocated on the stack int allocs = GetNumAllocs(); r = ctx->Execute(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // TODO: Why is it different on GNUC? #ifdef __GNUC__ if( (GetNumAllocs() - allocs) != 2 ) #else if( (GetNumAllocs() - allocs) != 0 ) #endif { printf("There were %d allocations during the execution\n", GetNumAllocs() - allocs); TEST_FAILED; } ctx->Release(); engine->Release(); } return fail; }