//====================================================================== bool CScriptInt::Initialize(const std::string& szScriptDir, void* pCallbackFunction) { //Initialize scripting interface object if (/*(!szScriptDir.length()) ||*/ (!pCallbackFunction)) return false; //Initialize AngelScript this->m_pScriptEngine = asCreateScriptEngine(); if (!this->m_pScriptEngine) return false; //Set message callback function if (AS_FAILED(this->m_pScriptEngine->SetMessageCallback(asFUNCTION(pCallbackFunction), 0, asCALL_CDECL))) { this->m_pScriptEngine->ShutDownAndRelease(); return false; } //Register string type RegisterStdString(this->m_pScriptEngine); //Register array type RegisterScriptArray(this->m_pScriptEngine, false); //Register math functions RegisterScriptMath(this->m_pScriptEngine); //Save script path this->m_szScriptPath = szScriptDir; //Set and return indicator value return this->m_bInitialized = true; }
bool TestExecuteScript() { bool fail = false; COutStream out; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterStdString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); CScriptBuilder builder; int r = builder.StartNewModule(engine, 0); if( r >= 0 ) r = builder.AddSectionFromFile("scripts/TestExecuteScript.as"); if( r >= 0 ) r = builder.BuildModule(); if( r >= 0 ) { fail = ExecuteScript(); } engine->Release(); engine = NULL; return fail; }
void ConfigureEngine(asIScriptEngine *engine) { int r; // Register the script string type // Look at the implementation for this function for more information // on how to register a custom string type, and other object types. RegisterStdString(engine); if( !strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { // Register the functions that the scripts will be allowed to use. // Note how the return code is validated with an assert(). This helps // us discover where a problem occurs, and doesn't pollute the code // with a lot of if's. If an error occurs in release mode it will // be caught when a script is being built, so it is not necessary // to do the verification here as well. r = engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(PrintString), asCALL_CDECL); assert( r >= 0 ); } // It is possible to register the functions, properties, and types in // configuration groups as well. When compiling the scripts it then // be defined which configuration groups should be available for that // script. If necessary a configuration group can also be removed from // the engine, so that the engine configuration could be changed // without having to recompile all the scripts. }
bool Test() { bool fail = false; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterStdString(engine); engine->RegisterObjectType("Obj", sizeof(Obj), asOBJ_CLASS); engine->RegisterObjectProperty("Obj", "int v", offsetof(Obj, v)); COutStream out; engine->AddScriptSection(0, TESTNAME, script1, strlen(script1), 0); r = engine->Build(0, &out); if( r < 0 ) { fail = true; printf("%s: Failed to compile the script\n", TESTNAME); } engine->Release(); // Success return fail; }
bool CommonAPI::InitAS() { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL); RegisterStdString(engine); RegisterAPI(); CScriptBuilder builder; if(builder.StartNewModule(engine, "MyModule") < 0) FILE_LOG(logERROR) << "Unrecoverable error while starting a new module."; string scriptPath = exePath.substr(0, exePath.find_last_of(".")).append(".tes"); if(builder.AddSectionFromFile(scriptPath.c_str()) < 0) FILE_LOG(logERROR) << "Unable to load script." << scriptPath; else FILE_LOG(logINFO) << "Script " << scriptPath << " loaded successfully."; if(builder.BuildModule() < 0) FILE_LOG(logERROR) << "Please correct the errors in the script and try again."; module = engine->GetModule("MyModule"); return true; }
void Engine::init() { _engine->SetMessageCallback(asMETHOD(Engine, messageCallback), this, asCALL_THISCALL); RegisterStdString(_engine); RegisterScriptHandle(_engine); RegisterScriptAny(_engine); }
bool Test() { bool fail = false; int r; COutStream out; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterStdString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); // Test type conversions r = ExecuteString(engine, "string a = 123; assert( a == '123' ); \n" "a += 123; assert( a == '123123' ); \n"); if (r != asEXECUTION_FINISHED) TEST_FAILED; r = ExecuteString(engine, "string a = 123.4; \n" "assert( a == '123.4' ); \n" "a += 123.4; \n" "assert( a == '123.4123.4' ); \n"); if (r != asEXECUTION_FINISHED) TEST_FAILED; // Test find routines r = ExecuteString(engine, "string a = 'The brown fox jumped the white fox'; \n" "assert( a.findFirst('fox') == 10); \n" "assert( a.findFirstOf('fjq') == 10); \n" "assert( a.findFirstNotOf('The') == 3); \n" "assert( a.findLast('fox') == 31); \n" "assert( a.findLastOf('fjq') == 31); \n" "assert( a.findLastNotOf('fox') == 33); \n"); if (r != asEXECUTION_FINISHED) TEST_FAILED; // Test insert and erase r = ExecuteString(engine, "string a; \n" "a.insert(5, 'hello'); \n"); // attempt to insert beyond the size of the string if (r != asEXECUTION_EXCEPTION) TEST_FAILED; r = ExecuteString(engine, "string a; \n" "a.insert(0, 'hello'); \n" // at index 1 beyond the last it is allowed "a.erase(2,2); \n" "assert( a == 'heo' ); \n"); if (r != asEXECUTION_FINISHED) TEST_FAILED; engine->ShutDownAndRelease(); return fail; }
//#################### CONSTRUCTORS #################### ASXEngine::ASXEngine() { m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); if(!m_engine) { throw ASXException("Could not create scripting engine"); } m_engine->SetMessageCallback(asMETHOD(ASXEngine, message_callback), this, asCALL_THISCALL); RegisterStdString(m_engine); }
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); RegisterStdString(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->RegisterObjectType("Dict", sizeof(CDict), asOBJ_VALUE | asOBJ_APP_CLASS_CDA); engine->RegisterObjectBehaviour("Dict", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct), asCALL_CDECL_OBJLAST); engine->RegisterObjectBehaviour("Dict", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Destruct), asCALL_CDECL_OBJLAST); engine->RegisterObjectMethod("Dict", "Dict &opAssign(const Dict &in)", asMETHOD(CDict,operator=), asCALL_THISCALL); engine->RegisterObjectMethod("Dict", "Dict &opIndex(string)", asMETHOD(CDict, operator[]), 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; printf("%s: Failed to compile the script\n", TESTNAME); } asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "TestDict()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); printf("%s: Failed to execute script\n", TESTNAME); TEST_FAILED; } if( ctx ) ctx->Release(); engine->Release(); // Success return fail; }
bool Test2() { bool fail = false; COutStream out; CBufferedOutStream bout; int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterStdString(engine); engine->RegisterObjectType("CMyObj", 0, asOBJ_REF); engine->RegisterObjectBehaviour("CMyObj", asBEHAVE_FACTORY, "CMyObj @f()", asFUNCTION(MyObj_factory), asCALL_CDECL); engine->RegisterObjectBehaviour("CMyObj", asBEHAVE_ADDREF, "void f()", asMETHOD(CMyObj, AddRef), asCALL_THISCALL); engine->RegisterObjectBehaviour("CMyObj", asBEHAVE_RELEASE, "void f()", asMETHOD(CMyObj, Release), asCALL_THISCALL); engine->RegisterObjectMethod("CMyObj", "void set_Text(const string &in)", asMETHOD(CMyObj, set_Text), asCALL_THISCALL); const char *string = "void main() { \n" " CMyObj @obj = @CMyObj(); \n" " obj.Text = 'Hello world!'; \n" "} \n"; asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("string", string); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Test disabling property accessors engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); engine->SetEngineProperty(asEP_PROPERTY_ACCESSOR_MODE, 0); r = ExecuteString(engine, "CMyObj o; o.Text = 'hello';"); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 12) : Error : 'Text' is not a member of 'CMyObj'\n" ) { TEST_FAILED; printf("%s", bout.buffer.c_str()); } engine->Release(); return fail; }
asIScriptEngine * ScriptEngine::CreateEngine() { asPrepareMultithread(); mScriptEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION); mScriptContext = mScriptEngine->CreateContext(); mScriptEngine->SetMessageCallback(asFUNCTION(ScriptEngine::MessageCallback), NULL, asCALL_CDECL); RegisterStdString(mScriptEngine); mScriptEngine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(ScriptEngine::Print), asCALL_CDECL); return mScriptEngine; }
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"); }
/** Configures the script engine by binding functions, enums * \param asIScriptEngine engine = engine to configure */ void ScriptEngine::configureEngine(asIScriptEngine *engine) { // Register the script string type RegisterStdString(engine); //register std::string RegisterVec3(engine); //register Vec3 Scripting::Track::registerScriptFunctions(m_engine); Scripting::Track::registerScriptEnums(m_engine); Scripting::Kart::registerScriptFunctions(m_engine); Scripting::Physics::registerScriptFunctions(m_engine); // It is possible to register the functions, properties, and types in // configuration groups as well. When compiling the scripts it can then // be defined which configuration groups should be available for that // script. If necessary a configuration group can also be removed from // the engine, so that the engine configuration could be changed // without having to recompile all the scripts. }
bool RegisterCoreAPI( CASManager& manager ) override { auto& engine = *manager.GetEngine(); RegisterStdString( &engine ); RegisterScriptCTime( engine ); RegisterScriptCDateTime( engine ); RegisterScriptSQLCommon( engine ); RegisterScriptSQLite( engine ); RegisterScriptMySQL( engine ); RegisterScriptEventAPI( engine ); engine.RegisterObjectType( "CSQL", 0, asOBJ_REF | asOBJ_NOCOUNT ); engine.RegisterObjectMethod( "CSQL", "SQLiteConnection@ CreateSQLiteConnection(const " AS_STRING_OBJNAME "& in szFilename)", asMETHOD( CASSQL, CreateSQLiteConnection ), asCALL_THISCALL ); engine.RegisterObjectMethod( "CSQL", "MySQLConnection@ CreateMySQLConnection(" "const " AS_STRING_OBJNAME "& in szHost, const " AS_STRING_OBJNAME "& in szUser," "const " AS_STRING_OBJNAME "& in szPassword, const " AS_STRING_OBJNAME "& in szDatabase = \"\")", asMETHOD( CASSQL, CreateMySQLConnection ), asCALL_THISCALL ); engine.RegisterGlobalProperty( "CSQL SQL", &g_ASSQL ); as::RegisterVarArgsFunction( engine, "void", "Print", "const " AS_STRING_OBJNAME "& in szFormat", 0, 8, asFUNCTION( Print ) ); engine.RegisterGlobalFunction( "void Assert(const bool bCondition)", asFUNCTION( ScriptAssert ), asCALL_CDECL ); return true; }
void run(GLFWwindow* window, std::string const& scriptFile) { asIScriptEngine* engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); anglhck::registerToEngine(engine); RegisterStdString(engine); engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_CDECL); CScriptBuilder builder; builder.StartNewModule(engine, "MyModule"); builder.AddSectionFromFile(scriptFile); builder.BuildModule(); asIScriptModule *mod = engine->GetModule("MyModule"); asIScriptFunction *func = mod->GetFunctionByDecl("void main()"); asIScriptContext *ctx = engine->CreateContext(); ctx->Prepare(func); int ret = ctx->Execute(); if( ret == asEXECUTION_EXCEPTION ) { std::cout << ctx->GetExceptionString() << std::endl; } }
ScriptEngine::ScriptEngine() : m_engine(asCreateScriptEngine()) { if (!m_engine) return; // Set the message callback to receive information on errors in human readable form. int r = m_engine->SetMessageCallback(asFUNCTION(MessageCallback), &m_errorString, asCALL_CDECL); assert(r >= 0); RegisterStdString(m_engine); // RegisterStdStringUtils(engine); // The scripts can directly print text r = m_engine->RegisterGlobalFunction("void print(const string &in message)", asMETHOD(ScriptEngine, print), asCALL_THISCALL_ASGLOBAL, this); assert(r >= 0); RegisterScriptMath(m_engine); aatc::RegisterAllContainers(m_engine); registerObject(m_engine); registerAllTypes(m_engine); generateReference(); }
Context::Context() { // Create the asEngine if the asEngine hasn't been created yet. static bool done = false; if(!done) { done = true; asDetails.asEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION); assert(asDetails.asEngine); asDetails.asEngine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL); asDetails.asEngine->GetModule(0, asGM_CREATE_IF_NOT_EXISTS); RegisterStdString(asDetails.asEngine); RegisterScriptArray(asDetails.asEngine, true); } asDetails.asContext = asDetails.asEngine->CreateContext(); assert(asDetails.asContext); }
bool Test() { RET_ON_MAX_PORT bool fail = false; int r; COutStream out; CBufferedOutStream bout; // asIScriptContext *ctx; asIScriptEngine *engine; // asIScriptModule *mod; // Test circular reference between grid and ref { engine = asCreateScriptEngine(); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptHandle(engine); RegisterScriptGrid(engine); // Create the circular reference r = ExecuteString(engine, "grid<ref> a; a.resize(1,1); @a[0,0] = a;"); if (r != asEXECUTION_FINISHED) TEST_FAILED; engine->GarbageCollect(); asUINT currSize, totDestroy, totDetect; engine->GetGCStatistics(&currSize, &totDestroy, &totDetect); if (currSize != 0 || totDestroy != 1 || totDetect != 1) TEST_FAILED; engine->ShutDownAndRelease(); } // Test empty initialization list // http://www.gamedev.net/topic/658849-empty-array-initialization/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptGrid(engine); r = ExecuteString(engine, "grid<int> a = {};"); // Valid 0x0 grid if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "grid<int> a = {{}};"); // Valid 0x1 grid if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "grid<int> a = {{},{}};"); // Valid 0x2 grid if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test grid object forcibly destroyed by garbage collector // http://www.gamedev.net/topic/657955-a-quite-specific-bug/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptGrid(engine); RegisterScriptAny(engine); RegisterScriptArray(engine, false); asIScriptModule *mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "class B {} \n" "class A \n" "{ \n" " any a; \n" " grid<B@> t(10, 10); \n" " A() \n" " { \n" " a.store(@this); \n" " } \n" "} \n" "array<A@> arr; \n" "void main() \n" "{ \n" " arr.insertLast(@A()); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; // The type B is not really garbage collected asITypeInfo *t = mod->GetTypeInfoByDecl("B"); if( t == 0 || (t->GetFlags() & asOBJ_GC) ) TEST_FAILED; // grid<B> is not garbage collected since B is not t = mod->GetTypeInfoByDecl("grid<B>"); if( t == 0 || (t->GetFlags() & asOBJ_GC) ) TEST_FAILED; // grid<B@> is however garbage collected because it is not possible to know // that no class derived from B can't form a circular reference with it. t = mod->GetTypeInfoByDecl("grid<B@>"); if( t == 0 || !(t->GetFlags() & asOBJ_GC) ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test resize { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptGrid(engine); RegisterStdString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, "grid<string> g; \n" "g.resize(1,1); \n" "g[0,0] = 'hello'; \n" "g.resize(2,2); \n" "assert( g[0,0] == 'hello' ); \n" "g[1,1] = 'there'; \n" "g.resize(1,1); \n" "assert( g.width() == 1 && g.height() == 1 ); \n"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test initialization lists { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptGrid(engine); RegisterStdString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, "grid<int8> g = {{1,2,3},{4,5,6},{7,8,9}}; \n" "assert( g[0,0] == 1 ); \n" "assert( g[2,2] == 9 ); \n" "assert( g[0,2] == 7 ); \n"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "grid<string> g = {{'1','2'},{'4','5'}}; \n" "assert( g[0,0] == '1' ); \n" "assert( g[1,1] == '5' ); \n" "assert( g[0,1] == '4' ); \n"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "grid<grid<int>@> g = {{grid<int> = {{1}}, grid<int> = {{2}}}, {grid<int> = {{3}}, grid<int> = {{4}}}}; \n" "assert( g[0,0][0,0] == 1 ); \n" "assert( g[1,1][0,0] == 4 ); \n"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Success 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 TestRefScoped() { 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 = 0; CBufferedOutStream bout; asIScriptEngine *engine; asIScriptModule *mod; // REF+SCOPED // This type requires a factory and a release behaviour. It cannot have the addref behaviour. // The intention of this type is to permit value types that have special needs for memory management, // for example must be aligned on 16 byte boundaries, or must use a memory pool. The type must not allow // object handles (except when returning a new value from registered functions). bout.buffer = ""; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterStdString(engine); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); r = engine->RegisterObjectType("scoped", 0, asOBJ_REF | asOBJ_SCOPED); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("scoped", asBEHAVE_FACTORY, "scoped @f()", asFUNCTION(Scoped_Factory), asCALL_CDECL); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("scoped", asBEHAVE_RELEASE, "void f()", asFUNCTION(Scoped_Release), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectMethod("scoped", "scoped @opNeg()", asFUNCTION(Scoped_Negate), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectMethod("scoped", "scoped &opAssign(const scoped &in)", asFUNCTION(Scoped_Assignment), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectMethod("scoped", "scoped @opAdd(int)", asFUNCTION(Scoped_Add), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); // Enumerate the objects behaviours asIObjectType *ot = engine->GetObjectTypeById(engine->GetTypeIdByDecl("scoped")); if( ot->GetBehaviourCount() != 1 ) fail = true; asEBehaviours beh; ot->GetBehaviourByIndex(0, &beh); if( beh != asBEHAVE_RELEASE ) fail = true; // Must be possible to determine type id for scoped types with handle asIScriptFunction *func = engine->GetFunctionDescriptorById(ot->GetFactoryIdByIndex(0)); int typeId = func->GetReturnTypeId(); if( typeId != engine->GetTypeIdByDecl("scoped@") ) fail = true; // Don't permit handles to be taken r = engine->ExecuteString(0, "scoped @s = null"); if( r >= 0 ) fail = true; // TODO: The second message is a consequence of the first error, and should ideally not be shown if( sizeof(void*) == 4 ) { if( bout.buffer != "ExecuteString (1, 8) : Error : Object handle is not supported for this type\n" "ExecuteString (1, 13) : Error : Can't implicitly convert from '<null handle>' to 'scoped&'.\n" ) { printf(bout.buffer.c_str()); fail = true; } } else { if( bout.buffer != "ExecuteString (1, 8) : Error : Object handle is not supported for this type\n" "ExecuteString (1, 13) : Error : Can't implicitly convert from '<null handle>' to 'scoped&'.\n" ) { printf(bout.buffer.c_str()); fail = true; } } // Test a legal actions r = engine->ExecuteString(0, "scoped a"); if( r != asEXECUTION_FINISHED ) fail = true; bout.buffer = ""; r = engine->ExecuteString(0, "scoped s; scoped t = s + 10;"); if( r != asEXECUTION_FINISHED ) fail = true; if( bout.buffer != "" ) { printf(bout.buffer.c_str()); fail = true; } // Test a compiler assert failure reported by Jeff Slutter on 2009-04-02 bout.buffer = ""; const char *script = "SetObjectPosition( GetWorldPositionByName() ); \n"; r = engine->RegisterGlobalFunction("const scoped @GetWorldPositionByName()", asFUNCTION(Scoped_Factory), asCALL_CDECL); assert( r >= 0 ); r = engine->RegisterGlobalFunction("void SetObjectPosition(scoped &in)", asFUNCTION(Scoped_InRef), asCALL_CDECL); assert( r >= 0 ); r = engine->ExecuteString(0, script); if( r != asEXECUTION_FINISHED ) fail = true; if( bout.buffer != "" ) { printf(bout.buffer.c_str()); fail = true; } // It must be possible to include the scoped type as member in script class bout.buffer = ""; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "class A { scoped s; }"); r = mod->Build(); if( r < 0 ) fail = true; if( bout.buffer != "" ) { printf(bout.buffer.c_str()); fail = true; } r = engine->ExecuteString(0, "A a; scoped s; a.s = s;"); if( r != asEXECUTION_FINISHED ) { fail = true; } // Don't permit functions to be registered with handle for parameters bout.buffer = ""; r = engine->RegisterGlobalFunction("void f(scoped@)", asFUNCTION(DummyFunc), asCALL_GENERIC); if( r >= 0 ) fail = true; if( bout.buffer != "System function (1, 14) : Error : Object handle is not supported for this type\n" ) { printf(bout.buffer.c_str()); fail = true; } // Don't permit functions to be registered to take type by reference (since that require handles) bout.buffer = ""; r = engine->RegisterGlobalFunction("void f(scoped&)", asFUNCTION(DummyFunc), asCALL_GENERIC); if( r >= 0 ) fail = true; if( bout.buffer != "System function (1, 14) : Error : Only object types that support object handles can use &inout. Use &in or &out instead\n" ) { printf(bout.buffer.c_str()); fail = true; } // Permit &in r = engine->RegisterGlobalFunction("void f(scoped&in)", asFUNCTION(DummyFunc), asCALL_GENERIC); if( r < 0 ) fail = true; engine->Release(); return fail; }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("AngelScript 2.31.0: Build 1.02 secs, Save 0.0920 secs, Load 0.125 secs\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_CDECL); //////////////////////////////////////////// printf("\nGenerating...\n"); const int numArrays = 2; #ifdef _DEBUG const int numElements = 10; #else const int numElements = 200000; #endif string script; std::stringstream script_buffer; for (unsigned i = 0; i < numArrays; i++) { script_buffer << "int[] array_" << i << " = {"; if (numElements > 0) { script_buffer << 0; } for (unsigned j = 1; j < numElements; j++) { script_buffer << ", " << j; } script_buffer << "};"; } script_buffer << std::endl << "int main() { print (\"elem 999 = \" + array_0[999] + \"\\n\"); return 0; }"; script = script_buffer.str(); //////////////////////////////////////////// printf("\nBuilding...\n"); double time = GetSystemTimer(); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script.c_str(), script.size(), 0); int r = mod->Build(); time = GetSystemTimer() - time; if( r != 0 ) printf("Build failed\n"); else printf("Time = %f secs\n", time); //////////////////////////////////////////// printf("\nSaving...\n"); time = GetSystemTimer(); CBytecodeStream stream(""); mod->SaveByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); printf("Size = %d\n", int(stream.buffer.size())); //////////////////////////////////////////// printf("\nLoading...\n"); time = GetSystemTimer(); asIScriptModule *mod2 = engine->GetModule(0, asGM_ALWAYS_CREATE); mod2->LoadByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); engine->Release(); }
bool Test() { 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; }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("AngelScript 2.30.0 WIP: 4.14 secs\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); //////////////////////////////////////////// printf("\nGenerating...\n"); #ifdef _DEBUG const int numLines = 40; #else const int numLines = 40000; #endif string script; script.reserve(strlen(scriptBegin) + numLines*(strlen(scriptMiddle)+5) + strlen(scriptEnd)); script += scriptBegin; for( int n = 0; n < numLines; n++ ) { char buf[500]; sprintf(buf, scriptMiddle, n); script += buf; } script += scriptEnd; //////////////////////////////////////////// printf("\nBuilding...\n"); double time = GetSystemTimer(); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script.c_str(), script.size(), 0); int r = mod->Build(); time = GetSystemTimer() - time; if( r != 0 ) printf("Build failed\n", TESTNAME); else printf("Time = %f secs\n", time); //////////////////////////////////////////// printf("\nSaving...\n"); time = GetSystemTimer(); CBytecodeStream stream(""); mod->SaveByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); printf("Size = %d\n", int(stream.buffer.size())); //////////////////////////////////////////// printf("\nLoading...\n"); time = GetSystemTimer(); asIScriptModule *mod2 = engine->GetModule(0, asGM_ALWAYS_CREATE); mod2->LoadByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); engine->Release(); }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("AngelScript 2.25.1 WIP 0: 0.28 secs\n"); printf("AngelScript 2.25.1 WIP 1: 0.28 secs (local bytecode optimizations)\n"); printf("AngelScript 2.25.1 WIP 2: 0.27 secs (reversed order)\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); //////////////////////////////////////////// printf("\nGenerating...\n"); const int numFuncs = 2000; const int numCalls = 20000; string script; script.reserve(strlen(scriptBegin) + numFuncs*(strlen(scriptFuncDecl)+5) + numCalls*(strlen(scriptMiddle)+5) + strlen(scriptEnd)); for( int a = 0; a < numFuncs; a++ ) { char buf[500]; sprintf(buf, scriptFuncDecl, a); script += buf; } script += scriptBegin; for( int n = 0; n < numCalls; n++ ) { char buf[500]; sprintf(buf, scriptMiddle, n%numFuncs); script += buf; } script += scriptEnd; //////////////////////////////////////////// printf("\nBuilding...\n"); double time = GetSystemTimer(); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script.c_str(), script.size(), 0); int r = mod->Build(); time = GetSystemTimer() - time; if( r != 0 ) printf("Build failed\n", TESTNAME); else printf("Time = %f secs\n", time); //////////////////////////////////////////// printf("\nSaving...\n"); time = GetSystemTimer(); CBytecodeStream stream(""); mod->SaveByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); printf("Size = %d\n", int(stream.buffer.size())); //////////////////////////////////////////// printf("\nLoading...\n"); time = GetSystemTimer(); asIScriptModule *mod2 = engine->GetModule(0, asGM_ALWAYS_CREATE); mod2->LoadByteCode(&stream); time = GetSystemTimer() - time; printf("Time = %f secs\n", time); engine->Release(); }
bool Test() { bool fail = false; if( !fail ) fail = Test2(); int r; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); COutStream out; CBufferedOutStream bout; engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptString_Generic(engine); engine->RegisterGlobalFunction("void Assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); // Register an interface from the application r = engine->RegisterInterface("appintf"); assert( r >= 0 ); r = engine->RegisterInterfaceMethod("appintf", "void test()"); assert( r >= 0 ); // Test working example asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "test()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Test calling the interface method from the application int typeId = engine->GetModule(0)->GetTypeIdByDecl("myclass"); asIScriptObject *obj = (asIScriptObject*)engine->CreateScriptObject(typeId); int intfTypeId = engine->GetModule(0)->GetTypeIdByDecl("myintf"); asIObjectType *type = engine->GetObjectTypeById(intfTypeId); asIScriptFunction *func = type->GetMethodByDecl("void test()"); asIScriptContext *ctx = engine->CreateContext(); r = ctx->Prepare(func); if( r < 0 ) TEST_FAILED; ctx->SetObject(obj); ctx->Execute(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; intfTypeId = engine->GetTypeIdByDecl("appintf"); type = engine->GetObjectTypeById(intfTypeId); func = type->GetMethodByDecl("void test()"); r = ctx->Prepare(func); if( r < 0 ) TEST_FAILED; ctx->SetObject(obj); ctx->Execute(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( ctx ) ctx->Release(); if( obj ) obj->Release(); // Test class that don't implement all functions of the interface. // Test instanciating an interface. Shouldn't work. // Test that classes don't implement the same interface twice // Try copying an interface variable to another. Shouldn't work. // Test implicit conversion from class to interface that is not being implemented. Should give compiler error // Test implicit conversion from interface to class. Should give compiler error. 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 ) TEST_FAILED; if( bout.buffer != "TestInterface (5, 23) : Warning : The interface 'intf' is already implemented\n" "TestInterface (5, 7) : Error : Missing implementation of 'void intf::test()'\n" "TestInterface (9, 1) : Info : Compiling void test(intf&inout)\n" "TestInterface (11, 9) : Error : Data type can't be 'intf'\n" "TestInterface (13, 6) : Error : There is no copy operator for the type 'intf' available.\n" "TestInterface (15, 16) : Error : Can't implicitly convert from 'myclass&' to 'nointf@&'.\n" "TestInterface (16, 16) : Error : Can't implicitly convert from 'intf@&' to 'myclass@&'.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); // Test cast for both temporary handle and non-temporary handle { const char *script = "interface ScriptLogic {} \n" "class PlayerLogic : ScriptLogic {} \n" "ScriptLogic @getScriptObject() { return PlayerLogic(); } \n"; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterStdString(engine); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r < 0 ) TEST_FAILED; // Non-temporary handle r = ExecuteString(engine, "ScriptLogic @c = getScriptObject(); cast<PlayerLogic>(c);", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Temporary handle r = ExecuteString(engine, "cast<PlayerLogic>(getScriptObject());", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // It should be possible to inherit the implementation of an interface method { const char *script = "interface I { void method(); } \n" "class B { void method() {} } \n" "class D : B, I {} \n" "D d; \n"; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->Release(); } // Allow script declared interfaces to inherit from other interfaces { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); const char *script = "interface A { void a(); } \n" "interface B : A { void b(); } \n" "class C : B {} \n"; // Must implement both a() and b() bout.buffer = ""; mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestInterface (3, 7) : Error : Missing implementation of 'void B::b()'\n" "TestInterface (3, 7) : Error : Missing implementation of 'void A::a()'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Don't allow shared interface to implement non-shared interface script = "interface A {} \n" "shared interface B : A {} \n"; bout.buffer = ""; mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestInterface (2, 22) : Error : Shared type cannot implement non-shared interface 'A'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Implicit casts to an inherited interface should work script = "interface A {} \n" "interface B : A {} \n" "void func() \n" "{ \n" " A@ a; B@ b; \n" " @a = b; \n" "} \n"; bout.buffer = ""; mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Don't allow circular inheritance script = "interface A : C {} \n" "interface B : A {} \n" "interface C : B {} \n"; bout.buffer = ""; mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "TestInterface (3, 15) : Error : Can't implement itself, or another interface that implements this interface\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; const char *script = "void Test() {} \n" "class A : I { void i(float) {} void a(int) {} float f; } \n" "class B : A { B(int) {} } \n" "interface I { void i(float); } \n" "float a; \n" "const float aConst = 3.141592f; \n" "I@ i; \n" "enum E { eval = 0, eval2 = 2 } \n" "E e; \n" "typedef float real; \n" "real pi = 3.14f; \n" "import void ImpFunc() from \"mod\"; \n"; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterStdString(engine); float f; engine->RegisterTypedef("myFloat", "float"); engine->RegisterGlobalProperty("myFloat f", &f); engine->RegisterGlobalProperty("const float myConst", &f); engine->RegisterGlobalFunction("void func(int &in)", asFUNCTION(0), asCALL_GENERIC); engine->BeginConfigGroup("test"); engine->RegisterGlobalFunction("void func2()", asFUNCTION(0), asCALL_GENERIC); engine->EndConfigGroup(); engine->RegisterEnum("myEnum"); engine->RegisterEnumValue("myEnum", "value1", 1); engine->RegisterEnumValue("myEnum", "value2", 2); engine->RegisterFuncdef("void Callback(int a, int b)"); engine->RegisterInterface("MyIntf"); engine->RegisterInterfaceMethod("MyIntf", "void func() const"); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) fail = true; WriteConfigToFile(engine, "AS_DEBUG/config.txt"); DumpModule(mod); // Save/Restore the bytecode and then test again for the loaded bytecode CBytecodeStream stream(__FILE__"1"); mod->SaveByteCode(&stream); mod = engine->GetModule("2", asGM_ALWAYS_CREATE); mod->LoadByteCode(&stream); DumpModule(mod); engine->Release(); return fail; }
static bool TestEnum() { 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; } asIScriptEngine *engine; CBufferedOutStream bout; int r; bool fail = false; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); // Register the enum value r = engine->RegisterEnum("TEST_ENUM"); assert(r >= 0); r = engine->RegisterEnumValue("TEST_ENUM", "ENUM1", ENUM1); assert(r >= 0); r = engine->RegisterEnumValue("TEST_ENUM", "ENUM2", ENUM2); assert(r >= 0); r = engine->RegisterEnumValue("TEST_ENUM", "ENUM3", ENUM3); assert(r >= 0); r = engine->RegisterGlobalFunction("void funce(TEST_ENUM)", asFUNCTION(func), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterGlobalFunction("void output(int val1)", asFUNCTION(scriptOutput), asCALL_CDECL); assert(r >= 0); // Test calling generic function with enum value r = ExecuteString(engine, "funce(ENUM1);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "funce(TEST_ENUM::ENUM3);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Test using the registered enum values r = ExecuteString(engine, "output(ENUM1); output(ENUM2)"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "1\n10\n" ) TEST_FAILED; // Test script that declare an enum // enum value can be given as expression of constants // enum can be implicitly cast to number buffer = ""; asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); r = mod->AddScriptSection(NULL, script, strlen(script), 0); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test1()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "-1\n1\n2\n1200\n1201\n1202\n1203\n1205\n0\n1\n2\n" ) { TEST_FAILED; printf("%s", buffer.c_str()); } // Registered enums are literal constants // variable of enum type can be implictly cast to primitive buffer = ""; r = ExecuteString(engine, "TEST_ENUM e = ENUM1; switch( e ) { case ENUM1: output(e); }"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "1\n" ) TEST_FAILED; // Script declared enums behave the same buffer = ""; r = ExecuteString(engine, "TEST2_ENUM e = TEST_1; switch( e ) {case TEST_1: output(e); }", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "-1\n" ) TEST_FAILED; // enum values can't be declared with expressions including subsequent values bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script2 = "enum TEST_ERR { ERR1 = ERR2, ERR2 }"; mod = engine->GetModule("error", asGM_ALWAYS_CREATE); r = mod->AddScriptSection("error", script2, strlen(script2)); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "error (1, 22) : Info : Compiling TEST_ERR ERR1\n" "error (1, 24) : Error : Use of uninitialized global variable 'ERR2'.\n" "error (1, 1) : Info : Compiling TEST_ERR ERR2\n" "error (1, 1) : Error : Use of uninitialized global variable 'ERR1'.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // enum type name can't be overloaded with variable name in another scope bout.buffer = ""; r = ExecuteString(engine, "int TEST_ENUM = 999"); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 5) : Error : Illegal variable name 'TEST_ENUM'.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); // enum value name can be overloaded with variable name in another scope buffer = ""; r = ExecuteString(engine, "int ENUM1 = 999; output(ENUM1)"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "999\n" ) TEST_FAILED; // number cannot be implicitly cast to enum type bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = ExecuteString(engine, "TEST_ENUM val = 1"); if( r >= 0 ) TEST_FAILED; r = ExecuteString(engine, "float f = 1.2f; TEST_ENUM val = f"); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 17) : Error : Can't implicitly convert from 'uint' to 'TEST_ENUM'.\n" "ExecuteString (1, 33) : Error : Can't implicitly convert from 'float' to 'TEST_ENUM'.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); // constant number can be explicitly cast to enum type r = ExecuteString(engine, "TEST_ENUM val = TEST_ENUM(1)"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // primitive value can be explicitly cast to enum type r = ExecuteString(engine, "float f = 1.2f; TEST_ENUM val = TEST_ENUM(f)"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // math operator with enums buffer = ""; r = ExecuteString(engine, "int a = ENUM2 * 10; output(a); output(ENUM2 + ENUM1)"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "100\n11\n" ) TEST_FAILED; // comparison operator with enums buffer = ""; r = ExecuteString(engine, "if( ENUM2 > ENUM1 ) output(1);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "1\n" ) TEST_FAILED; // bitwise operators with enums buffer = ""; r = ExecuteString(engine, "output( ENUM2 << ENUM1 )"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "20\n" ) TEST_FAILED; // circular reference between enum and global variable are // allowed if they can be resolved const char *script3 = "enum TEST_EN \n" "{ \n" " EN1, \n" " EN2 = gvar, \n" " EN3, \n" "} \n" "const int gvar = EN1 + 10; \n"; mod = engine->GetModule("en", asGM_ALWAYS_CREATE); mod->AddScriptSection("en", script3, strlen(script3)); r = mod->Build(); if( r < 0 ) TEST_FAILED; buffer = ""; r = ExecuteString(engine, "output(EN2); output(EN3)", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "10\n11\n" ) TEST_FAILED; // functions can be overloaded for parameters with enum type const char *script4 = "void func(TEST_ENUM) { output(1); } \n" "void func(int) { output(2); } \n"; mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script4, strlen(script4)); r = mod->Build(); if( r < 0 ) TEST_FAILED; buffer = ""; r = ExecuteString(engine, "func(1); func(1.0f); TEST_ENUM e = ENUM1; func(e)", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "2\n2\n1\n" ) TEST_FAILED; // Using registered enum type in a script engine->RegisterEnum("game_type_t"); const char *script5 = "game_type_t random_game_type;\n" "void foo(game_type_t game_type)\n" "{\n" " random_game_type = game_type;\n" "};\n"; r = mod->AddScriptSection("script", script5, strlen(script5)); r = mod->Build(); if( r < 0 ) TEST_FAILED; // enum with assignment without comma const char *script6 = "enum test_wo_comma { value = 0 }"; r = mod->AddScriptSection("script", script6, strlen(script6)); r = mod->Build(); if( r < 0 ) TEST_FAILED; // Enums are not object types int eid; const char *ename = mod->GetEnumByIndex(0, &eid); if( eid < 0 || ename == 0 ) TEST_FAILED; asIObjectType *eot = engine->GetObjectTypeById(eid); if( eot ) TEST_FAILED; // enum must allow negate and binary complement operators bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = ExecuteString(engine, "int a = -ENUM1; int b = ~ENUM1;"); if( r < 0 ) TEST_FAILED; if( bout.buffer != "ExecuteString (1, 25) : Warning : Implicit conversion changed sign of value\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test specifying an unknown enum type name bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script7 = "void f() { funce(UNKNOWN_ENUM::ENUM1); }"; mod = engine->GetModule("error", asGM_ALWAYS_CREATE); r = mod->AddScriptSection("error", script7, strlen(script7)); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "error (1, 1) : Info : Compiling void f()\n" "error (1, 18) : Error : 'UNKNOWN_ENUM::ENUM1' is not declared\n") { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test specifying a non enum type name before the scope bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script8 = "class SomeClass\n" "{\n" " int SOMEVALUE;\n" "}\n" "void f() { funce(SomeClass::SOMEVALUE); }"; mod = engine->GetModule("error", asGM_ALWAYS_CREATE); r = mod->AddScriptSection("error", script8, strlen(script8)); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "error (5, 1) : Info : Compiling void f()\n" "error (5, 18) : Error : 'SomeClass::SOMEVALUE' is not declared\n") { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test engine property r = engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, 1); if( r != 0 ) TEST_FAILED; bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); const char *script9 = "void f() { funce(ENUM1); }"; mod = engine->GetModule("error", asGM_ALWAYS_CREATE); r = mod->AddScriptSection("error", script9, strlen(script9)); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "error (1, 1) : Info : Compiling void f()\n" "error (1, 18) : Error : 'ENUM1' is not declared\n") { printf("%s", bout.buffer.c_str()); TEST_FAILED; } buffer = ""; r = ExecuteString(engine, "output(TEST_ENUM::ENUM1);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "1\n" ) TEST_FAILED; // Test enum in param to class method assert( sizeof(TEST_ENUM) == 4 ); r = engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, 0); r = engine->RegisterObjectType("Obj", 0, asOBJ_REF | asOBJ_NOHANDLE); assert( r >= 0 ); r = engine->RegisterObjectMethod("Obj", "bool TestEnum(TEST_ENUM)", asMETHOD(CTestObject, TestEnum), asCALL_THISCALL); assert( r >= 0 ); CTestObject obj; obj.val = ENUM1; r = engine->RegisterGlobalProperty("Obj obj", &obj); assert( r >= 0 ); bout.buffer = ""; r = ExecuteString(engine, "if( !obj.TestEnum(ENUM2) ) assert(false); "); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } if( obj.val != ENUM2 ) TEST_FAILED; // Repeated enum values would enter an infinit loop bout.buffer = ""; const char *script10 = "enum Infinite { inf, inf }"; mod->AddScriptSection("test", script10); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "test (1, 22) : Error : Name conflict. 'inf' is already used.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); { COutStream out; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterStdString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "enum waveformType \n" "{ \n" " sawtoothWave = 1, \n" " squareWave = 2, \n" " sineWave = 3 \n" "} \n" "void main() \n" "{ \n" " tone_synth synth; \n" " synth.waveform_type = sineWave; \n" " assert( '' + sineWave + '' == '3' ); \n" " assert( synth.waveform_type == 3 ); \n" "} \n" "class tone_synth { void set_waveform_type(double v) {prop = v;} double get_waveform_type() {return prop;} double prop; }\n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } //paste at the end of TestEnum() { COutStream out; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); r = engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, 1); assert(r >= 0); r = engine->RegisterEnum("RENUM_1"); assert(r >= 0); r = engine->RegisterEnumValue("RENUM_1", "R_GLOBAL", 0); assert(r >= 0); r = engine->RegisterEnumValue("RENUM_1", "RE1_1", 1); assert(r >= 0); r = engine->RegisterEnumValue("RENUM_1", "RE1_2", 2); assert(r >= 0); r = engine->RegisterEnum("RENUM_2"); assert(r >= 0); r = engine->RegisterEnumValue("RENUM_2", "R_GLOBAL", 13); assert(r >= 0); r = engine->RegisterEnumValue("RENUM_2", "RE2_1", 1); assert(r >= 0); r = engine->RegisterEnumValue("RENUM_2", "RE2_2", 2); assert(r >= 0); r = engine->RegisterGlobalFunction("void output(int val1)", asFUNCTION(scriptOutput), asCALL_CDECL); assert(r >= 0); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "enum ENUM_1 \n" "{ \n" " TEST_GLOBAL = 0, \n" " E1_VAL1, \n" " E1_VAL2, \n" " RE1_2 \n" "} \n" "enum ENUM_2 \n" "{ \n" " TEST_GLOBAL = 0, \n" " E2_VAL1, \n" " E2_VAL2 \n" "} \n" "ENUM_1 g_e1 = ENUM_1::E1_VAL1; \n" "RENUM_1 rg_e1 = RENUM_1::RE1_2; \n" " \n" "void main() \n" "{ \n" " ENUM_1 l_e1 = ENUM_1::E1_VAL1; \n" " g_e1 = ENUM_1::E1_VAL1; \n" " rg_e1 = RENUM_1::R_GLOBAL; \n" " RENUM_2 rl_e2 = RENUM_2::R_GLOBAL; \n" " output(rg_e1); \n" " output(rl_e2); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; buffer = ""; r = ExecuteString(engine, "main();", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( buffer != "0\n13\n" ) TEST_FAILED; engine->Release(); } // Some validations that must be done { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); r = engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, 1); assert(r >= 0); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "enum ENUM_1 \n" "{ \n" " E1_VAL1 \n" "}; \n" // Semi colon after enum declaration is allowed, but optional "ENUM_1 g_e1 = ENUM_1::E1_VAL1; \n" "ENUM_1 g_e2 = E2_VAL1; \n"); // <- that shouldn't (?) r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (6, 13) : Info : Compiling ENUM_1 g_e2\n" "script (6, 15) : Error : 'E2_VAL1' is not declared\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = engine->RegisterEnum("RENUM_1"); assert(r >= 0); r = engine->RegisterEnumValue("RENUM_1", "@#$%", 777); // shouldn't work if( r >= 0 ) TEST_FAILED; engine->Release(); } // Test problem reported by Andrew Ackermann // The code crashed in ALLOC as the enum was copied as 8bytes on 64bit platforms { const char *script = "enum TestEnum { \n" " TE_0, \n" " TE_1, \n" "}; \n" "class TestClass { \n" " TestClass(TestEnum en) { \n" " } \n" "}; \n" "void init() { \n" " TestClass@ cl = TestClass(TE_1); \n" "} \n"; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); 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, "init()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test problem reported by SadSingleton // http://www.gamedev.net/topic/622524-crash-using-the-identity-operator-with-enum-values/ { const char *script = "enum MyEnum { MyEnumValue = 1 } \n" "void Update() \n" "{ \n" " MyEnum enumValue = MyEnumValue; \n" " bool condition = true; \n" " if (condition) \n" " { \n" " if(enumValue is MyEnumValue) \n" " { \n" " int i = 0; \n" " } \n" " } \n" " else \n" " { \n" " int j = 1; \n" " } \n" "} \n"; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); bout.buffer = ""; r = engine->SetMessageCallback(asMETHOD(CBufferedOutStream,Callback), &bout, asCALL_THISCALL); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (2, 1) : Info : Compiling void Update()\n" "script (8, 30) : Error : Both operands must be handles when comparing identity\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Success return fail; }
void Test() { printf("---------------------------------------------\n"); printf("%s\n\n", TESTNAME); printf("Machine 1\n"); printf("AngelScript 1.9.0 : 11.00 secs\n"); printf("AngelScript 1.9.1 : 9.618 secs\n"); printf("AngelScript 1.9.2 : 8.748 secs\n"); printf("AngelScript 1.10.0 with C++ VM: 7.073 secs\n"); printf("AngelScript 1.10.0 with ASM VM: 7.613 secs\n"); printf("AngelScript 1.10.1 with ASM VM: 6.432 secs\n"); printf("\n"); printf("Machine 2\n"); printf("AngelScript 1.9.0 : 4.806 secs\n"); printf("AngelScript 1.9.1 : 4.300 secs\n"); printf("AngelScript 1.9.2 : 3.686 secs\n"); printf("AngelScript 1.10.0 with C++ VM: 2.973 secs\n"); printf("AngelScript 1.10.0 with ASM VM: 3.329 secs\n"); printf("AngelScript 1.10.1 with ASM VM: 2.936 secs\n"); printf("AngelScript 2.0.0 with C++ VM: 6.182 secs\n"); printf("AngelScript 2.0.0 with ASM VM: 5.958 secs\n"); printf("\nBuilding...\n"); asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterStdString(engine); COutStream out; engine->AddScriptSection(0, TESTNAME, script, strlen(script), 0); engine->Build(0, &out); asIScriptContext *ctx; engine->CreateContext(&ctx); 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(); }
bool Test() { bool fail = false; int r; COutStream out; asIScriptContext *ctx; asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, true); RegisterStdString(engine); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->RegisterGlobalFunction("void print(int)", asFUNCTION(print_generic), asCALL_GENERIC); engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print_generic), asCALL_GENERIC); asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection(TESTNAME, script1, strlen(script1), 0); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } ctx = engine->CreateContext(); r = ExecuteString(engine, "main()", mod, ctx); if( r != asEXECUTION_FINISHED ) { if( r == asEXECUTION_EXCEPTION ) PRINTF("%s", GetExceptionInfo(ctx).c_str()); PRINTF("%s: Failed to execute script\n", TESTNAME); TEST_FAILED; } if( ctx ) ctx->Release(); { const char *script = "class CTest \n" "{ \n" " string name; \n" " \n" " CTest() { name = 'temp'; print('CTest::CTest() for ' + name + '\\n'); } \n" " CTest(string s) { name = s; print('CTest::CTest() for ' + name + '\\n'); } \n" " ~CTest(){ print('CTest::~CTest() for ' + name + '\\n'); } \n" " \n" // " CTest @opAssign(const CTest &in o) { print('CTest::opAssign(), ' + name + ' becomes ' + o.name + '\\n'); name = o.name; return this; } \n" " void test(){ print('CTest::test() for ' + name + '\\n'); } \n" "} \n" "void test() \n" "{ \n" " CTest t1('Ent1'); \n" " CTest t2('Ent2'); \n" " \n" " t1.test(); \n" " t2.test(); \n" " \n" " t2 = t1; \n" "} \n"; mod->AddScriptSection(TESTNAME, script); r = mod->Build(); if( r < 0 ) { TEST_FAILED; PRINTF("%s: Failed to compile the script\n", TESTNAME); } r = ExecuteString(engine, "test()", mod); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; } // There shouldn't be any temporary variable created for the assignment if( buffer != "CTest::CTest() for Ent1\n" "CTest::CTest() for Ent2\n" "CTest::test() for Ent1\n" "CTest::test() for Ent2\n" "CTest::~CTest() for Ent1\n" "CTest::~CTest() for Ent1\n" ) { PRINTF("%s", buffer.c_str()); TEST_FAILED; } } engine->Release(); // Success 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; }