From adceb3581231e3d08a987703695335100cc0bcc6 Mon Sep 17 00:00:00 2001 From: konstin Date: Thu, 23 Apr 2015 17:40:20 +0200 Subject: [PATCH 01/44] add --log to --help message --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 98b88bcad..5d9854cf1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -547,6 +547,8 @@ void cmdLineHelp() " stdout.log.\n" " --console Write messages in the console and files\n" " -h, --help Show this help.\n" + " --log=N Set the verbosity to a value between\n" + " 0 (Debug) and 5 (Only Fatal messages)\n" "\n" "You can visit SuperTuxKart's homepage at " "http://supertuxkart.sourceforge.net\n\n", From 9f883db6f7a65ec3dde21427eca40276401a0689 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Fri, 24 Apr 2015 20:59:32 -0400 Subject: [PATCH 02/44] Start work to enable scripting --- lib/angelscript/source/as_context.cpp | 2 +- src/scriptengine/script_engine.cpp | 120 +++++++++++++-------- src/scriptengine/script_engine.hpp | 1 + src/scriptengine/script_track.cpp | 2 +- src/scriptengine/scriptstdstring_utils.cpp | 109 +++++++++++++++++-- src/tracks/track.cpp | 4 + src/tracks/track_object_presentation.cpp | 9 +- src/utils/string_utils.cpp | 28 ++++- src/utils/string_utils.hpp | 7 +- src/utils/translation.cpp | 42 +++----- 10 files changed, 230 insertions(+), 94 deletions(-) diff --git a/lib/angelscript/source/as_context.cpp b/lib/angelscript/source/as_context.cpp index e3eec61de..25a1e376c 100644 --- a/lib/angelscript/source/as_context.cpp +++ b/lib/angelscript/source/as_context.cpp @@ -339,7 +339,7 @@ int asCContext::Prepare(asIScriptFunction *func) asASSERT( m_engine ); // Make sure the function is from the same engine as the context to avoid mixups - if( m_engine != func->GetEngine() ) + if (m_engine != func->GetEngine()) { asCString str; str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, "Prepare", func->GetDeclaration(true, true), asINVALID_ARG); diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 2d6bf293b..646bd6b28 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -29,7 +29,7 @@ #include "states_screens/dialogs/tutorial_message_dialog.hpp" #include "tracks/track_object_manager.hpp" #include "tracks/track.hpp" - +#include "utils/profiler.hpp" using namespace Scripting; @@ -38,8 +38,7 @@ namespace Scripting { -//Line Callback -void MessageCallback(const asSMessageInfo *msg, void *param) +void AngelScript_ErrorCallback (const asSMessageInfo *msg, void *param) { const char *type = "ERR "; if (msg->type == asMSGTYPE_WARNING) @@ -47,7 +46,7 @@ void MessageCallback(const asSMessageInfo *msg, void *param) else if (msg->type == asMSGTYPE_INFORMATION) type = "INFO"; - printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message); + Log::warn("Scripting", "%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message); } @@ -56,19 +55,19 @@ ScriptEngine::ScriptEngine() { // Create the script engine m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); - if( m_engine == 0 ) + if (m_engine == NULL) { - std::cout << "Failed to create script engine." << std::endl; + Log::error("Scripting", "Failed to create script engine."); } // The script compiler will write any compiler messages to the callback. - //m_engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL); + m_engine->SetMessageCallback(asFUNCTION(AngelScript_ErrorCallback), 0, asCALL_CDECL); // Configure the script engine with all the functions, // and variables that the script should be able to use. configureEngine(m_engine); - } + ScriptEngine::~ScriptEngine() { // Release the engine @@ -85,12 +84,15 @@ std::string getScript(std::string scriptName) { std::string script_dir = file_manager->getAsset(FileManager::SCRIPT, ""); script_dir += World::getWorld()->getTrack()->getIdent() + "/"; - if (scriptName != "update" && scriptName != "collisions" && scriptName!="start") scriptName = "triggers"; + if (scriptName != "update" && scriptName != "collisions" && scriptName != "start") + scriptName = "triggers"; + script_dir += scriptName + ".as"; FILE *f = fopen(script_dir.c_str(), "rb"); - if( f == 0 ) + if (f == NULL) { - std::cout << "Failed to open the script file " + scriptName + ".as" << std::endl; + Log::debug("Scripting", "File does not exist : {0}.as", scriptName.c_str()); + return ""; } // Determine the size of the file @@ -103,25 +105,27 @@ std::string getScript(std::string scriptName) script.resize(len); int c = fread(&script[0], len, 1, f); fclose(f); - if( c == 0 ) + if (c == NULL) { - std::cout << "Failed to load script file." << std::endl; + Log::error("Scripting", "Failed to load script file."); + return ""; } return script; } + //----------------------------------------------------------------------------- /** runs the specified script * \param string scriptName = name of script to run */ void ScriptEngine::runScript(std::string scriptName) { - return; // Scripting disabled for now + PROFILER_PUSH_CPU_MARKER("RunScript", 255, 0, 0); // Create a context that will execute the script. asIScriptContext *ctx = m_engine->CreateContext(); - if (ctx == 0) + if (ctx == NULL) { - std::cout << "Failed to create the context." << std::endl; + Log::error("Scripting", "Failed to create the context."); //m_engine->Release(); return; } @@ -132,16 +136,12 @@ void ScriptEngine::runScript(std::string scriptName) if (m_script_cache.find(scriptName) == m_script_cache.end()) { // Compile the script code + Log::debug("Scripting", "Compiling script '%s' (was not in cache)", scriptName.c_str()); r = compileScript(m_engine, scriptName); if (r < 0) { - return; - } - - //set line callback here soon - if (r < 0) - { - std::cout << "Failed to set the line callback function." << std::endl; + Log::debug("Scripting", "Script '%s' is not available", scriptName.c_str()); + m_script_cache[scriptName] = NULL; // remember that this script is unavaiable ctx->Release(); return; } @@ -166,32 +166,40 @@ void ScriptEngine::runScript(std::string scriptName) //trigger type can have different names func = Scripting::Track::registerScriptCallbacks(m_engine, scriptName); } - if (func == 0) + + if (func == NULL) { - std::cout << "The required function was not found." << std::endl; + Log::warn("Scripting", "The required function was not found : %s", scriptName.c_str()); ctx->Release(); - //m_engine->Release(); return; } //CACHE UPDATE m_script_cache[scriptName] = func; + func->AddRef(); } else { //Script present in cache + // TODO: clear when done func = m_script_cache[scriptName]; - std::cout << "FOUND CACHED : " << scriptName << std::endl; } + + if (func == NULL) + { + PROFILER_POP_CPU_MARKER(); + return; // script unavailable + } + // Prepare the script context with the function we wish to execute. Prepare() // must be called on the context before each new script function that will be // executed. Note, that if because we intend to execute the same function // several times, we will store the function returned by // GetFunctionByDecl(), so that this relatively slow call can be skipped. r = ctx->Prepare(func); - if( r < 0 ) + if (r < 0) { - std::cout << "Failed to prepare the context." << std::endl; + Log::error("Scripting", "Failed to prepare the context."); ctx->Release(); //m_engine->Release(); return; @@ -204,25 +212,29 @@ void ScriptEngine::runScript(std::string scriptName) // Execute the function r = ctx->Execute(); - if( r != asEXECUTION_FINISHED ) + if (r != asEXECUTION_FINISHED) { // The execution didn't finish as we had planned. Determine why. - if( r == asEXECUTION_ABORTED ) - std::cout << "The script was aborted before it could finish. Probably it timed out." << std::endl; - else if( r == asEXECUTION_EXCEPTION ) + if (r == asEXECUTION_ABORTED) { - std::cout << "The script ended with an exception." << std::endl; + Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out."); + } + else if (r == asEXECUTION_EXCEPTION) + { + Log::error("Scripting", "The script ended with an exception."); // Write some information about the script exception asIScriptFunction *func = ctx->GetExceptionFunction(); - std::cout << "func: " << func->GetDeclaration() << std::endl; - std::cout << "modl: " << func->GetModuleName() << std::endl; - std::cout << "sect: " << func->GetScriptSectionName() << std::endl; - std::cout << "line: " << ctx->GetExceptionLineNumber() << std::endl; - std::cout << "desc: " << ctx->GetExceptionString() << std::endl; + //std::cout << "func: " << func->GetDeclaration() << std::endl; + //std::cout << "modl: " << func->GetModuleName() << std::endl; + //std::cout << "sect: " << func->GetScriptSectionName() << std::endl; + //std::cout << "line: " << ctx->GetExceptionLineNumber() << std::endl; + //std::cout << "desc: " << ctx->GetExceptionString() << std::endl; } else - std::cout << "The script ended for some unforeseen reason (" << r << ")." << std::endl; + { + Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); + } } else { @@ -234,8 +246,19 @@ void ScriptEngine::runScript(std::string scriptName) // We must release the contexts when no longer using them ctx->Release(); + PROFILER_POP_CPU_MARKER(); } +//----------------------------------------------------------------------------- + +void ScriptEngine::cleanupCache() +{ + for (auto curr : m_script_cache) + { + curr.second->Release(); + } + m_script_cache.clear(); +} //----------------------------------------------------------------------------- /** Configures the script engine by binding functions, enums @@ -245,6 +268,7 @@ void ScriptEngine::configureEngine(asIScriptEngine *engine) { // Register the script string type RegisterStdString(engine); //register std::string + RegisterStdStringUtils(engine); RegisterVec3(engine); //register Vec3 Scripting::Track::registerScriptFunctions(m_engine); @@ -266,13 +290,17 @@ void ScriptEngine::configureEngine(asIScriptEngine *engine) //----------------------------------------------------------------------------- - - int ScriptEngine::compileScript(asIScriptEngine *engine, std::string scriptName) { int r; std::string script = getScript(scriptName); + if (script.size() == 0) + { + // No such file + return -1; + } + // Add the script sections that will be compiled into executable code. // If we want to combine more than one file into the same script, then // we can call AddScriptSection() several times for the same module and @@ -280,9 +308,9 @@ int ScriptEngine::compileScript(asIScriptEngine *engine, std::string scriptName) // section name, will allow us to localize any errors in the script code. asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); r = mod->AddScriptSection("script", &script[0], script.size()); - if( r < 0 ) + if (r < 0) { - std::cout << "AddScriptSection() failed" << std::endl; + Log::error("Scripting", "AddScriptSection() failed"); return -1; } @@ -291,9 +319,9 @@ int ScriptEngine::compileScript(asIScriptEngine *engine, std::string scriptName) // script engine. If there are no errors, and no warnings, nothing will // be written to the stream. r = mod->Build(); - if( r < 0 ) + if (r < 0) { - std::cout << "Build() failed" << std::endl; + Log::error("Scripting", "Build() failed"); return -1; } diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index 6d742ccc5..2affef764 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -65,6 +65,7 @@ namespace Scripting ~ScriptEngine(); void runScript(std::string scriptName); + void cleanupCache(); private: asIScriptEngine *m_engine; diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index 26db06ce8..41d6702ee 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -156,7 +156,7 @@ namespace Scripting void displayMessage(asIScriptGeneric *gen) { std::string *input = (std::string*)gen->GetArgAddress(0); - irr::core::stringw out = irr::core::stringw((*input).c_str()); //irr::core::stringw supported by message dialogs + irr::core::stringw out = StringUtils::utf8_to_wide(input->c_str()); new TutorialMessageDialog((out), true); } //generic disable method for track objects diff --git a/src/scriptengine/scriptstdstring_utils.cpp b/src/scriptengine/scriptstdstring_utils.cpp index 27ba927c1..1436e8479 100644 --- a/src/scriptengine/scriptstdstring_utils.cpp +++ b/src/scriptengine/scriptstdstring_utils.cpp @@ -20,6 +20,7 @@ #include "scriptarray.hpp" #include #include +#include "utils/translation.hpp" using namespace std; @@ -125,22 +126,110 @@ static void StringJoin_Generic(asIScriptGeneric *gen) new(gen->GetAddressOfReturnLocation()) string(StringJoin(*array, *delim)); } +void translate(asIScriptGeneric *gen) +{ + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + + irr::core::stringw out = translations->fribidize(translations->w_gettext(input->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) string(StringUtils::wide_to_utf8(out.c_str())); +} + +void insertValues1(asIScriptGeneric *gen) +{ + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), + StringUtils::utf8_to_wide(arg1->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) string(StringUtils::wide_to_utf8(out.c_str())); +} + +void insertValues2(asIScriptGeneric *gen) +{ + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + std::string *arg2 = (std::string*)gen->GetArgAddress(2); + + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) string(StringUtils::wide_to_utf8(out.c_str())); +} + +void insertValues3(asIScriptGeneric *gen) +{ + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + std::string *arg2 = (std::string*)gen->GetArgAddress(2); + std::string *arg3 = (std::string*)gen->GetArgAddress(3); + + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str()), + StringUtils::utf8_to_wide(arg3->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) string(StringUtils::wide_to_utf8(out.c_str())); +} + +/* +void insertValues(asIScriptGeneric *gen) +{ + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + CScriptArray *array = *(CScriptArray**)gen->GetAddressOfArg(1); + + int size = array->GetSize(); + vector all_values; + for (int i = 0; i < size; i++) + { + string* curr = (string*)array->At(i); + all_values.push_back(curr); + } + + StringUtils::insertValues(*input, all_values); + + irr::core::stringw out = translations->fribidize(translations->w_gettext(input->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) string(irr::core::stringc(out).c_str()); +} +*/ + // This is where the utility functions are registered. // The string type must have been registered first. void RegisterStdStringUtils(asIScriptEngine *engine) { int r; - if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) - { - r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0); - } - else - { - r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0); - } + //if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")) + //{ + //r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0); + //r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string translate(const string &in)", asFUNCTION(translate), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in)", asFUNCTION(insertValues1), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(insertValues2), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(insertValues3), asCALL_GENERIC); assert(r >= 0); + //} + //else + //{ + // //r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0); + // //r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0); + // r = engine->RegisterGlobalFunction("string translate(const string &in)", asFUNCTION(translate), asCALL_CDECL); assert(r >= 0); + // r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &arg1)", asFUNCTION(insertValues1), asCALL_CDECL); assert(r >= 0); + // r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &arg1, const string &arg2)", asFUNCTION(insertValues2), asCALL_CDECL); assert(r >= 0); + // r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &arg1, const string &arg2, const string &arg3)", asFUNCTION(insertValues3), asCALL_CDECL); assert(r >= 0); + //} } END_AS_NAMESPACE diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index dc54d4896..3645fe53a 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -448,6 +448,10 @@ void Track::cleanup() Log::info("CACHE", "[%i] %s", i, path.getPath().c_str()); } #endif + + Scripting::ScriptEngine* script_engine = + World::getWorld()->getScriptEngine(); + script_engine->cleanupCache(); } // cleanup //----------------------------------------------------------------------------- diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 5e2ba515a..30ca9a2d0 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -921,6 +921,7 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) _("Complete all challenges to unlock the big door!"), true); } } + /* else if (m_action == "tutorial_drive") { //if (World::getWorld()->getPhase() == World::RACE_PHASE) @@ -1036,19 +1037,19 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) new TutorialMessageDialog(_("You are now ready to race. Good luck!"), true); - } + }*/ else if (m_action == "tutorial_exit") { + // TODO: move to scripting World::getWorld()->scheduleExitRace(); return; } else { - //TODO move all above functions into scripts and remove the ifs - Scripting::ScriptEngine* m_script_engine = + Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); m_action_active = false; - m_script_engine->runScript(m_action); + script_engine->runScript(m_action); /* Catch exception -> script not found diff --git a/src/utils/string_utils.cpp b/src/utils/string_utils.cpp index 3984e874d..f5e6e8ea3 100644 --- a/src/utils/string_utils.cpp +++ b/src/utils/string_utils.cpp @@ -22,7 +22,7 @@ #include "utils/log.hpp" #include "utils/time.hpp" - +#include "utils/utf8.h" #include "coreutil.h" #include @@ -684,6 +684,32 @@ namespace StringUtils return output.str(); } // xmlEncode + // ------------------------------------------------------------------------ + + std::string wide_to_utf8(const wchar_t* input) + { + static std::vector utf8line; + utf8line.clear(); + + utf8::utf16to8(input, input + wcslen(input), back_inserter(utf8line)); + utf8line.push_back(0); + + return std::string(&utf8line[0]); + } + + // ------------------------------------------------------------------------ + + irr::core::stringw utf8_to_wide(const char* input) + { + static std::vector utf16line; + utf16line.clear(); + + utf8::utf8to16(input, input + strlen(input), back_inserter(utf16line)); + utf16line.push_back(0); + + return irr::core::stringw(&utf16line[0]); + } + // ------------------------------------------------------------------------ /** Converts a version string (in the form of 'X.Y.Za-rcU' into an * integer number. diff --git a/src/utils/string_utils.hpp b/src/utils/string_utils.hpp index ebcc28895..95128c45d 100644 --- a/src/utils/string_utils.hpp +++ b/src/utils/string_utils.hpp @@ -259,7 +259,7 @@ namespace StringUtils all_vals.push_back(irr::core::stringw(std::forward(v))); __Fill(all_vals, std::forward(args)...); } - + static void __Fill(std::vector&) {} }; @@ -363,6 +363,11 @@ namespace StringUtils return parseString(input.c_str(), output); } // parseString + // ------------------------------------------------------------------------ + + std::string wide_to_utf8(const wchar_t* input); + irr::core::stringw utf8_to_wide(const char* input); + } // namespace StringUtils #endif diff --git a/src/utils/translation.cpp b/src/utils/translation.cpp index da9139bbb..6d45faa73 100644 --- a/src/utils/translation.cpp +++ b/src/utils/translation.cpp @@ -42,7 +42,6 @@ #include "io/file_manager.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" -#include "utils/utf8.h" // set to 1 to debug i18n @@ -71,29 +70,6 @@ const LanguageList* Translations::getLanguageList() const return &g_language_list; } - -char* wide_to_utf8(const wchar_t* input) -{ - static std::vector utf8line; - utf8line.clear(); - - utf8::utf16to8(input, input + wcslen(input), back_inserter(utf8line)); - utf8line.push_back(0); - - return &utf8line[0]; -} - -wchar_t* utf8_to_wide(const char* input) -{ - static std::vector utf16line; - utf16line.clear(); - - utf8::utf8to16(input, input + strlen(input), back_inserter(utf16line)); - utf16line.push_back(0); - - return &utf16line[0]; -} - // ---------------------------------------------------------------------------- /** Frees the memory allocated for the result of toFribidiChar(). */ void freeFribidiChar(FriBidiChar *str) @@ -439,7 +415,8 @@ bool Translations::isRTLText(const wchar_t *in_ptr) */ const wchar_t* Translations::w_gettext(const wchar_t* original, const char* context) { - return w_gettext( wide_to_utf8(original), context ); + std::string in = StringUtils::wide_to_utf8(original); + return w_gettext(in.c_str(), context); } /** @@ -461,7 +438,7 @@ const wchar_t* Translations::w_gettext(const char* original, const char* context if (original_t == original) { - m_converted_string = utf8_to_wide(original); + m_converted_string = StringUtils::utf8_to_wide(original); #if TRANSLATE_VERBOSE std::wcout << L" translation : " << m_converted_string << std::endl; @@ -472,9 +449,10 @@ const wchar_t* Translations::w_gettext(const char* original, const char* context // print //for (int n=0;; n+=4) - wchar_t* original_tw = utf8_to_wide(original_t.c_str()); + static core::stringw original_tw; + original_tw = StringUtils::utf8_to_wide(original_t.c_str()); - wchar_t* out_ptr = original_tw; + const wchar_t* out_ptr = original_tw.c_str(); if (REMOVE_BOM) out_ptr++; #if TRANSLATE_VERBOSE @@ -493,7 +471,9 @@ const wchar_t* Translations::w_gettext(const char* original, const char* context */ const wchar_t* Translations::w_ngettext(const wchar_t* singular, const wchar_t* plural, int num, const char* context) { - return w_ngettext( wide_to_utf8(singular), wide_to_utf8(plural), num, context); + std::string in = StringUtils::wide_to_utf8(singular); + std::string in2 = StringUtils::wide_to_utf8(plural); + return w_ngettext(in.c_str(), in2.c_str(), num, context); } /** @@ -509,7 +489,9 @@ const wchar_t* Translations::w_ngettext(const char* singular, const char* plural m_dictionary.translate_plural(singular, plural, num) : m_dictionary.translate_ctxt_plural(context, singular, plural, num)); - wchar_t* out_ptr = utf8_to_wide(res.c_str()); + static core::stringw str_buffer; + str_buffer = StringUtils::utf8_to_wide(res.c_str()); + const wchar_t* out_ptr = str_buffer.c_str(); if (REMOVE_BOM) out_ptr++; #if TRANSLATE_VERBOSE From 6e0b96c21f3b2382ba64429671d7b759598cca3d Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sat, 25 Apr 2015 16:01:50 +0200 Subject: [PATCH 03/44] Fix TEST_BIDI option --- src/utils/translation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/translation.cpp b/src/utils/translation.cpp index 6197baaf9..c3b8655f6 100644 --- a/src/utils/translation.cpp +++ b/src/utils/translation.cpp @@ -147,9 +147,10 @@ FriBidiChar* toFribidiChar(const wchar_t* str) // Prepend a character that forces RTL style FriBidiChar *tmp = result; result = new FriBidiChar[++length + 1]; - std::memcpy(result + 1, tmp, length * sizeof(FriBidiChar)); + memcpy(result + 1, tmp, length * sizeof(FriBidiChar)); result[0] = L'\u202E'; - freeFribidiChar(tmp); + if (sizeof(wchar_t) != sizeof(FriBidiChar)) + delete[] tmp; #endif return result; From 56aecdb6929687e25e85b677f16c0907ca011655 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sat, 25 Apr 2015 16:10:54 +0200 Subject: [PATCH 04/44] Fix achievements RTL display --- src/achievements/achievement_info.hpp | 2 +- src/states_screens/online_profile_achievements.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/achievements/achievement_info.hpp b/src/achievements/achievement_info.hpp index d79caa2d0..fa3e3d24c 100644 --- a/src/achievements/achievement_info.hpp +++ b/src/achievements/achievement_info.hpp @@ -113,7 +113,7 @@ public: irr::core::stringw getDescription() const { return _(m_description.c_str()); } // ------------------------------------------------------------------------ /** Returns the name of this achievement. */ - irr::core::stringw getName() const { return _(m_name.c_str()); } + irr::core::stringw getName() const { return _LTR(m_name.c_str()); } // ------------------------------------------------------------------------ bool needsResetAfterRace() const { return m_reset_type == AFTER_RACE; } // ------------------------------------------------------------------------ diff --git a/src/states_screens/online_profile_achievements.cpp b/src/states_screens/online_profile_achievements.cpp index 0cddada26..7e660ca5f 100644 --- a/src/states_screens/online_profile_achievements.cpp +++ b/src/states_screens/online_profile_achievements.cpp @@ -107,7 +107,7 @@ void BaseOnlineProfileAchievements::init() const Achievement *a = it->second; if(a->getInfo()->isSecret() && !a->isAchieved()) continue; - ListWidget::ListCell title(a->getInfo()->getName(), -1, 2); + ListWidget::ListCell title(translations->fribidize(a->getInfo()->getName()), -1, 2); ListWidget::ListCell progress(a->getProgressAsString(), -1, 1); row.push_back(title); row.push_back(progress); From fae12f3714472092152d08f221e22b56bce90478 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sun, 26 Apr 2015 22:29:59 +0200 Subject: [PATCH 05/44] Fix RTL texts with multiple lines --- src/utils/translation.cpp | 134 +++++++++++++++++++++++++------------- src/utils/translation.hpp | 3 + 2 files changed, 92 insertions(+), 45 deletions(-) diff --git a/src/utils/translation.cpp b/src/utils/translation.cpp index c3b8655f6..f6d7981da 100644 --- a/src/utils/translation.cpp +++ b/src/utils/translation.cpp @@ -144,11 +144,28 @@ FriBidiChar* toFribidiChar(const wchar_t* str) } #ifdef TEST_BIDI - // Prepend a character that forces RTL style + // Prepend a character in each line that forces RTL style + int lines = 1; + for (std::size_t i = 0; i <= length; i++) + { + if (str[i] == L'\n') + lines++; + } FriBidiChar *tmp = result; - result = new FriBidiChar[++length + 1]; - memcpy(result + 1, tmp, length * sizeof(FriBidiChar)); + length += lines; + result = new FriBidiChar[length + 1]; + lines = 1; result[0] = L'\u202E'; + for (std::size_t i = 1; i <= length; i++) + { + result[i] = tmp[i - lines]; + if (str[i - lines] == L'\n') + { + lines++; + i++; + result[i] = L'\u202E'; + } + } if (sizeof(wchar_t) != sizeof(FriBidiChar)) delete[] tmp; #endif @@ -367,54 +384,31 @@ Translations::Translations() //: m_dictionary_manager("UTF-16") const wchar_t* Translations::fribidize(const wchar_t* in_ptr) { -#if ENABLE_BIDI - if(this->isRTLLanguage()) + if (isRTLText(in_ptr)) { - FriBidiChar *fribidiInput = toFribidiChar(in_ptr); - std::size_t length = 0; - while (fribidiInput[length]) - length++; + // Split text into lines + std::vector input_lines = StringUtils::split(in_ptr, '\n'); + // Reverse lines for RTL strings, irrlicht will reverse them back + // This is needed because irrlicht inserts line breaks itself if a text + // is too long for one line and then reverses the lines again. + std::reverse(input_lines.begin(), input_lines.end()); - // Assume right to left as start direction. -#if FRIBIDI_MINOR_VERSION==10 - // While the doc for older fribidi versions is somewhat sparse, - // using the RIGHT-TO-LEFT EMBEDDING character here appears to - // work correct. - FriBidiCharType pbase_dir = L'\u202B'; -#else - FriBidiCharType pbase_dir = FRIBIDI_PAR_ON; -#endif - - FriBidiChar *fribidiOutput = new FriBidiChar[length + 1]; - memset(fribidiOutput, 0, (length + 1) * sizeof(FriBidiChar)); - fribidi_boolean result = fribidi_log2vis(fribidiInput, - length, - &pbase_dir, - fribidiOutput, - /* gint *position_L_to_V_list */ NULL, - /* gint *position_V_to_L_list */ NULL, - /* gint8 *embedding_level_list */ NULL - ); - - freeFribidiChar(fribidiInput); - - if (!result) + // Fribidize and concat lines + for (std::vector::iterator it = input_lines.begin(); + it != input_lines.end(); it++) { - delete[] fribidiOutput; - Log::error("Translations::fribidize", "Fribidi failed in 'fribidi_log2vis' =("); - m_converted_string = core::stringw(in_ptr); - return m_converted_string.c_str(); + if (it == input_lines.begin()) + m_converted_string = fribidizeLine(*it); + else + { + m_converted_string += "\n"; + m_converted_string += fribidizeLine(*it); + } } - - wchar_t *convertedString = fromFribidiChar(fribidiOutput); - m_converted_string = core::stringw(convertedString); - freeFribidiChar(convertedString); - delete[] fribidiOutput; return m_converted_string.c_str(); } - -#endif // ENABLE_BIDI - return in_ptr; + else + return in_ptr; } bool Translations::isRTLText(const wchar_t *in_ptr) @@ -546,3 +540,53 @@ std::string Translations::getCurrentLanguageName() //return m_dictionary_manager.get_language().get_name(); } +core::stringw Translations::fribidizeLine(const core::stringw &str) +{ +#if ENABLE_BIDI + FriBidiChar *fribidiInput = toFribidiChar(str.c_str()); + std::size_t length = 0; + while (fribidiInput[length]) + length++; + + // Assume right to left as start direction. +#if FRIBIDI_MINOR_VERSION==10 + // While the doc for older fribidi versions is somewhat sparse, + // using the RIGHT-TO-LEFT EMBEDDING character here appears to + // work correct. + FriBidiCharType pbase_dir = L'\u202B'; +#else + FriBidiCharType pbase_dir = FRIBIDI_PAR_ON; +#endif + + // Reverse text line by line + FriBidiChar *fribidiOutput = new FriBidiChar[length + 1]; + memset(fribidiOutput, 0, (length + 1) * sizeof(FriBidiChar)); + fribidi_boolean result = fribidi_log2vis(fribidiInput, + length, + &pbase_dir, + fribidiOutput, + /* gint *position_L_to_V_list */ NULL, + /* gint *position_V_to_L_list */ NULL, + /* gint8 *embedding_level_list */ NULL + ); + + freeFribidiChar(fribidiInput); + + if (!result) + { + delete[] fribidiOutput; + Log::error("Translations::fribidize", "Fribidi failed in 'fribidi_log2vis' =("); + return core::stringw(str); + } + + wchar_t *convertedString = fromFribidiChar(fribidiOutput); + core::stringw converted_string(convertedString); + freeFribidiChar(convertedString); + delete[] fribidiOutput; + return converted_string; + +#else + return core::stringw(str); +#endif // ENABLE_BIDI +} + diff --git a/src/utils/translation.hpp b/src/utils/translation.hpp index eeeec161b..23d589bad 100644 --- a/src/utils/translation.hpp +++ b/src/utils/translation.hpp @@ -70,6 +70,9 @@ public: const std::vector* getLanguageList() const; std::string getCurrentLanguageName(); + +private: + irr::core::stringw fribidizeLine(const irr::core::stringw &str); }; // Translations From 0b9876c53ec7cb08b850e511dde1466b04117881 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sun, 26 Apr 2015 22:55:28 +0200 Subject: [PATCH 06/44] Use isRTLText in the BubbleWidget --- src/guiengine/widgets/bubble_widget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guiengine/widgets/bubble_widget.cpp b/src/guiengine/widgets/bubble_widget.cpp index 65fec72a1..0e51478d4 100644 --- a/src/guiengine/widgets/bubble_widget.cpp +++ b/src/guiengine/widgets/bubble_widget.cpp @@ -74,7 +74,7 @@ void BubbleWidget::replaceText() EGUI_ALIGNMENT align = EGUIA_UPPERLEFT; if (m_properties[PROP_TEXT_ALIGN] == "center") align = EGUIA_CENTER; else if (m_properties[PROP_TEXT_ALIGN] == "right") align = EGUIA_LOWERRIGHT; - else if (translations->isRTLLanguage()) align = EGUIA_LOWERRIGHT; + else if (translations->isRTLText(message)) align = EGUIA_LOWERRIGHT; EGUI_ALIGNMENT valign = EGUIA_CENTER ; //TODO: make label v-align configurable through XML file? @@ -90,7 +90,7 @@ void BubbleWidget::replaceText() m_expanded_size.LowerRightCorner.Y += additionalNeededSize/2 + 10; // reduce text to fit in the available space if it's too long - if (translations->isRTLLanguage()) + if (translations->isRTLText(message)) { while (text_height > m_shrinked_size.getHeight() && message.size() > 10) { From af88b4a16f7a8930cd371715af6d1451f25a8d65 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sun, 26 Apr 2015 23:31:57 +0200 Subject: [PATCH 07/44] Fix translation issues with grand-prixs --- src/race/grand_prix_data.hpp | 2 +- src/states_screens/grand_prix_editor_screen.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/race/grand_prix_data.hpp b/src/race/grand_prix_data.hpp index b720a7d3a..a788aed53 100644 --- a/src/race/grand_prix_data.hpp +++ b/src/race/grand_prix_data.hpp @@ -148,7 +148,7 @@ public: // ------------------------------------------------------------------------ /** @return the (potentially translated) user-visible name of the Grand * Prix (apply fribidi as needed) */ - irr::core::stringw getName() const { return _LTR(m_name.c_str()); } + irr::core::stringw getName() const { return m_editable ? m_name.c_str() : _LTR(m_name.c_str()); } // ------------------------------------------------------------------------ /** @return the internal indentifier of the Grand Prix (not translated) */ diff --git a/src/states_screens/grand_prix_editor_screen.cpp b/src/states_screens/grand_prix_editor_screen.cpp index 3366057df..8a666a11b 100644 --- a/src/states_screens/grand_prix_editor_screen.cpp +++ b/src/states_screens/grand_prix_editor_screen.cpp @@ -161,7 +161,7 @@ void GrandPrixEditorScreen::setSelection (const GrandPrixData* gpdata) if (gpdata == NULL) { m_selection = NULL; - gpname_widget->setText (L"Please select a Grand Prix", true); + gpname_widget->setText (_("Please select a Grand Prix"), true); tracks_widget->clearItems(); tracks_widget->updateItemDisplay(); } From 5462a62c75c99ba9425544bb245adf7f033695b0 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Mon, 27 Apr 2015 00:13:23 +0200 Subject: [PATCH 08/44] Fix TEST_BIDI in multiplayer KartSelection --- src/guiengine/widgets/player_kart_widget.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/guiengine/widgets/player_kart_widget.cpp b/src/guiengine/widgets/player_kart_widget.cpp index d9d0a6115..062400dba 100644 --- a/src/guiengine/widgets/player_kart_widget.cpp +++ b/src/guiengine/widgets/player_kart_widget.cpp @@ -344,6 +344,7 @@ void PlayerKartWidget::add() name = m_associated_player->getProfile()->getName(); if (m_associated_user) name = m_associated_user->getUserName(); + core::stringw label = translations->fribidize(name); if (m_parent_screen->m_multiplayer) { @@ -357,21 +358,21 @@ void PlayerKartWidget::add() { // I18N: 'handicapped' indicates that per-player handicaps are // activated for this kart (i.e. it will drive slower) - label = _("%s (handicapped)", label); + label = _("%s (handicapped)", name); m_player_ident_spinner->addLabel(label); } } // select the right player profile in the spinner - m_player_ident_spinner->setValue(name); + m_player_ident_spinner->setValue(label); } else { - m_player_ident_spinner->addLabel(name); + m_player_ident_spinner->addLabel(label); m_player_ident_spinner->setVisible(false); } - assert(m_player_ident_spinner->getStringValue() == name); + assert(m_player_ident_spinner->getStringValue() == label); } // add // ------------------------------------------------------------------------ From d19112760f2ae2bdf5ca793c6d0392cb4593ab5f Mon Sep 17 00:00:00 2001 From: Flakebi Date: Mon, 27 Apr 2015 00:14:52 +0200 Subject: [PATCH 09/44] Include algorithm in translation.cpp --- src/utils/translation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/translation.cpp b/src/utils/translation.cpp index f6d7981da..4a58be123 100644 --- a/src/utils/translation.cpp +++ b/src/utils/translation.cpp @@ -25,6 +25,7 @@ #include "utils/translation.hpp" +#include #include #include #include From eacd599b9374ca8cb7c89f2d3ba1fa080162ceed Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 27 Apr 2015 09:04:16 +1000 Subject: [PATCH 10/44] Removed version number warning for Radeon cards on osx (since they have completely different version numbers). --- data/graphical_restrictions.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data/graphical_restrictions.xml b/data/graphical_restrictions.xml index e742452dc..16bb60729 100644 --- a/data/graphical_restrictions.xml +++ b/data/graphical_restrictions.xml @@ -17,6 +17,9 @@ - + + + From dfcaf44058951b7833d13f2e218a4b3cd95558a2 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 26 Apr 2015 19:06:51 -0400 Subject: [PATCH 11/44] Scripting work --- src/scriptengine/script_engine.cpp | 21 +- src/scriptengine/script_gui.cpp | 214 +++++++++++++++++++++ src/scriptengine/script_gui.hpp | 38 ++++ src/scriptengine/script_kart.cpp | 1 + src/scriptengine/script_physics.cpp | 1 + src/scriptengine/script_track.cpp | 33 +--- src/scriptengine/script_track.hpp | 6 - src/scriptengine/scriptstdstring_utils.cpp | 153 +++------------ src/tracks/track_object_presentation.cpp | 117 ----------- 9 files changed, 298 insertions(+), 286 deletions(-) create mode 100644 src/scriptengine/script_gui.cpp create mode 100644 src/scriptengine/script_gui.hpp diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 646bd6b28..35efa63e5 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -16,16 +16,17 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include // assert() +#include #include #include "io/file_manager.hpp" -#include // cout #include "karts/kart.hpp" #include "modes/world.hpp" -#include "script_engine.hpp" -#include "scriptstdstring.hpp" -#include "scriptvec3.hpp" -#include // strstr() +#include "scriptengine/script_engine.hpp" +#include "scriptengine/script_gui.hpp" +#include "scriptengine/script_track.hpp" +#include "scriptengine/scriptstdstring.hpp" +#include "scriptengine/scriptvec3.hpp" +#include #include "states_screens/dialogs/tutorial_message_dialog.hpp" #include "tracks/track_object_manager.hpp" #include "tracks/track.hpp" @@ -255,7 +256,8 @@ void ScriptEngine::cleanupCache() { for (auto curr : m_script_cache) { - curr.second->Release(); + if (curr.second != NULL) + curr.second->Release(); } m_script_cache.clear(); } @@ -268,17 +270,16 @@ void ScriptEngine::configureEngine(asIScriptEngine *engine) { // Register the script string type RegisterStdString(engine); //register std::string - RegisterStdStringUtils(engine); RegisterVec3(engine); //register Vec3 Scripting::Track::registerScriptFunctions(m_engine); - Scripting::Track::registerScriptEnums(m_engine); - Scripting::Kart::registerScriptFunctions(m_engine); Scripting::Physics::registerScriptFunctions(m_engine); + Scripting::GUI::registerScriptFunctions(m_engine); + Scripting::GUI::registerScriptEnums(m_engine); // It is possible to register the functions, properties, and types in // configuration groups as well. When compiling the scripts it can then diff --git a/src/scriptengine/script_gui.cpp b/src/scriptengine/script_gui.cpp new file mode 100644 index 000000000..62ccc2d02 --- /dev/null +++ b/src/scriptengine/script_gui.cpp @@ -0,0 +1,214 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014-2015 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "script_track.hpp" + +#include "animations/three_d_animation.hpp" +#include "input/device_manager.hpp" +#include "input/input_device.hpp" +#include "input/input_manager.hpp" +#include "modes/world.hpp" +#include "states_screens/dialogs/tutorial_message_dialog.hpp" +#include "tracks/track.hpp" +#include "tracks/track_object.hpp" +#include "tracks/track_object_manager.hpp" + +#include +#include "scriptarray.hpp" + +#include +#include //debug + +namespace Scripting +{ + namespace GUI + { + //getter for key binding for player action enums + void getKeyBinding(asIScriptGeneric *gen) + { + int Enum_value = (int)gen->GetArgDWord(0); + InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); + DeviceConfig* config = device->getConfiguration(); + irr::core::stringw control; + PlayerAction ScriptAction = (PlayerAction)Enum_value; + control = config->getBindingAsString(ScriptAction); + std::string key = std::string(irr::core::stringc(control).c_str()); + void *key_pointer = &key; + gen->SetReturnObject(key_pointer); + } + + // Displays the message specified in displayMessage( string message ) within the script + void displayMessage(asIScriptGeneric *gen) + { + std::string *input = (std::string*)gen->GetArgAddress(0); + irr::core::stringw out = StringUtils::utf8_to_wide(input->c_str()); + new TutorialMessageDialog((out), true); + } + + + void translate(asIScriptGeneric *gen) + { + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + + irr::core::stringw out = translations->fribidize(translations->w_gettext(input->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + } + + void insertValues1(asIScriptGeneric *gen) + { + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), + StringUtils::utf8_to_wide(arg1->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + } + + void insertValues2(asIScriptGeneric *gen) + { + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + std::string *arg2 = (std::string*)gen->GetArgAddress(2); + + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + } + + void insertValues3(asIScriptGeneric *gen) + { + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + std::string *arg2 = (std::string*)gen->GetArgAddress(2); + std::string *arg3 = (std::string*)gen->GetArgAddress(3); + + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str()), + StringUtils::utf8_to_wide(arg3->c_str())); + + // Return the string + new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + } + + void translateAndInsertValues1(asIScriptGeneric *gen) + { + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + + irr::core::stringw out = translations->w_gettext(input->c_str()); + + out = StringUtils::insertValues(out, + StringUtils::utf8_to_wide(arg1->c_str())); + + out = translations->fribidize(out); + + // Return the string + new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + } + + void translateAndInsertValues2(asIScriptGeneric *gen) + { + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + std::string *arg2 = (std::string*)gen->GetArgAddress(2); + + irr::core::stringw out = translations->w_gettext(input->c_str()); + + out = StringUtils::insertValues(out, + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str())); + + out = translations->fribidize(out); + + // Return the string + new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + } + + void translateAndInsertValues3(asIScriptGeneric *gen) + { + // Get the arguments + std::string *input = (std::string*)gen->GetArgAddress(0); + std::string *arg1 = (std::string*)gen->GetArgAddress(1); + std::string *arg2 = (std::string*)gen->GetArgAddress(2); + std::string *arg3 = (std::string*)gen->GetArgAddress(3); + + irr::core::stringw out = translations->w_gettext(input->c_str()); + + out = StringUtils::insertValues(out, + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str()), + StringUtils::utf8_to_wide(arg3->c_str())); + + out = translations->fribidize(out); + + // Return the string + new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + } + + void registerScriptFunctions(asIScriptEngine *engine) + { + int r; // of type asERetCodes + engine->SetDefaultNamespace("GUI"); + r = engine->RegisterGlobalFunction("void displayMessage(string &in)", asFUNCTION(displayMessage), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string getKeyBinding(int input)", asFUNCTION(getKeyBinding), asCALL_GENERIC); assert(r >= 0); + + r = engine->RegisterGlobalFunction("string translate(const string &in)", asFUNCTION(translate), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in)", asFUNCTION(translateAndInsertValues1), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in, const string &in)", asFUNCTION(translateAndInsertValues2), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(translateAndInsertValues3), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in)", asFUNCTION(insertValues1), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(insertValues2), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(insertValues3), asCALL_GENERIC); assert(r >= 0); + } + + void registerScriptEnums(asIScriptEngine *engine) + { + engine->RegisterEnum("PA"); + engine->RegisterEnumValue("PA", "STEER_LEFT", PA_STEER_LEFT); + engine->RegisterEnumValue("PA", "STEER_RIGHT", PA_STEER_RIGHT); + engine->RegisterEnumValue("PA", "ACCEL", PA_ACCEL); + engine->RegisterEnumValue("PA", "BRAKE", PA_BRAKE); + engine->RegisterEnumValue("PA", "NITRO", PA_NITRO); + engine->RegisterEnumValue("PA", "DRIFT", PA_DRIFT); + engine->RegisterEnumValue("PA", "RESCUE", PA_RESCUE); + engine->RegisterEnumValue("PA", "FIRE", PA_FIRE); + engine->RegisterEnumValue("PA", "LOOK_BACK", PA_LOOK_BACK); + engine->RegisterEnumValue("PA", "PAUSE_RACE", PA_PAUSE_RACE); + engine->RegisterEnumValue("PA", "MENU_UP", PA_MENU_UP); + engine->RegisterEnumValue("PA", "MENU_DOWN", PA_MENU_DOWN); + engine->RegisterEnumValue("PA", "MENU_LEFT", PA_MENU_LEFT); + engine->RegisterEnumValue("PA", "MENU_RIGHT", PA_MENU_RIGHT); + engine->RegisterEnumValue("PA", "MENU_SELECT", PA_MENU_SELECT); + engine->RegisterEnumValue("PA", "MENU_CANCEL", PA_MENU_CANCEL); + } + } +} diff --git a/src/scriptengine/script_gui.hpp b/src/scriptengine/script_gui.hpp new file mode 100644 index 000000000..f5d5434f6 --- /dev/null +++ b/src/scriptengine/script_gui.hpp @@ -0,0 +1,38 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014-2015 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_SCRIPT_GUI_HPP +#define HEADER_SCRIPT_GUI_HPP + +#include + +#include + +namespace Scripting +{ + namespace GUI + { + //script engine functions + void registerScriptFunctions(asIScriptEngine *engine); + asIScriptFunction* + registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName); + void registerScriptEnums(asIScriptEngine *engine); + } + +} +#endif diff --git a/src/scriptengine/script_kart.cpp b/src/scriptengine/script_kart.cpp index 458168e4f..8642fbf00 100644 --- a/src/scriptengine/script_kart.cpp +++ b/src/scriptengine/script_kart.cpp @@ -112,6 +112,7 @@ namespace Scripting void registerScriptFunctions(asIScriptEngine *engine) { int r; + engine->SetDefaultNamespace("Karts"); r = engine->RegisterGlobalFunction("void squashKart(int id, float time)", asFUNCTION(squashKart), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void teleportKart(int id, Vec3 &in)", asFUNCTION(teleportKart), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void setVelocity(int id, Vec3 &in)", asFUNCTION(setVelocity), asCALL_GENERIC); assert(r >= 0); diff --git a/src/scriptengine/script_physics.cpp b/src/scriptengine/script_physics.cpp index 7f33d7617..2dfabf7b4 100644 --- a/src/scriptengine/script_physics.cpp +++ b/src/scriptengine/script_physics.cpp @@ -82,6 +82,7 @@ namespace Scripting void registerScriptFunctions(asIScriptEngine *engine) { int r; + engine->SetDefaultNamespace("Physics"); r = engine->RegisterGlobalFunction("uint getCollidingKart1()", asFUNCTION(getCollidingKart1), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterGlobalFunction("uint getCollidingKart2()", asFUNCTION(getCollidingKart2), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterGlobalFunction("string getCollisionType()", asFUNCTION(getCollisionType), asCALL_GENERIC); assert(r >= 0); diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index 41d6702ee..3b95f39af 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -207,15 +207,13 @@ namespace Scripting { int r; - r = engine->RegisterGlobalFunction("void displayMessage(string &in)", asFUNCTION(displayMessage), asCALL_GENERIC); assert(r >= 0); + engine->SetDefaultNamespace("Track"); r = engine->RegisterGlobalFunction("void disable(string &in)", asFUNCTION(disableTrackObject), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void enable(string &in)", asFUNCTION(enableTrackObject), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void enableTrigger(string &in)", asFUNCTION(enableTrigger), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void disableTrigger(string &in)", asFUNCTION(disableTrigger), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void createTrigger(string &in,Vec3 &in, float distance)", asFUNCTION(createTrigger), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string getKeyBinding(int input)", asFUNCTION(getKeyBinding), asCALL_GENERIC); assert(r >= 0); - /* //Test singleton, and various calling conventions @@ -275,35 +273,6 @@ namespace Scripting r = engine->RegisterObjectMethod("Animator", "void setPaused(bool mode)", asFUNCTION( setPaused ), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterGlobalFunction("void runScript(string &in)", asFUNCTION(runScript), asCALL_GENERIC); assert(r >= 0); - } - - void registerScriptEnums(asIScriptEngine *engine) - { - - engine->RegisterEnum("PA"); - engine->RegisterEnumValue("PA", "STEER_LEFT", PA_STEER_LEFT); - engine->RegisterEnumValue("PA", "STEER_RIGHT", PA_STEER_RIGHT); - engine->RegisterEnumValue("PA", "ACCEL", PA_ACCEL); - engine->RegisterEnumValue("PA", "BRAKE", PA_BRAKE); - engine->RegisterEnumValue("PA", "NITRO", PA_NITRO); - engine->RegisterEnumValue("PA", "DRIFT", PA_DRIFT); - engine->RegisterEnumValue("PA", "RESCUE", PA_RESCUE); - engine->RegisterEnumValue("PA", "FIRE", PA_FIRE); - engine->RegisterEnumValue("PA", "LOOK_BACK", PA_LOOK_BACK); - engine->RegisterEnumValue("PA", "PAUSE_RACE", PA_PAUSE_RACE); - engine->RegisterEnumValue("PA", "MENU_UP", PA_MENU_UP); - engine->RegisterEnumValue("PA", "MENU_DOWN", PA_MENU_DOWN); - engine->RegisterEnumValue("PA", "MENU_LEFT", PA_MENU_LEFT); - engine->RegisterEnumValue("PA", "MENU_RIGHT", PA_MENU_RIGHT); - engine->RegisterEnumValue("PA", "MENU_SELECT", PA_MENU_SELECT); - engine->RegisterEnumValue("PA", "MENU_CANCEL", PA_MENU_CANCEL); - - } - - - - - } } diff --git a/src/scriptengine/script_track.hpp b/src/scriptengine/script_track.hpp index ddea74408..9b2b77c70 100644 --- a/src/scriptengine/script_track.hpp +++ b/src/scriptengine/script_track.hpp @@ -25,25 +25,19 @@ namespace Scripting { - namespace Track { - //script engine functions void registerScriptFunctions(asIScriptEngine *engine); asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine , std::string scriptName); - void registerScriptEnums(asIScriptEngine *engine); - //script-bound functions - void displayMessage(asIScriptGeneric *gen); void disableAnimation(asIScriptGeneric *gen); void enableAnimation(asIScriptGeneric *gen); void enableTrigger(asIScriptGeneric *gen); void disableTrigger(asIScriptGeneric *gen); void createTrigger(asIScriptGeneric *gen); - } } diff --git a/src/scriptengine/scriptstdstring_utils.cpp b/src/scriptengine/scriptstdstring_utils.cpp index 1436e8479..fe8986b31 100644 --- a/src/scriptengine/scriptstdstring_utils.cpp +++ b/src/scriptengine/scriptstdstring_utils.cpp @@ -20,7 +20,6 @@ #include "scriptarray.hpp" #include #include -#include "utils/translation.hpp" using namespace std; @@ -45,19 +44,19 @@ static CScriptArray *StringSplit(const string &delim, const string &str) asIScriptEngine *engine = ctx->GetEngine(); // TODO: This should only be done once - // TODO: This assumes that CScriptArray was already registered - asIObjectType *arrayType = engine->GetObjectTypeById(engine->GetTypeIdByDecl("array")); + // TODO: This assumes that CScriptArray was already registered + asIObjectType *arrayType = engine->GetObjectTypeById(engine->GetTypeIdByDecl("array")); // Create the array object CScriptArray *array = new CScriptArray(0, arrayType); // Find the existence of the delimiter in the input string int pos = 0, prev = 0, count = 0; - while( (pos = (int)str.find(delim, prev)) != (int)string::npos ) + while ((pos = (int)str.find(delim, prev)) != (int)string::npos) { // Add the part to the array - array->Resize(array->GetSize()+1); - ((string*)array->At(count))->assign(&str[prev], pos-prev); + array->Resize(array->GetSize() + 1); + ((string*)array->At(count))->assign(&str[prev], pos - prev); // Find the next part count++; @@ -65,16 +64,16 @@ static CScriptArray *StringSplit(const string &delim, const string &str) } // Add the remaining part - array->Resize(array->GetSize()+1); + array->Resize(array->GetSize() + 1); ((string*)array->At(count))->assign(&str[prev]); - return array; + return array; } static void StringSplit_Generic(asIScriptGeneric *gen) { // Get the arguments - string *str = (string*)gen->GetObject(); + string *str = (string*)gen->GetObject(); string *delim = *(string**)gen->GetAddressOfArg(0); // Return the array by handle @@ -100,20 +99,20 @@ static string StringJoin(const CScriptArray &array, const string &delim) { // Create the new string string str = ""; - if( array.GetSize() ) - { - int n; - for( n = 0; n < (int)array.GetSize() - 1; n++ ) - { - str += *(string*)array.At(n); - str += delim; - } + if (array.GetSize()) + { + int n; + for (n = 0; n < (int)array.GetSize() - 1; n++) + { + str += *(string*)array.At(n); + str += delim; + } - // Add the last part - str += *(string*)array.At(n); - } + // Add the last part + str += *(string*)array.At(n); + } - return str; + return str; } static void StringJoin_Generic(asIScriptGeneric *gen) @@ -126,110 +125,22 @@ static void StringJoin_Generic(asIScriptGeneric *gen) new(gen->GetAddressOfReturnLocation()) string(StringJoin(*array, *delim)); } -void translate(asIScriptGeneric *gen) -{ - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - - irr::core::stringw out = translations->fribidize(translations->w_gettext(input->c_str())); - - // Return the string - new(gen->GetAddressOfReturnLocation()) string(StringUtils::wide_to_utf8(out.c_str())); -} - -void insertValues1(asIScriptGeneric *gen) -{ - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - - irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), - StringUtils::utf8_to_wide(arg1->c_str())); - - // Return the string - new(gen->GetAddressOfReturnLocation()) string(StringUtils::wide_to_utf8(out.c_str())); -} - -void insertValues2(asIScriptGeneric *gen) -{ - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - std::string *arg2 = (std::string*)gen->GetArgAddress(2); - - irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), - StringUtils::utf8_to_wide(arg1->c_str()), - StringUtils::utf8_to_wide(arg2->c_str())); - - // Return the string - new(gen->GetAddressOfReturnLocation()) string(StringUtils::wide_to_utf8(out.c_str())); -} - -void insertValues3(asIScriptGeneric *gen) -{ - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - std::string *arg2 = (std::string*)gen->GetArgAddress(2); - std::string *arg3 = (std::string*)gen->GetArgAddress(3); - - irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), - StringUtils::utf8_to_wide(arg1->c_str()), - StringUtils::utf8_to_wide(arg2->c_str()), - StringUtils::utf8_to_wide(arg3->c_str())); - - // Return the string - new(gen->GetAddressOfReturnLocation()) string(StringUtils::wide_to_utf8(out.c_str())); -} - -/* -void insertValues(asIScriptGeneric *gen) -{ - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - CScriptArray *array = *(CScriptArray**)gen->GetAddressOfArg(1); - - int size = array->GetSize(); - vector all_values; - for (int i = 0; i < size; i++) - { - string* curr = (string*)array->At(i); - all_values.push_back(curr); - } - - StringUtils::insertValues(*input, all_values); - - irr::core::stringw out = translations->fribidize(translations->w_gettext(input->c_str())); - - // Return the string - new(gen->GetAddressOfReturnLocation()) string(irr::core::stringc(out).c_str()); -} -*/ - // This is where the utility functions are registered. // The string type must have been registered first. void RegisterStdStringUtils(asIScriptEngine *engine) { - int r; + int r; - //if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")) - //{ - //r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0); - //r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string translate(const string &in)", asFUNCTION(translate), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in)", asFUNCTION(insertValues1), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(insertValues2), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(insertValues3), asCALL_GENERIC); assert(r >= 0); - //} - //else - //{ - // //r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0); - // //r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0); - // r = engine->RegisterGlobalFunction("string translate(const string &in)", asFUNCTION(translate), asCALL_CDECL); assert(r >= 0); - // r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &arg1)", asFUNCTION(insertValues1), asCALL_CDECL); assert(r >= 0); - // r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &arg1, const string &arg2)", asFUNCTION(insertValues2), asCALL_CDECL); assert(r >= 0); - // r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &arg1, const string &arg2, const string &arg3)", asFUNCTION(insertValues3), asCALL_CDECL); assert(r >= 0); - //} + if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")) + { + r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0); + } + else + { + r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0); + } } -END_AS_NAMESPACE +END_AS_NAMESPACE \ No newline at end of file diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 30ca9a2d0..308ecc17f 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -921,123 +921,6 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) _("Complete all challenges to unlock the big door!"), true); } } - /* - else if (m_action == "tutorial_drive") - { - //if (World::getWorld()->getPhase() == World::RACE_PHASE) - { - m_action_active = false; - //World::getWorld()->getRaceGUI()->clearAllMessages(); - - InputDevice* device = input_manager->getDeviceManager() - ->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw accel = config->getBindingAsString(PA_ACCEL); - irr::core::stringw left = config->getBindingAsString(PA_STEER_LEFT); - irr::core::stringw right = config->getBindingAsString(PA_STEER_RIGHT); - - new TutorialMessageDialog(_("Accelerate with <%s> and steer with " - "<%s> and <%s>", accel, left, right), - false); - } - } - else if (m_action == "tutorial_bananas") - { - m_action_active = false; - - new TutorialMessageDialog(_("Avoid bananas!"), true); - } - else if (m_action == "tutorial_giftboxes") - { - m_action_active = false; - InputDevice* device = input_manager->getDeviceManager() - ->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw fire = config->getBindingAsString(PA_FIRE); - - new TutorialMessageDialog(_("Collect gift boxes, and fire the weapon " - "with <%s> to blow away these boxes!", - fire),true); - } - else if (m_action == "tutorial_backgiftboxes") - { - m_action_active = false; - InputDevice* device = input_manager->getDeviceManager() - ->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw fire = config->getBindingAsString(PA_FIRE); - irr::core::stringw back = config->getBindingAsString(PA_LOOK_BACK); - - new TutorialMessageDialog( - _("Press <%s> to look behind, and fire the weapon with <%s> while " - "pressing <%s> to fire behind!", back, fire, back), - true); - } - else if (m_action == "tutorial_nitro_collect") - { - m_action_active = false; - - new TutorialMessageDialog(_("Collect nitro bottles (we will use them " - "after the curve)"), true); - } - else if (m_action == "tutorial_nitro_use") - { - m_action_active = false; - InputDevice* device = input_manager->getDeviceManager() - ->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw nitro = config->getBindingAsString(PA_NITRO); - - new TutorialMessageDialog(_("Use the nitro you collected by " - "pressing <%s>!", nitro), true); - } - else if (m_action == "tutorial_rescue") - { - m_action_active = false; - InputDevice* device = input_manager->getDeviceManager() - ->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw rescue = config->getBindingAsString(PA_RESCUE); - - new TutorialMessageDialog(_("Oops! When you're in trouble, press <%s> " - "to be rescued", rescue), - false); - } - else if (m_action == "tutorial_skidding") - { - m_action_active = false; - //World::getWorld()->getRaceGUI()->clearAllMessages(); - - InputDevice* device = input_manager->getDeviceManager() - ->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw skid = config->getBindingAsString(PA_DRIFT); - - - new TutorialMessageDialog(_("Accelerate and press the <%s> key while " - "turning to skid. Skidding for a short " - "while can help you turn faster to take " - "sharp turns.", skid), - true); - } - else if (m_action == "tutorial_skidding2") - { - m_action_active = false; - World::getWorld()->getRaceGUI()->clearAllMessages(); - - new TutorialMessageDialog(_("Note that if you manage to skid for " - "several seconds, you will receive a bonus " - "speedup as a reward!"), - true); - } - else if (m_action == "tutorial_endmessage") - { - m_action_active = false; - World::getWorld()->getRaceGUI()->clearAllMessages(); - - new TutorialMessageDialog(_("You are now ready to race. Good luck!"), - true); - }*/ else if (m_action == "tutorial_exit") { // TODO: move to scripting From aaf20dc9194c0347d8369f66743a255241d69859 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 27 Apr 2015 17:14:23 +1000 Subject: [PATCH 12/44] Fixed #2118. --- src/achievements/achievement.cpp | 11 ++++++----- src/achievements/achievement.hpp | 3 ++- src/config/player_manager.hpp | 23 +++++++++++++++++------ src/physics/physics.cpp | 5 ++++- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/achievements/achievement.cpp b/src/achievements/achievement.cpp index 9fd6e6ba7..d6dbb3589 100644 --- a/src/achievements/achievement.cpp +++ b/src/achievements/achievement.cpp @@ -151,20 +151,21 @@ irr::core::stringw Achievement::getProgressAsString() const * \param key The key whose value is increased. * \param increase Amount to add to the value of this key. */ -void Achievement::increase(const std::string & key, int increase) +void Achievement::increase(const std::string & key, + const std::string &goal_key, int increase) { std::map::iterator it; it = m_progress_map.find(key); if (it != m_progress_map.end()) { it->second += increase; - if (it->second > m_achievement_info->getGoalValue(key)) - it->second = m_achievement_info->getGoalValue(key); + if (it->second > m_achievement_info->getGoalValue(goal_key)) + it->second = m_achievement_info->getGoalValue(goal_key); } else { - if (increase>m_achievement_info->getGoalValue(key)) - increase = m_achievement_info->getGoalValue(key); + if (increase>m_achievement_info->getGoalValue(goal_key)) + increase = m_achievement_info->getGoalValue(goal_key); m_progress_map[key] = increase; } check(); diff --git a/src/achievements/achievement.hpp b/src/achievements/achievement.hpp index a1618a2d4..5802d963b 100644 --- a/src/achievements/achievement.hpp +++ b/src/achievements/achievement.hpp @@ -65,7 +65,8 @@ public: virtual void load(const XMLNode *node); virtual void save(UTFWriter &out); virtual int getValue(const std::string & key); - void increase(const std::string & key, int increase = 1); + void increase(const std::string & key, const std::string &goal_key, + int increase = 1); virtual void reset(); virtual irr::core::stringw getProgressAsString() const; diff --git a/src/config/player_manager.hpp b/src/config/player_manager.hpp index f11912534..929d55f90 100644 --- a/src/config/player_manager.hpp +++ b/src/config/player_manager.hpp @@ -143,16 +143,27 @@ public: } // getCurrentAchievementsStatus // ------------------------------------------------------------------------ /** A handy shortcut to increase points for an achievement key of the - * current player. */ - static void increaseAchievement(unsigned int index, const std::string &key, - int increase = 1) + * current player. + * \param achievement_id The achievement id. + * \param key The key of the current value to increase. + * \param increase How much to increase the current value. + * \param goal_key Optional: The goal key to compare the current value + * with. If not set, defaults to key. + */ + static void increaseAchievement(unsigned int achievement_id, + const std::string &key, + int increase = 1, + const std::string &goal_key="") { - Achievement *a = getCurrentAchievementsStatus()->getAchievement(index); + Achievement *a = getCurrentAchievementsStatus() + ->getAchievement(achievement_id); if (!a) { - Log::fatal("PlayerManager", "Achievement '%d' not found.", index); + Log::fatal("PlayerManager", "Achievement '%d' not found.", + achievement_id); } - a->increase(key, increase); + a->increase(key, goal_key.empty() ? key : goal_key, increase); + } // increaseAchievement // ------------------------------------------------------------------------ }; // PlayerManager diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 7935282f3..5c72cdafd 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -301,8 +301,11 @@ void Physics::update(float dt) if (target_kart != kart && c && c->getPlayer()->getConstProfile() == PlayerManager::getCurrentPlayer()) { + // Compare the current value of hits with the 'hit' goal value + // (otherwise it would be compared with the kart id goal value, + // which doesn't exist. PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_ARCH_ENEMY, - target_kart->getIdent(), 1); + target_kart->getIdent(), 1, "hit"); if (type == PowerupManager::POWERUP_BOWLING) { PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_STRIKE, From 75b24c96adeba7b9faddc85ce641fa2f36ef4038 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 29 Apr 2015 08:07:06 +1000 Subject: [PATCH 13/44] Unlock all tracks and GPs in split screen mode. --- src/states_screens/tracks_screen.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 32bd92abf..aeb8693b7 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -82,7 +82,7 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, } // if clicked_track } // selection=="random_track" - else if (selection == "locked") + else if (selection == "locked" && race_manager->getNumLocalPlayers() == 1) { unlock_manager->playLockSound(); } @@ -105,7 +105,7 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, const std::string &selection = gps_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); - if (selection == "locked") + if (selection == "locked" && race_manager->getNumLocalPlayers()==1) { unlock_manager->playLockSound(); } @@ -196,7 +196,8 @@ void TracksScreen::init() } assert(screenshots.size() > 0); - if (PlayerManager::getCurrentPlayer()->isLocked(gp->getId())) + if (PlayerManager::getCurrentPlayer()->isLocked(gp->getId()) && + race_manager->getNumLocalPlayers() == 1) { gps_widget->addAnimatedItem(_("Locked!"), "locked", screenshots, 1.5f, @@ -277,7 +278,8 @@ void TracksScreen::buildTrackList() for (unsigned int i = 0; i < tracks.size(); i++) { Track *curr = tracks.get(i); - if (PlayerManager::getCurrentPlayer()->isLocked(curr->getIdent())) + if (PlayerManager::getCurrentPlayer()->isLocked(curr->getIdent()) && + race_manager->getNumLocalPlayers() == 1) { tracks_widget->addItem( _("Locked : solve active challenges to gain access to more!"), From c91f9655d82e1fdcf4fabae794cb2bb26da1d98a Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 29 Apr 2015 08:18:52 +1000 Subject: [PATCH 14/44] Unlock all karts in multiplayer mode. --- src/states_screens/kart_selection.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index 05269388b..a7dd5fddf 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -711,7 +711,7 @@ void KartSelectionScreen::playerConfirm(const int player_id) DynamicRibbonWidget* w = getWidget("karts"); assert(w != NULL); const std::string selection = w->getSelectionIDString(player_id); - if (StringUtils::startsWith(selection, ID_LOCKED)) + if (StringUtils::startsWith(selection, ID_LOCKED) && !m_multiplayer) { unlock_manager->playLockSound(); return; @@ -839,7 +839,7 @@ void KartSelectionScreen::updateKartWidgetModel(uint8_t widget_id, ->setText( _("Random Kart"), false ); } // selection contains the name of the kart, so check only for substr - else if (StringUtils::startsWith(selection, ID_LOCKED)) + else if (StringUtils::startsWith(selection, ID_LOCKED) && !m_multiplayer) { w3->clearModels(); w3->addModel(irr_driver->getAnimatedMesh( @@ -1009,13 +1009,13 @@ void KartSelectionScreen::eventCallback(Widget* widget, { // FIXME: two players may be given the same kart by // the use of random - const int randomID = random.get( count ); + const int random_id = random.get( count ); // select kart for players > 0 (player 0 is the one // that can change the groups, so focus for player 0 // must remain on the tabs) const bool success = - w->setSelection( randomID, n, + w->setSelection( random_id, n, n != PLAYER_ID_GAME_MASTER ); if (!success) Log::warn("KartSelectionScreen", @@ -1155,21 +1155,23 @@ void KartSelectionScreen::allPlayersDone() if (selected_kart == RANDOM_KART_ID) { // don't select an already selected kart - int randomID; + int random_id; // to prevent infinite loop in case they are all locked int count = 0; bool done = false; do { - randomID = random.get(item_count); - if (items[randomID].m_code_name != ID_DONT_USE && - !StringUtils::startsWith(items[randomID].m_code_name, - ID_LOCKED)) + random_id = random.get(item_count); + // valid kart if it can bt used, and is either not locked, + // or it's a multiplayer race. + if (items[random_id].m_code_name != ID_DONT_USE && + (!StringUtils::startsWith(items[random_id].m_code_name, ID_LOCKED) + || m_multiplayer) ) { - selected_kart = items[randomID].m_code_name; + selected_kart = items[random_id].m_code_name; done = true; } - items[randomID].m_code_name = ID_DONT_USE; + items[random_id].m_code_name = ID_DONT_USE; count++; if (count > 100) return; } @@ -1444,7 +1446,8 @@ void KartSelectionScreen::setKartsFromCurrentGroup() for(unsigned int i=0; iisLocked(prop->getIdent())) + if (PlayerManager::getCurrentPlayer()->isLocked(prop->getIdent()) && + !m_multiplayer) { w->addItem(_("Locked : solve active challenges to gain access to more!"), ID_LOCKED + prop->getIdent(), From 85cf5b181e7f438552587108dc93306c1ce79f12 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Thu, 30 Apr 2015 19:03:54 -0400 Subject: [PATCH 15/44] Support extracting translations from scripts --- data/po/update_pot.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/po/update_pot.sh b/data/po/update_pot.sh index 848ee1ede..f15520492 100755 --- a/data/po/update_pot.sh +++ b/data/po/update_pot.sh @@ -17,11 +17,13 @@ XML_FILE_LIST="`find ./data ../stk-assets/tracks ../stk-assets/karts \ -name '*.grandprix' -or \ -name '*.stkgui' \ `" +ANGELSCRIPT_FILE_LIST="`find ./data ../stk-assets/tracks -name '*.as'`" echo "--------------------" echo " Source Files :" echo "--------------------" echo $CPP_FILE_LIST +echo $ANGELSCRIPT_FILE_LIST echo "--------------------" echo " XML Files :" @@ -49,6 +51,10 @@ xgettext -j -d supertuxkart -s --keyword=_ --keyword=N_ --keyword=_LTR \ -p ./data/po -o supertuxkart.pot $CPP_FILE_LIST \ --package-name=supertuxkart +# Angelscript files (xgettext doesn't support AS so pretend it's c++) +xgettext -j -d supertuxkart -s --keyword="translate" --add-comments="I18N:" \ + -p ./data/po -o supertuxkart.pot $ANGELSCRIPT_FILE_LIST \ + --package-name=supertuxkart --language=c++ echo " Done" From 585d1c50169c267966aa98db6d01e912583cd63c Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Thu, 30 Apr 2015 19:37:11 -0400 Subject: [PATCH 16/44] Scripting cleanup, remove hardcoded stuff in favor of more generic approaches --- src/physics/physics.cpp | 26 +++++++++----------- src/scriptengine/script_engine.cpp | 38 +++++++++-------------------- src/scriptengine/script_engine.hpp | 9 ------- src/scriptengine/script_gui.cpp | 25 +++++++++++++++++++ src/scriptengine/script_physics.cpp | 17 ------------- src/scriptengine/script_track.cpp | 12 --------- src/tracks/track.cpp | 4 +-- 7 files changed, 50 insertions(+), 81 deletions(-) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 7935282f3..056044f7d 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -171,9 +171,9 @@ void Physics::update(float dt) Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); int kartid1 = p->getUserPointer(0)->getPointerKart()->getWorldKartId(); int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); + // TODO: do not use globals this way, pass directly as function paramters Scripting::Physics::setCollision(kartid1,kartid2); - Scripting::Physics::setCollisionType("KartKart"); - script_engine->runScript("collisions"); + script_engine->runScript("onKartKartCollision"); continue; } // if kart-kart collision @@ -183,16 +183,15 @@ void Physics::update(float dt) // ------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); Scripting::Physics::setCollision(0, 0); - Scripting::Physics::setCollisionType("KartObject"); //object as in physical object - Scripting::Physics::setCollision - ( + //Scripting::Physics::setCollisionType("KartObject"); //object as in physical object + Scripting::Physics::setCollision( p->getUserPointer(0)->getPointerPhysicalObject()->getID(), "kart" - ); - script_engine->runScript("collisions"); + ); + script_engine->runScript("onKartObjectCollision"); PhysicalObject *obj = p->getUserPointer(0) ->getPointerPhysicalObject(); - if(obj->isCrashReset()) + if (obj->isCrashReset()) { AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); new RescueAnimation(kart); @@ -219,7 +218,7 @@ void Physics::update(float dt) continue; } - if(p->getUserPointer(0)->is(UserPointer::UP_ANIMATION)) + if (p->getUserPointer(0)->is(UserPointer::UP_ANIMATION)) { // Kart hits animation ThreeDAnimation *anim=p->getUserPointer(0)->getPointerAnimation(); @@ -257,13 +256,12 @@ void Physics::update(float dt) // ------------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); Scripting::Physics::setCollision(0,0); //TODO : support item types etc - Scripting::Physics::setCollisionType("ItemObject"); - Scripting::Physics::setCollision - ( + //Scripting::Physics::setCollisionType("ItemObject"); + Scripting::Physics::setCollision( p->getUserPointer(1)->getPointerPhysicalObject()->getID(), "item" - ); - script_engine->runScript("collisions"); + ); + script_engine->runScript("onItemObjectCollision"); p->getUserPointer(0)->getPointerFlyable() ->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject()); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 35efa63e5..13aa1ac42 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -85,10 +85,9 @@ std::string getScript(std::string scriptName) { std::string script_dir = file_manager->getAsset(FileManager::SCRIPT, ""); script_dir += World::getWorld()->getTrack()->getIdent() + "/"; - if (scriptName != "update" && scriptName != "collisions" && scriptName != "start") - scriptName = "triggers"; - script_dir += scriptName + ".as"; + // TODO: allow splitting in multiple files + script_dir += "scripting.as"; FILE *f = fopen(script_dir.c_str(), "rb"); if (f == NULL) { @@ -134,7 +133,8 @@ void ScriptEngine::runScript(std::string scriptName) asIScriptFunction *func; - if (m_script_cache.find(scriptName) == m_script_cache.end()) + auto cached_script = m_script_cache.find(scriptName); + if (cached_script == m_script_cache.end()) { // Compile the script code Log::debug("Scripting", "Compiling script '%s' (was not in cache)", scriptName.c_str()); @@ -142,7 +142,7 @@ void ScriptEngine::runScript(std::string scriptName) if (r < 0) { Log::debug("Scripting", "Script '%s' is not available", scriptName.c_str()); - m_script_cache[scriptName] = NULL; // remember that this script is unavaiable + m_script_cache[scriptName] = NULL; // remember that this script is unavailable ctx->Release(); return; } @@ -150,27 +150,12 @@ void ScriptEngine::runScript(std::string scriptName) // Find the function for the function we want to execute. //This is how you call a normal function with arguments //asIScriptFunction *func = engine->GetModule(0)->GetFunctionByDecl("void func(arg1Type, arg2Type)"); - if (scriptName == "collisions") - { - func = Scripting::Physics::registerScriptCallbacks(m_engine); - } - else if (scriptName == "update") - { - func = Scripting::Track::registerUpdateScriptCallbacks(m_engine); - } - else if (scriptName == "start") - { - func = Scripting::Track::registerStartScriptCallbacks(m_engine); - } - else - { - //trigger type can have different names - func = Scripting::Track::registerScriptCallbacks(m_engine, scriptName); - } - + func = Scripting::Track::registerScriptCallbacks(m_engine, scriptName); + if (func == NULL) { - Log::warn("Scripting", "The required function was not found : %s", scriptName.c_str()); + Log::debug("Scripting", "Scripting function was not found : %s", scriptName.c_str()); + m_script_cache[scriptName] = NULL; // remember that this script is unavailable ctx->Release(); return; } @@ -181,9 +166,8 @@ void ScriptEngine::runScript(std::string scriptName) } else { - //Script present in cache - // TODO: clear when done - func = m_script_cache[scriptName]; + // Script present in cache + func = cached_script->second; } if (func == NULL) diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index 2affef764..67b145b38 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -30,10 +30,7 @@ namespace Scripting namespace Physics { void registerScriptFunctions(asIScriptEngine *engine); - asIScriptFunction* - registerScriptCallbacks(asIScriptEngine *engine); void setCollision(int collider1,int collider2); - void setCollisionType(std::string collisionType); void setCollision(std::string collider1, std::string collider2); } @@ -50,12 +47,6 @@ namespace Scripting asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName); - - asIScriptFunction* - registerUpdateScriptCallbacks(asIScriptEngine *engine); - - asIScriptFunction* - registerStartScriptCallbacks(asIScriptEngine *engine); } class ScriptEngine diff --git a/src/scriptengine/script_gui.cpp b/src/scriptengine/script_gui.cpp index 62ccc2d02..60118cd71 100644 --- a/src/scriptengine/script_gui.cpp +++ b/src/scriptengine/script_gui.cpp @@ -174,6 +174,24 @@ namespace Scripting new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); } + void scriptLogInfo(asIScriptGeneric *gen) + { + std::string input = *(std::string*)gen->GetArgAddress(0); + Log::info("Script", "%s", input.c_str()); + } + + void scriptLogWarning(asIScriptGeneric *gen) + { + std::string input = *(std::string*)gen->GetArgAddress(0); + Log::warn("Script", "%s", input.c_str()); + } + + void scriptLogError(asIScriptGeneric *gen) + { + std::string input = *(std::string*)gen->GetArgAddress(0); + Log::error("Script", "%s", input.c_str()); + } + void registerScriptFunctions(asIScriptEngine *engine) { int r; // of type asERetCodes @@ -185,13 +203,20 @@ namespace Scripting r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in)", asFUNCTION(translateAndInsertValues1), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in, const string &in)", asFUNCTION(translateAndInsertValues2), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(translateAndInsertValues3), asCALL_GENERIC); assert(r >= 0); + + engine->SetDefaultNamespace("Utils"); r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in)", asFUNCTION(insertValues1), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(insertValues2), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(insertValues3), asCALL_GENERIC); assert(r >= 0); + + r = engine->RegisterGlobalFunction("void logInfo(const string &in)", asFUNCTION(scriptLogInfo), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("void logWarning(const string &in)", asFUNCTION(scriptLogWarning), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("void logError(const string &in)", asFUNCTION(scriptLogError), asCALL_GENERIC); assert(r >= 0); } void registerScriptEnums(asIScriptEngine *engine) { + engine->SetDefaultNamespace("GUI"); engine->RegisterEnum("PA"); engine->RegisterEnumValue("PA", "STEER_LEFT", PA_STEER_LEFT); engine->RegisterEnumValue("PA", "STEER_RIGHT", PA_STEER_RIGHT); diff --git a/src/scriptengine/script_physics.cpp b/src/scriptengine/script_physics.cpp index 2dfabf7b4..893f32624 100644 --- a/src/scriptengine/script_physics.cpp +++ b/src/scriptengine/script_physics.cpp @@ -51,11 +51,6 @@ namespace Scripting void *pointer = &m_collider1; gen->SetReturnObject(pointer); } - void getCollisionType(asIScriptGeneric *gen) - { - void *pointer = &m_collisionType; - gen->SetReturnObject(pointer); - } //Callbacks from Physics Engine, for collisions void setCollision(int collider1,int collider2) @@ -68,24 +63,12 @@ namespace Scripting m_collider1 = collider1; m_collider2 = collider2; } - void setCollisionType(std::string collisionType) - { - m_collisionType = collisionType; - } - asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine) - { - asIScriptFunction *func; - std::string function_name = "void on" + m_collisionType + "Collision()"; - func = engine->GetModule(0)->GetFunctionByDecl(function_name.c_str()); - return func; - } void registerScriptFunctions(asIScriptEngine *engine) { int r; engine->SetDefaultNamespace("Physics"); r = engine->RegisterGlobalFunction("uint getCollidingKart1()", asFUNCTION(getCollidingKart1), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterGlobalFunction("uint getCollidingKart2()", asFUNCTION(getCollidingKart2), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterGlobalFunction("string getCollisionType()", asFUNCTION(getCollisionType), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string getCollidingID()", asFUNCTION(getCollidingID), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string createExplosion(Vec3 &in)", asFUNCTION(createExplosion), asCALL_GENERIC); assert(r >= 0); } diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index 3b95f39af..090bc9e18 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -46,18 +46,6 @@ namespace Scripting func = engine->GetModule(0)->GetFunctionByDecl(function_name.c_str()); return func; } - asIScriptFunction* registerStartScriptCallbacks(asIScriptEngine *engine) - { - asIScriptFunction *func; - func = engine->GetModule(0)->GetFunctionByDecl("void onStart()"); - return func; - } - asIScriptFunction* registerUpdateScriptCallbacks(asIScriptEngine *engine) - { - asIScriptFunction *func; - func = engine->GetModule(0)->GetFunctionByDecl("void onUpdate()"); - return func; - } /* void disableAnimation(std::string *name, void *memory) { diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 3645fe53a..49fe9b9bd 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -1486,7 +1486,7 @@ void Track::update(float dt) if (!m_startup_run) // first time running update = good point to run startup script { Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - script_engine->runScript("start"); + script_engine->runScript("onStart"); m_startup_run = true; } m_track_object_manager->update(dt); @@ -1498,7 +1498,7 @@ void Track::update(float dt) CheckManager::get()->update(dt); ItemManager::get()->update(dt); Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - script_engine->runScript("update"); + script_engine->runScript("onUpdate"); } // update // ---------------------------------------------------------------------------- From 1559d03ea130cd3be4827329244fe963037be4d9 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Thu, 30 Apr 2015 20:24:02 -0400 Subject: [PATCH 17/44] More work on scripting, test ways to pass arguments to script functions --- src/physics/physics.cpp | 6 ++-- src/scriptengine/script_engine.cpp | 38 ++++++++++++++---------- src/scriptengine/script_engine.hpp | 5 +++- src/scriptengine/script_track.cpp | 4 +-- src/tracks/track.cpp | 8 +++-- src/tracks/track_object_presentation.cpp | 16 +++++----- 6 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 056044f7d..0ad5d29e3 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -173,7 +173,7 @@ void Physics::update(float dt) int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); // TODO: do not use globals this way, pass directly as function paramters Scripting::Physics::setCollision(kartid1,kartid2); - script_engine->runScript("onKartKartCollision"); + script_engine->runScript("void onKartKartCollision()"); continue; } // if kart-kart collision @@ -188,7 +188,7 @@ void Physics::update(float dt) p->getUserPointer(0)->getPointerPhysicalObject()->getID(), "kart" ); - script_engine->runScript("onKartObjectCollision"); + script_engine->runScript("void onKartObjectCollision()"); PhysicalObject *obj = p->getUserPointer(0) ->getPointerPhysicalObject(); if (obj->isCrashReset()) @@ -261,7 +261,7 @@ void Physics::update(float dt) p->getUserPointer(1)->getPointerPhysicalObject()->getID(), "item" ); - script_engine->runScript("onItemObjectCollision"); + script_engine->runScript("void onItemObjectCollision()"); p->getUserPointer(0)->getPointerFlyable() ->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject()); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 13aa1ac42..885bd22a2 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -114,24 +114,25 @@ std::string getScript(std::string scriptName) } //----------------------------------------------------------------------------- + /** runs the specified script * \param string scriptName = name of script to run */ void ScriptEngine::runScript(std::string scriptName) { - PROFILER_PUSH_CPU_MARKER("RunScript", 255, 0, 0); + std::function callback; + runScript(scriptName, callback); +} - // Create a context that will execute the script. - asIScriptContext *ctx = m_engine->CreateContext(); - if (ctx == NULL) - { - Log::error("Scripting", "Failed to create the context."); - //m_engine->Release(); - return; - } +//----------------------------------------------------------------------------- + +/** runs the specified script +* \param string scriptName = name of script to run +*/ +void ScriptEngine::runScript(std::string scriptName, std::function callback) +{ int r; //int for error checking - asIScriptFunction *func; auto cached_script = m_script_cache.find(scriptName); if (cached_script == m_script_cache.end()) @@ -143,7 +144,6 @@ void ScriptEngine::runScript(std::string scriptName) { Log::debug("Scripting", "Script '%s' is not available", scriptName.c_str()); m_script_cache[scriptName] = NULL; // remember that this script is unavailable - ctx->Release(); return; } @@ -156,7 +156,6 @@ void ScriptEngine::runScript(std::string scriptName) { Log::debug("Scripting", "Scripting function was not found : %s", scriptName.c_str()); m_script_cache[scriptName] = NULL; // remember that this script is unavailable - ctx->Release(); return; } @@ -172,10 +171,19 @@ void ScriptEngine::runScript(std::string scriptName) if (func == NULL) { - PROFILER_POP_CPU_MARKER(); return; // script unavailable } + + // Create a context that will execute the script. + asIScriptContext *ctx = m_engine->CreateContext(); + if (ctx == NULL) + { + Log::error("Scripting", "Failed to create the context."); + //m_engine->Release(); + return; + } + // Prepare the script context with the function we wish to execute. Prepare() // must be called on the context before each new script function that will be // executed. Note, that if because we intend to execute the same function @@ -194,6 +202,8 @@ void ScriptEngine::runScript(std::string scriptName) //ctx->setArgType(index, value); //for example : ctx->SetArgFloat(0, 3.14159265359f); + if (callback) + callback(ctx); // Execute the function r = ctx->Execute(); @@ -230,8 +240,6 @@ void ScriptEngine::runScript(std::string scriptName) // We must release the contexts when no longer using them ctx->Release(); - - PROFILER_POP_CPU_MARKER(); } //----------------------------------------------------------------------------- diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index 67b145b38..41c2c65cd 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -21,6 +21,7 @@ #include #include +#include class TrackObjectPresentation; @@ -48,14 +49,16 @@ namespace Scripting asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName); } - + class ScriptEngine { public: + ScriptEngine(); ~ScriptEngine(); void runScript(std::string scriptName); + void runScript(std::string scriptName, std::function callback); void cleanupCache(); private: diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index 090bc9e18..7cfb2602b 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -39,10 +39,10 @@ namespace Scripting namespace Track { //register callbacks - asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName) + // TODO: move this out of Track namespace, it's not specific to tracks + asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine, std::string function_name) { asIScriptFunction *func; - std::string function_name = "void " + scriptName + "()"; func = engine->GetModule(0)->GetFunctionByDecl(function_name.c_str()); return func; } diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 49fe9b9bd..deaf53440 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -1486,7 +1486,7 @@ void Track::update(float dt) if (!m_startup_run) // first time running update = good point to run startup script { Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - script_engine->runScript("onStart"); + script_engine->runScript("void onStart()"); m_startup_run = true; } m_track_object_manager->update(dt); @@ -1497,8 +1497,10 @@ void Track::update(float dt) } CheckManager::get()->update(dt); ItemManager::get()->update(dt); - Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - script_engine->runScript("onUpdate"); + + // TODO: enable onUpdate scripts if we ever find a compelling use for them + //Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); + //script_engine->runScript("void onUpdate()"); } // update // ---------------------------------------------------------------------------- diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 308ecc17f..9780c1242 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -22,6 +22,7 @@ #include "audio/sfx_buffer.hpp" #include "challenges/unlock_manager.hpp" #include "config/user_config.hpp" +#include "graphics/camera.hpp" #include "graphics/irr_driver.hpp" #include "graphics/material_manager.hpp" #include "graphics/mesh_tools.hpp" @@ -34,6 +35,7 @@ #include "input/input_device.hpp" #include "input/input_manager.hpp" #include "items/item_manager.hpp" +#include "karts/abstract_kart.hpp" #include "modes/world.hpp" #include "scriptengine/script_engine.hpp" #include "states_screens/dialogs/race_paused_dialog.hpp" @@ -932,13 +934,11 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); m_action_active = false; - script_engine->runScript(m_action); - - /* - Catch exception -> script not found - fprintf(stderr, "[TrackObject] WARNING: unknown action <%s>\n", - m_action.c_str()); - - */ + int idKart = 0; + Camera* camera = Camera::getActiveCamera(); + if (camera != NULL && camera->getKart() != NULL) + idKart = camera->getKart()->getWorldKartId(); + script_engine->runScript("void " + m_action + "(int)", + [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, idKart); }); } } // onTriggerItemApproached From d98c1044e6d4bbee031a432a1257665cd58834f3 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Thu, 30 Apr 2015 20:36:54 -0400 Subject: [PATCH 18/44] More work on scripting --- src/physics/physics.cpp | 39 ++++++++++++++--------------- src/scriptengine/script_engine.hpp | 2 -- src/scriptengine/script_physics.cpp | 30 ---------------------- src/scriptengine/script_physics.hpp | 25 +----------------- 4 files changed, 20 insertions(+), 76 deletions(-) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 0ad5d29e3..5481f763e 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -171,9 +171,11 @@ void Physics::update(float dt) Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); int kartid1 = p->getUserPointer(0)->getPointerKart()->getWorldKartId(); int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); - // TODO: do not use globals this way, pass directly as function paramters - Scripting::Physics::setCollision(kartid1,kartid2); - script_engine->runScript("void onKartKartCollision()"); + script_engine->runScript("void onKartKartCollision(int, int)", + [=](asIScriptContext* ctx) { + ctx->SetArgDWord(0, kartid1); + ctx->SetArgDWord(1, kartid2); + }); continue; } // if kart-kart collision @@ -182,28 +184,27 @@ void Physics::update(float dt) // Kart hits physical object // ------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - Scripting::Physics::setCollision(0, 0); - //Scripting::Physics::setCollisionType("KartObject"); //object as in physical object - Scripting::Physics::setCollision( - p->getUserPointer(0)->getPointerPhysicalObject()->getID(), - "kart" - ); - script_engine->runScript("void onKartObjectCollision()"); + AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); + int kartId = kart->getWorldKartId(); + std::string obj_id = p->getUserPointer(0)->getPointerPhysicalObject()->getID(); + + // TODO: pass obj_id as arguent + script_engine->runScript("void onKartObjectCollision(int)", + [=](asIScriptContext* ctx) { + ctx->SetArgDWord(0, kartId); + }); PhysicalObject *obj = p->getUserPointer(0) ->getPointerPhysicalObject(); if (obj->isCrashReset()) { - AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); new RescueAnimation(kart); } else if (obj->isExplodeKartObject()) { - AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); ExplosionAnimation::create(kart); } else if (obj->isFlattenKartObject()) { - AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration() * kart->getPlayerDifficulty()->getSquashDuration(), kp->getSquashSlowdown() * kart->getPlayerDifficulty()->getSquashSlowdown()); @@ -211,7 +212,6 @@ void Physics::update(float dt) else if(obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) { - int kartId = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); soccerWorld->setLastKartTohitBall(kartId); } @@ -255,12 +255,11 @@ void Physics::update(float dt) // Projectile hits physical object // ------------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - Scripting::Physics::setCollision(0,0); //TODO : support item types etc - //Scripting::Physics::setCollisionType("ItemObject"); - Scripting::Physics::setCollision( - p->getUserPointer(1)->getPointerPhysicalObject()->getID(), - "item" - ); + // TODO: pass arguments + //Scripting::Physics::setColl0ision( + // p->getUserPointer(1)->getPointerPhysicalObject()->getID(), + // "item" + //); script_engine->runScript("void onItemObjectCollision()"); p->getUserPointer(0)->getPointerFlyable() ->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject()); diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index 41c2c65cd..e1c596454 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -31,8 +31,6 @@ namespace Scripting namespace Physics { void registerScriptFunctions(asIScriptEngine *engine); - void setCollision(int collider1,int collider2); - void setCollision(std::string collider1, std::string collider2); } namespace Kart diff --git a/src/scriptengine/script_physics.cpp b/src/scriptengine/script_physics.cpp index 893f32624..60fc0ce68 100644 --- a/src/scriptengine/script_physics.cpp +++ b/src/scriptengine/script_physics.cpp @@ -36,40 +36,10 @@ namespace Scripting HitEffect *he = new Explosion(*explosion_loc, "explosion", "explosion_bomb.xml"); projectile_manager->addHitEffect(he); } - //Bind getters for colliding karts - void getCollidingKart1(asIScriptGeneric *gen) - { - gen->SetReturnDWord(m_collidingkartid1); - } - void getCollidingKart2(asIScriptGeneric *gen) - { - gen->SetReturnDWord(m_collidingkartid2); - } - //Bind getter for colliding objects - void getCollidingID(asIScriptGeneric *gen) - { - void *pointer = &m_collider1; - gen->SetReturnObject(pointer); - } - - //Callbacks from Physics Engine, for collisions - void setCollision(int collider1,int collider2) - { - m_collidingkartid1 = collider1; - m_collidingkartid2 = collider2; - } - void setCollision(std::string collider1, std::string collider2) - { - m_collider1 = collider1; - m_collider2 = collider2; - } void registerScriptFunctions(asIScriptEngine *engine) { int r; engine->SetDefaultNamespace("Physics"); - r = engine->RegisterGlobalFunction("uint getCollidingKart1()", asFUNCTION(getCollidingKart1), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterGlobalFunction("uint getCollidingKart2()", asFUNCTION(getCollidingKart2), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterGlobalFunction("string getCollidingID()", asFUNCTION(getCollidingID), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string createExplosion(Vec3 &in)", asFUNCTION(createExplosion), asCALL_GENERIC); assert(r >= 0); } } diff --git a/src/scriptengine/script_physics.hpp b/src/scriptengine/script_physics.hpp index d6f47534d..51fbaa9c7 100644 --- a/src/scriptengine/script_physics.hpp +++ b/src/scriptengine/script_physics.hpp @@ -27,34 +27,11 @@ namespace Scripting namespace Physics { - - //IDs of kart collisions - int m_collidingkartid1; - int m_collidingkartid2; - - //Details of collision - std::string m_collider1; - std::string m_collider2; - std::string m_collisionType; - + //script engine functions void registerScriptFunctions(asIScriptEngine *engine); asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine); - - - //game engine functions - void setCollision(int collider1, int collider2); - void setCollisionType(std::string); - - - - //script-bound functions - void getCollidingKart1(asIScriptGeneric *gen); - void getCollidingKart2(asIScriptGeneric *gen); - void getCollsionType(asIScriptGeneric *gen); - void getCollidingID(asIScriptGeneric *gen); - } From eea90692652e2d7e3de0952c2f830b37d482a43b Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sun, 3 May 2015 02:52:15 +0200 Subject: [PATCH 19/44] Fix #2171 - Font tool build failure with CMake 3.2 --- tools/font_tool/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/font_tool/CMakeLists.txt b/tools/font_tool/CMakeLists.txt index e67e6fbce..fce46bf16 100644 --- a/tools/font_tool/CMakeLists.txt +++ b/tools/font_tool/CMakeLists.txt @@ -11,6 +11,7 @@ if(FONT_TOOL) if(UNIX AND NOT APPLE) find_package(Xrandr REQUIRED) find_package(X11 REQUIRED) + include_directories(${X11_INCLUDE_DIR}) if(NOT XRANDR_FOUND) message(STATUS "XRANDR not found.") endif() @@ -27,6 +28,7 @@ if(FONT_TOOL) if(UNIX AND NOT APPLE) target_link_libraries(font_tool ${XRANDR_LIBRARIES}) target_link_libraries(font_tool ${X11_Xft_LIB} Xxf86vm) + target_link_libraries(font_tool ${X11_LIBRARIES}) endif() else() message(STATUS "Freetype was not found, the font tool won't be built (only useful for developers)") From f019f8622c93e57c12c0763dba44416c580f85a7 Mon Sep 17 00:00:00 2001 From: deve Date: Mon, 4 May 2015 11:09:53 +0200 Subject: [PATCH 20/44] Make sure that window size is larger than 0 --- src/graphics/irr_driver.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 92608912a..d3409f2c9 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -109,7 +109,7 @@ IrrDriver::IrrDriver() m_phase = SOLID_NORMAL_AND_DEPTH_PASS; m_device = createDevice(video::EDT_NULL, irr::core::dimension2d(640, 480), - /*bits*/16U, /**fullscreen*/ false, + /*bits*/16U, /**fullscreen*/ false, /*stencilBuffer*/ false, /*vsync*/false, /*event receiver*/ NULL, @@ -349,8 +349,13 @@ void IrrDriver::initDevice() video::IVideoModeList* modes = m_device->getVideoModeList(); const core::dimension2d ssize = modes->getDesktopResolution(); - if (UserConfigParams::m_width > (int)ssize.Width || - UserConfigParams::m_height > (int)ssize.Height) + + if (ssize.Width < 1 || ssize.Height < 1) + { + Log::warn("irr_driver", "Unknown desktop resolution."); + } + else if (UserConfigParams::m_width > (int)ssize.Width || + UserConfigParams::m_height > (int)ssize.Height) { Log::warn("irr_driver", "The window size specified in " "user config is larger than your screen!"); @@ -358,13 +363,13 @@ void IrrDriver::initDevice() UserConfigParams::m_height = (int)ssize.Height; } - core::dimension2d res = core::dimension2du(UserConfigParams::m_width, - UserConfigParams::m_height); - if (UserConfigParams::m_fullscreen) { if (modes->getVideoModeCount() > 0) { + core::dimension2d res = core::dimension2du( + UserConfigParams::m_width, + UserConfigParams::m_height); res = modes->getVideoModeResolution(res, res); UserConfigParams::m_width = res.Width; @@ -372,13 +377,20 @@ void IrrDriver::initDevice() } else { - Log::verbose("irr_driver", "Cannot get information about " - "resolutions. Try to use the default one."); - UserConfigParams::m_width = MIN_SUPPORTED_WIDTH; - UserConfigParams::m_height = MIN_SUPPORTED_HEIGHT; + Log::warn("irr_driver", "Cannot get information about " + "resolutions. Disable fullscreen."); + UserConfigParams::m_fullscreen = false; } } + if (UserConfigParams::m_width < 1 || UserConfigParams::m_height < 1) + { + Log::warn("irr_driver", "Invalid window size. " + "Try to use the default one."); + UserConfigParams::m_width = MIN_SUPPORTED_WIDTH; + UserConfigParams::m_height = MIN_SUPPORTED_HEIGHT; + } + m_device->closeDevice(); m_video_driver = NULL; m_gui_env = NULL; @@ -703,7 +715,7 @@ bool IrrDriver::moveWindow(int x, int y) } #elif defined(__linux__) && !defined(ANDROID) const video::SExposedVideoData& videoData = m_video_driver->getExposedVideoData(); - + Display* display = (Display*)videoData.OpenGLLinux.X11Display; int screen = DefaultScreen(display); int screen_w = DisplayWidth(display, screen); @@ -713,7 +725,7 @@ bool IrrDriver::moveWindow(int x, int y) { x = screen_w - UserConfigParams::m_width; } - + if (y + UserConfigParams::m_height > screen_h) { y = screen_h - UserConfigParams::m_height; From a311281aa79c64b9784ee22aa2b95ab374be0eb3 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Wed, 6 May 2015 20:39:40 +0200 Subject: [PATCH 21/44] Fix Random Grand Prix being reversed --- src/race/grand_prix_data.hpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/race/grand_prix_data.hpp b/src/race/grand_prix_data.hpp index a788aed53..3e4ee4327 100644 --- a/src/race/grand_prix_data.hpp +++ b/src/race/grand_prix_data.hpp @@ -145,30 +145,30 @@ public: unsigned int laps, bool reverse); void remove(const unsigned int track); - // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------- /** @return the (potentially translated) user-visible name of the Grand * Prix (apply fribidi as needed) */ irr::core::stringw getName() const { return m_editable ? m_name.c_str() : _LTR(m_name.c_str()); } - // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------- /** @return the internal indentifier of the Grand Prix (not translated) */ - const std::string& getId() const { return m_id; } + const std::string& getId() const { return m_id; } - // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------- /** Returns true if this GP is a random GP. */ - bool isRandomGP() const { return m_id==getRandomGPID(); } - // ------------------------------------------------------------------------ + bool isRandomGP() const { return m_id==getRandomGPID(); } + // ------------------------------------------------------------------------- /** Returns the filename of the grand prix xml file. */ - const std::string& getFilename() const { return m_filename; } + const std::string& getFilename() const { return m_filename; } // ------------------------------------------------------------------------ - enum GPGroupType getGroup() const { return m_group; } + enum GPGroupType getGroup() const { return m_group; } - // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------- enum GPReverseType getReverseType() - const { return m_reverse_type; } - static const char* getRandomGPID() { return "random"; } - static const wchar_t* getRandomGPName() { return _("Random Grand Prix"); } + const { return m_reverse_type; } + static const char* getRandomGPID() { return "random"; } + static const wchar_t* getRandomGPName() { return _LTR("Random Grand Prix");} }; // GrandPrixData #endif From 78f9500b1d70da70f1f3b4412d6a1c2e01bc1a5e Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sat, 9 May 2015 21:29:37 -0400 Subject: [PATCH 22/44] Work on scripting --- src/scriptengine/script_engine.cpp | 17 ++- src/scriptengine/script_engine.hpp | 7 +- src/scriptengine/script_gui.cpp | 208 +++++++++++------------------ src/scriptengine/script_gui.hpp | 2 - src/scriptengine/script_kart.cpp | 136 ++++++++++--------- src/scriptengine/script_track.cpp | 8 -- src/scriptengine/script_utils.cpp | 165 +++++++++++++++++++++++ src/scriptengine/script_utils.hpp | 34 +++++ src/scriptengine/scriptvec3.cpp | 18 ++- 9 files changed, 375 insertions(+), 220 deletions(-) create mode 100644 src/scriptengine/script_utils.cpp create mode 100644 src/scriptengine/script_utils.hpp diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 885bd22a2..138658e36 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -24,6 +24,7 @@ #include "scriptengine/script_engine.hpp" #include "scriptengine/script_gui.hpp" #include "scriptengine/script_track.hpp" +#include "scriptengine/script_utils.hpp" #include "scriptengine/scriptstdstring.hpp" #include "scriptengine/scriptvec3.hpp" #include @@ -150,7 +151,7 @@ void ScriptEngine::runScript(std::string scriptName, std::functionGetModule(0)->GetFunctionByDecl("void func(arg1Type, arg2Type)"); - func = Scripting::Track::registerScriptCallbacks(m_engine, scriptName); + func = registerScriptCallbacks(m_engine, scriptName); if (func == NULL) { @@ -244,6 +245,16 @@ void ScriptEngine::runScript(std::string scriptName, std::functionGetModule(0)->GetFunctionByDecl(function_name.c_str()); + return func; +} + +//----------------------------------------------------------------------------- + void ScriptEngine::cleanupCache() { for (auto curr : m_script_cache) @@ -265,11 +276,9 @@ void ScriptEngine::configureEngine(asIScriptEngine *engine) RegisterVec3(engine); //register Vec3 Scripting::Track::registerScriptFunctions(m_engine); - Scripting::Kart::registerScriptFunctions(m_engine); - Scripting::Physics::registerScriptFunctions(m_engine); - + Scripting::Utils::registerScriptFunctions(m_engine); Scripting::GUI::registerScriptFunctions(m_engine); Scripting::GUI::registerScriptEnums(m_engine); diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index e1c596454..9dca59770 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -43,9 +43,6 @@ namespace Scripting void registerScriptFunctions(asIScriptEngine *engine); void registerScriptEnums(asIScriptEngine *engine); - - asIScriptFunction* - registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName); } class ScriptEngine @@ -58,7 +55,9 @@ namespace Scripting void runScript(std::string scriptName); void runScript(std::string scriptName, std::function callback); void cleanupCache(); - + asIScriptFunction* + registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName); + private: asIScriptEngine *m_engine; std::map m_script_cache; diff --git a/src/scriptengine/script_gui.cpp b/src/scriptengine/script_gui.cpp index 60118cd71..783351819 100644 --- a/src/scriptengine/script_gui.cpp +++ b/src/scriptengine/script_gui.cpp @@ -34,114 +34,64 @@ #include #include //debug +/** \cond DOXYGEN_IGNORE */ namespace Scripting { +/** \endcond */ + namespace GUI { - //getter for key binding for player action enums - void getKeyBinding(asIScriptGeneric *gen) + /** \addtogroup Scripting + * @{ + */ + /** \addtogroup GUI + * @{ + */ + + /** Get the key bound to a player action (enum GUI::PlayerAction)*/ + std::string getKeyBinding(int Enum_value) { - int Enum_value = (int)gen->GetArgDWord(0); InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); DeviceConfig* config = device->getConfiguration(); irr::core::stringw control; PlayerAction ScriptAction = (PlayerAction)Enum_value; control = config->getBindingAsString(ScriptAction); std::string key = std::string(irr::core::stringc(control).c_str()); - void *key_pointer = &key; - gen->SetReturnObject(key_pointer); + return key; } - // Displays the message specified in displayMessage( string message ) within the script - void displayMessage(asIScriptGeneric *gen) + /** Show the specified message in a popup */ + void displayMessage(std::string* input) { - std::string *input = (std::string*)gen->GetArgAddress(0); irr::core::stringw out = StringUtils::utf8_to_wide(input->c_str()); new TutorialMessageDialog((out), true); } - - void translate(asIScriptGeneric *gen) + /** Get translated version of string */ + std::string translate(std::string* input) { - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - irr::core::stringw out = translations->fribidize(translations->w_gettext(input->c_str())); - // Return the string - new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + return StringUtils::wide_to_utf8(out.c_str()); } - void insertValues1(asIScriptGeneric *gen) + /** Translate string and insert values. e.g. GUI::translate("Hello %s !", "John") */ + std::string translate(std::string* formatString, std::string* arg1) { - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - - irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), - StringUtils::utf8_to_wide(arg1->c_str())); - - // Return the string - new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); - } - - void insertValues2(asIScriptGeneric *gen) - { - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - std::string *arg2 = (std::string*)gen->GetArgAddress(2); - - irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), - StringUtils::utf8_to_wide(arg1->c_str()), - StringUtils::utf8_to_wide(arg2->c_str())); - - // Return the string - new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); - } - - void insertValues3(asIScriptGeneric *gen) - { - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - std::string *arg2 = (std::string*)gen->GetArgAddress(2); - std::string *arg3 = (std::string*)gen->GetArgAddress(3); - - irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(input->c_str()), - StringUtils::utf8_to_wide(arg1->c_str()), - StringUtils::utf8_to_wide(arg2->c_str()), - StringUtils::utf8_to_wide(arg3->c_str())); - - // Return the string - new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); - } - - void translateAndInsertValues1(asIScriptGeneric *gen) - { - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - - irr::core::stringw out = translations->w_gettext(input->c_str()); + irr::core::stringw out = translations->w_gettext(formatString->c_str()); out = StringUtils::insertValues(out, StringUtils::utf8_to_wide(arg1->c_str())); out = translations->fribidize(out); - // Return the string - new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + return StringUtils::wide_to_utf8(out.c_str()); } - void translateAndInsertValues2(asIScriptGeneric *gen) + /** Translate string and insert values. e.g. GUI::translate("Hello %s !", "John") */ + std::string translate(std::string* formatString, std::string* arg1, std::string* arg2) { - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - std::string *arg2 = (std::string*)gen->GetArgAddress(2); - - irr::core::stringw out = translations->w_gettext(input->c_str()); + irr::core::stringw out = translations->w_gettext(formatString->c_str()); out = StringUtils::insertValues(out, StringUtils::utf8_to_wide(arg1->c_str()), @@ -149,19 +99,14 @@ namespace Scripting out = translations->fribidize(out); - // Return the string - new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + return StringUtils::wide_to_utf8(out.c_str()); } - void translateAndInsertValues3(asIScriptGeneric *gen) + /** Translate string and insert values. e.g. GUI::translate("Hello %s !", "John") */ + std::string translate(std::string* formatString, std::string* arg1, std::string* arg2, + std::string* arg3) { - // Get the arguments - std::string *input = (std::string*)gen->GetArgAddress(0); - std::string *arg1 = (std::string*)gen->GetArgAddress(1); - std::string *arg2 = (std::string*)gen->GetArgAddress(2); - std::string *arg3 = (std::string*)gen->GetArgAddress(3); - - irr::core::stringw out = translations->w_gettext(input->c_str()); + irr::core::stringw out = translations->w_gettext(formatString->c_str()); out = StringUtils::insertValues(out, StringUtils::utf8_to_wide(arg1->c_str()), @@ -170,70 +115,73 @@ namespace Scripting out = translations->fribidize(out); - // Return the string - new(gen->GetAddressOfReturnLocation()) std::string(StringUtils::wide_to_utf8(out.c_str())); + return StringUtils::wide_to_utf8(out.c_str()); } - - void scriptLogInfo(asIScriptGeneric *gen) + /** @}*/ + /** @}*/ + + // UNDOCUMENTED PROXIES : Use proxies to have different signatures, then redirect to the + // documented function whose name is exposed in angelscript (these proxies exist so that + // angelscript can properly resolve overloads, but doxygen can still generate the right docs + /** \cond DOXYGEN_IGNORE */ + std::string proxy_translate(std::string* formatString) { - std::string input = *(std::string*)gen->GetArgAddress(0); - Log::info("Script", "%s", input.c_str()); + return translate(formatString); } - void scriptLogWarning(asIScriptGeneric *gen) + std::string proxy_translateAndInsertValues1(std::string* formatString, std::string* arg1) { - std::string input = *(std::string*)gen->GetArgAddress(0); - Log::warn("Script", "%s", input.c_str()); + return translate(formatString, arg1); } - void scriptLogError(asIScriptGeneric *gen) + std::string proxy_translateAndInsertValues2(std::string* formatString, std::string* arg1, std::string* arg2) { - std::string input = *(std::string*)gen->GetArgAddress(0); - Log::error("Script", "%s", input.c_str()); + return translate(formatString, arg1, arg2); } + std::string proxy_translateAndInsertValues3(std::string* formatString, std::string* arg1, std::string* arg2, + std::string* arg3) + { + return translate(formatString, arg1, arg2, arg3); + } + /** \endcond */ + void registerScriptFunctions(asIScriptEngine *engine) { int r; // of type asERetCodes engine->SetDefaultNamespace("GUI"); - r = engine->RegisterGlobalFunction("void displayMessage(string &in)", asFUNCTION(displayMessage), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string getKeyBinding(int input)", asFUNCTION(getKeyBinding), asCALL_GENERIC); assert(r >= 0); - - r = engine->RegisterGlobalFunction("string translate(const string &in)", asFUNCTION(translate), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in)", asFUNCTION(translateAndInsertValues1), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in, const string &in)", asFUNCTION(translateAndInsertValues2), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(translateAndInsertValues3), asCALL_GENERIC); assert(r >= 0); - - engine->SetDefaultNamespace("Utils"); - r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in)", asFUNCTION(insertValues1), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(insertValues2), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(insertValues3), asCALL_GENERIC); assert(r >= 0); - - r = engine->RegisterGlobalFunction("void logInfo(const string &in)", asFUNCTION(scriptLogInfo), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void logWarning(const string &in)", asFUNCTION(scriptLogWarning), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void logError(const string &in)", asFUNCTION(scriptLogError), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("void displayMessage(const string &in)", asFUNCTION(displayMessage), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string getKeyBinding(int input)", asFUNCTION(getKeyBinding), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string translate(const string &in)", asFUNCTION(proxy_translate), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in)", asFUNCTION(proxy_translateAndInsertValues1), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in, const string &in)", asFUNCTION(proxy_translateAndInsertValues2), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string translate(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_translateAndInsertValues3), asCALL_CDECL); assert(r >= 0); } void registerScriptEnums(asIScriptEngine *engine) { + // TODO: document enum in doxygen-generated scripting docs engine->SetDefaultNamespace("GUI"); - engine->RegisterEnum("PA"); - engine->RegisterEnumValue("PA", "STEER_LEFT", PA_STEER_LEFT); - engine->RegisterEnumValue("PA", "STEER_RIGHT", PA_STEER_RIGHT); - engine->RegisterEnumValue("PA", "ACCEL", PA_ACCEL); - engine->RegisterEnumValue("PA", "BRAKE", PA_BRAKE); - engine->RegisterEnumValue("PA", "NITRO", PA_NITRO); - engine->RegisterEnumValue("PA", "DRIFT", PA_DRIFT); - engine->RegisterEnumValue("PA", "RESCUE", PA_RESCUE); - engine->RegisterEnumValue("PA", "FIRE", PA_FIRE); - engine->RegisterEnumValue("PA", "LOOK_BACK", PA_LOOK_BACK); - engine->RegisterEnumValue("PA", "PAUSE_RACE", PA_PAUSE_RACE); - engine->RegisterEnumValue("PA", "MENU_UP", PA_MENU_UP); - engine->RegisterEnumValue("PA", "MENU_DOWN", PA_MENU_DOWN); - engine->RegisterEnumValue("PA", "MENU_LEFT", PA_MENU_LEFT); - engine->RegisterEnumValue("PA", "MENU_RIGHT", PA_MENU_RIGHT); - engine->RegisterEnumValue("PA", "MENU_SELECT", PA_MENU_SELECT); - engine->RegisterEnumValue("PA", "MENU_CANCEL", PA_MENU_CANCEL); + engine->RegisterEnum("PlayerAction"); + engine->RegisterEnumValue("PlayerAction", "STEER_LEFT", PA_STEER_LEFT); + engine->RegisterEnumValue("PlayerAction", "STEER_RIGHT", PA_STEER_RIGHT); + engine->RegisterEnumValue("PlayerAction", "ACCEL", PA_ACCEL); + engine->RegisterEnumValue("PlayerAction", "BRAKE", PA_BRAKE); + engine->RegisterEnumValue("PlayerAction", "NITRO", PA_NITRO); + engine->RegisterEnumValue("PlayerAction", "DRIFT", PA_DRIFT); + engine->RegisterEnumValue("PlayerAction", "RESCUE", PA_RESCUE); + engine->RegisterEnumValue("PlayerAction", "FIRE", PA_FIRE); + engine->RegisterEnumValue("PlayerAction", "LOOK_BACK", PA_LOOK_BACK); + engine->RegisterEnumValue("PlayerAction", "PAUSE_RACE", PA_PAUSE_RACE); + engine->RegisterEnumValue("PlayerAction", "MENU_UP", PA_MENU_UP); + engine->RegisterEnumValue("PlayerAction", "MENU_DOWN", PA_MENU_DOWN); + engine->RegisterEnumValue("PlayerAction", "MENU_LEFT", PA_MENU_LEFT); + engine->RegisterEnumValue("PlayerAction", "MENU_RIGHT", PA_MENU_RIGHT); + engine->RegisterEnumValue("PlayerAction", "MENU_SELECT", PA_MENU_SELECT); + engine->RegisterEnumValue("PlayerAction", "MENU_CANCEL", PA_MENU_CANCEL); } } + +/** \cond DOXYGEN_IGNORE */ } +/** \endcond */ diff --git a/src/scriptengine/script_gui.hpp b/src/scriptengine/script_gui.hpp index f5d5434f6..804abf1f8 100644 --- a/src/scriptengine/script_gui.hpp +++ b/src/scriptengine/script_gui.hpp @@ -29,8 +29,6 @@ namespace Scripting { //script engine functions void registerScriptFunctions(asIScriptEngine *engine); - asIScriptFunction* - registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName); void registerScriptEnums(asIScriptEngine *engine); } diff --git a/src/scriptengine/script_kart.cpp b/src/scriptengine/script_kart.cpp index 8642fbf00..d950b2ccf 100644 --- a/src/scriptengine/script_kart.cpp +++ b/src/scriptengine/script_kart.cpp @@ -25,99 +25,105 @@ //debug #include - +/** \cond DOXYGEN_IGNORE */ namespace Scripting { +/** \endcond */ namespace Kart { - //Squashes the specified kart, specified time - void squashKart(asIScriptGeneric *gen) + /** \addtogroup Scripting + * @{ + */ + /** \addtogroup Kart + * @{ + */ + + /** Squashes the specified kart, for the specified time */ + void squash(int idKart, float time) { - int id = (int)gen->GetArgDWord(0); - float time = gen->GetArgFloat(1); - AbstractKart* kart = World::getWorld()->getKart(id); + AbstractKart* kart = World::getWorld()->getKart(idKart); kart->setSquash(time, 0.5); //0.5 * max speed is new max for squashed duration } - //Teleports the kart to the specified Vec3 location - void teleportKart(asIScriptGeneric *gen) + + /** Teleports the kart to the specified Vec3 location */ + void teleport(int idKart, Vec3* position) { - int id = (int)gen->GetArgDWord(0); - Vec3 *position = (Vec3*)gen->GetArgAddress(1); + // TODO: must we delete "position" ? - float x = position->getX(); - float y = position->getY(); - float z = position->getZ(); - - AbstractKart* kart = World::getWorld()->getKart(id); - kart->setXYZ(btVector3(x, y, z)); + AbstractKart* kart = World::getWorld()->getKart(idKart); + kart->setXYZ(*position); unsigned int index = World::getWorld()->getRescuePositionIndex(kart); btTransform s = World::getWorld()->getRescueTransform(index); const btVector3 &xyz = s.getOrigin(); s.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), 0.0f)); World::getWorld()->moveKartTo(kart, s); } - //Attempts to project kart to the given 2D location, to the position - //with height 0, at a 45 degree angle. - void jumpKartTo(asIScriptGeneric *gen) - { - //attempts to project kart to target destination - //at present, assumes both initial and target location are - //on the same horizontal plane (z=k plane) and projects - //at a 45 degree angle. - int id = (int)gen->GetArgDWord(0); - float x = gen->GetArgFloat(1); - float y = gen->GetArgFloat(2); - //float velocity = gen->GetArgFloat(3); - //angle = pi/4 so t = v/(root 2 * g) - //d = t * v/root 2 so d = v^2/(2g) => v = root(2dg) - //component in x = component in y = root (dg) - AbstractKart* kart = World::getWorld()->getKart(id); - Vec3 pos = kart->getXYZ(); - float dx = x - pos[0]; - float dy = y - pos[2]; //blender uses xyz, bullet xzy - float d = (sqrtf(dx*dx + dy*dy)); - float normalized_dx = dx / d; - float normalized_dy = dy / d; - float g = 9.81f; - float velocity = sqrtf(d * g); - - kart->setVelocity(btVector3(velocity * normalized_dx, velocity, velocity * normalized_dy)); + /** Attempts to project kart to the given 2D location, to the position + * with height 0, at a 45 degree angle. + */ + // TODO: not sure what this function is for + //void jumpTo(asIScriptGeneric *gen) + //{ + // //attempts to project kart to target destination + // //at present, assumes both initial and target location are + // //on the same horizontal plane (z=k plane) and projects + // //at a 45 degree angle. + // int id = (int)gen->GetArgDWord(0); + // + // float x = gen->GetArgFloat(1); + // float y = gen->GetArgFloat(2); + // //float velocity = gen->GetArgFloat(3); + // //angle = pi/4 so t = v/(root 2 * g) + // //d = t * v/root 2 so d = v^2/(2g) => v = root(2dg) + // //component in x = component in y = root (dg) + // AbstractKart* kart = World::getWorld()->getKart(id); + // Vec3 pos = kart->getXYZ(); + // float dx = x - pos[0]; + // float dy = y - pos[2]; //blender uses xyz, bullet xzy + // float d = (sqrtf(dx*dx + dy*dy)); + // float normalized_dx = dx / d; + // float normalized_dy = dy / d; + // float g = 9.81f; + // float velocity = sqrtf(d * g); + // + // kart->setVelocity(btVector3(velocity * normalized_dx, velocity, velocity * normalized_dy)); + //} + + /** Returns the location of the corresponding kart. */ + Vec3 getLocation(int idKart) + { + AbstractKart* kart = World::getWorld()->getKart(idKart); + return kart->getXYZ(); } - //Bind kart location - void getKartLocation(asIScriptGeneric *gen) + + /** Sets the kart's velocity to the specified value. */ + void setVelocity(int idKart, Vec3* position) { - int id = (int)gen->GetArgDWord(0); - - AbstractKart* kart = World::getWorld()->getKart(id); - Vec3 kart_loc = kart->getXYZ(); - void *pointer = &kart_loc; - - gen->SetReturnObject(pointer); - } - //Bind setter for velocity - void setVelocity(asIScriptGeneric *gen) - { - int id = (int)gen->GetArgDWord(0); - Vec3 *position = (Vec3*)gen->GetArgAddress(1); - float x = position->getX(); float y = position->getY(); float z = position->getZ(); - AbstractKart* kart = World::getWorld()->getKart(id); + AbstractKart* kart = World::getWorld()->getKart(idKart); kart->setVelocity(btVector3(x, y, z)); } + + /** @}*/ + /** @}*/ + void registerScriptFunctions(asIScriptEngine *engine) { int r; - engine->SetDefaultNamespace("Karts"); - r = engine->RegisterGlobalFunction("void squashKart(int id, float time)", asFUNCTION(squashKart), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void teleportKart(int id, Vec3 &in)", asFUNCTION(teleportKart), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void setVelocity(int id, Vec3 &in)", asFUNCTION(setVelocity), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void jumpKartTo(int id, float x, float y)", asFUNCTION(jumpKartTo), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("Vec3 getKartLocation(int id)", asFUNCTION(getKartLocation), asCALL_GENERIC); assert(r >= 0); + engine->SetDefaultNamespace("Kart"); + r = engine->RegisterGlobalFunction("void squash(int id, float time)", asFUNCTION(squash), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void teleport(int id, const Vec3 &in)", asFUNCTION(teleport), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void setVelocity(int id, const Vec3 &in)", asFUNCTION(setVelocity), asCALL_CDECL); assert(r >= 0); + //r = engine->RegisterGlobalFunction("void jumpTo(int id, float x, float y)", asFUNCTION(jumpTo), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("Vec3 getLocation(int id)", asFUNCTION(getLocation), asCALL_CDECL); assert(r >= 0); } } + +/** \cond DOXYGEN_IGNORE */ } +/** \endcond */ diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index 7cfb2602b..fce2a3dfa 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -38,14 +38,6 @@ namespace Scripting namespace Track { - //register callbacks - // TODO: move this out of Track namespace, it's not specific to tracks - asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine, std::string function_name) - { - asIScriptFunction *func; - func = engine->GetModule(0)->GetFunctionByDecl(function_name.c_str()); - return func; - } /* void disableAnimation(std::string *name, void *memory) { diff --git a/src/scriptengine/script_utils.cpp b/src/scriptengine/script_utils.cpp new file mode 100644 index 000000000..fcb19efad --- /dev/null +++ b/src/scriptengine/script_utils.cpp @@ -0,0 +1,165 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014-2015 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "script_track.hpp" + +#include "animations/three_d_animation.hpp" +#include "input/device_manager.hpp" +#include "input/input_device.hpp" +#include "input/input_manager.hpp" +#include "modes/world.hpp" +#include "states_screens/dialogs/tutorial_message_dialog.hpp" +#include "tracks/track.hpp" +#include "tracks/track_object.hpp" +#include "tracks/track_object_manager.hpp" + +#include +#include "scriptarray.hpp" + +#include +#include //debug + +/** \cond DOXYGEN_IGNORE */ +namespace Scripting +{ + /** \endcond */ + + namespace Utils + { + /** \addtogroup Scripting + * @{ + */ + /** \addtogroup Utils + * @{ + */ + // TODO: build these variations with variadic templates? + + /** Replaces placeholders with values. Note, in angelscript, omit the trailing number. + * e.g. Utils::insertValues("Hello %s !", "world"); + */ + std::string insertValues(std::string* formatString, std::string* arg1) + { + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(formatString->c_str()), + StringUtils::utf8_to_wide(arg1->c_str())); + + return StringUtils::wide_to_utf8(out.c_str()); + } + + /** Replaces placeholders with values. Note, in angelscript, omit the trailing number. + * e.g. Utils::insertValues("Hello %s %s !", "John", "Doe"); + */ + std::string insertValues(std::string* formatString, std::string* arg1, std::string* arg2) + { + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(formatString->c_str()), + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str())); + + return StringUtils::wide_to_utf8(out.c_str()); + } + + /** Replaces placeholders with values. Note, in angelscript, omit the trailing number. + * e.g. Utils::insertValues("Hello %s %s %s !", "Mr", "John", "Doe"); + */ + std::string insertValues(std::string* formatString, std::string* arg1, std::string* arg2, + std::string* arg3) + { + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(formatString->c_str()), + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str()), + StringUtils::utf8_to_wide(arg3->c_str())); + + return StringUtils::wide_to_utf8(out.c_str()); + } + + /** Replaces placeholders with values. Note, in angelscript, omit the trailing number. + * e.g. Utils::insertValues("%s %s %s %s !", "Hello", "Mr", "John", "Doe"); + */ + std::string insertValues(std::string* formatString, std::string* arg1, std::string* arg2, + std::string* arg3, std::string* arg4) + { + irr::core::stringw out = StringUtils::insertValues(StringUtils::utf8_to_wide(formatString->c_str()), + StringUtils::utf8_to_wide(arg1->c_str()), + StringUtils::utf8_to_wide(arg2->c_str()), + StringUtils::utf8_to_wide(arg3->c_str()), + StringUtils::utf8_to_wide(arg4->c_str())); + + return StringUtils::wide_to_utf8(out.c_str()); + } + + /** Log to the console */ + void logInfo(std::string* log) + { + Log::info("Script", "%s", log->c_str()); + } + + /** Log warning to the console */ + void logWarning(std::string* log) + { + Log::warn("Script", "%s", log->c_str()); + } + + /** Log error to the console */ + void logError(std::string* log) + { + Log::error("Script", "%s", log->c_str()); + } + /** @}*/ + /** @}*/ + + // UNDOCUMENTED PROXIES : Use proxies to have different signatures, then redirect to the + // documented function whose name is exposed in angelscript (these proxies exist so that + // angelscript can properly resolve overloads, but doxygen can still generate the right docs + /** \cond DOXYGEN_IGNORE */ + std::string proxy_insertValues1(std::string* formatString, std::string* arg1) + { + return insertValues(formatString, arg1); + } + std::string proxy_insertValues2(std::string* formatString, std::string* arg1, std::string* arg2) + { + return insertValues(formatString, arg1, arg2); + } + std::string proxy_insertValues3(std::string* formatString, std::string* arg1, std::string* arg2, + std::string* arg3) + { + return insertValues(formatString, arg1, arg2, arg3); + } + std::string proxy_insertValues4(std::string* formatString, std::string* arg1, std::string* arg2, + std::string* arg3, std::string* arg4) + { + return insertValues(formatString, arg1, arg2, arg3, arg4); + } + /** \endcond */ + + void registerScriptFunctions(asIScriptEngine *engine) + { + int r; // of type asERetCodes + engine->SetDefaultNamespace("Utils"); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in)", asFUNCTION(proxy_insertValues1), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues2), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues3), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues4), asCALL_CDECL); assert(r >= 0); + + r = engine->RegisterGlobalFunction("void logInfo(const string &in)", asFUNCTION(logInfo), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void logWarning(const string &in)", asFUNCTION(logWarning), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void logError(const string &in)", asFUNCTION(logError), asCALL_CDECL); assert(r >= 0); + } + } + +/** \cond DOXYGEN_IGNORE */ +} +/** \endcond */ diff --git a/src/scriptengine/script_utils.hpp b/src/scriptengine/script_utils.hpp new file mode 100644 index 000000000..8920d8d36 --- /dev/null +++ b/src/scriptengine/script_utils.hpp @@ -0,0 +1,34 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014-2015 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_SCRIPT_UTILS_HPP +#define HEADER_SCRIPT_UTILS_HPP + +#include + +#include + +namespace Scripting +{ + namespace Utils + { + void registerScriptFunctions(asIScriptEngine *engine); + } + +} +#endif diff --git a/src/scriptengine/scriptvec3.cpp b/src/scriptengine/scriptvec3.cpp index 0f7bc9306..1be851d6f 100644 --- a/src/scriptengine/scriptvec3.cpp +++ b/src/scriptengine/scriptvec3.cpp @@ -27,40 +27,44 @@ namespace Scripting { - - void Constructor(void *memory) { // Initialize the pre-allocated memory by calling the // object constructor with the placement-new operator new(memory)Vec3(); } + void Destructor(void *memory) { // Uninitialize the memory by calling the object destructor ((Vec3*)memory)->~Vec3(); } - void ConstructVector3FromFloats(float a, float b, float c, void *memory){ + + void ConstructVector3FromFloats(float a, float b, float c, void *memory) + { //Constructor using 3 floats new (memory)(Vec3)(Vec3(a, b, c)); } + //Print for debugging purposes void printVec3(asIScriptGeneric *gen) { Vec3 *script_vec3 = (Vec3*)gen->GetArgObject(0); std::cout << script_vec3->getX() << "," << script_vec3->getY() << "," << script_vec3->getZ() << std::endl; } + void RegisterVec3(asIScriptEngine *engine) { int r; - r = engine->RegisterObjectType("Vec3", sizeof(Vec3), asOBJ_VALUE); assert(r >= 0); + r = engine->RegisterObjectType("Vec3", sizeof(Vec3), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert(r >= 0); // Register the behaviours r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Constructor), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Destructor), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectMethod("Vec3", "Vec3 &opAssign(const Vec3 &in)", asMETHODPR(Vec3, operator =, (const Vec3&), Vec3&), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTION(ConstructVector3FromFloats), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterGlobalFunction("void printVec3(Vec3 a)", asFUNCTION(printVec3), asCALL_GENERIC); assert(r >= 0); - - + r = engine->RegisterGlobalFunction("void printVec3(Vec3 a)", asFUNCTION(printVec3), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float &getX()", asMETHOD(Vec3, getX), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float &getY()", asMETHOD(Vec3, getY), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float &getZ()", asMETHOD(Vec3, getZ), asCALL_THISCALL); assert(r >= 0); } } From a29fa5c4a3ec7a391500357546ab0a96f4f343c4 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 10 May 2015 19:19:35 -0400 Subject: [PATCH 23/44] More work on scripting --- src/physics/physical_object.hpp | 32 +- src/scriptengine/script_physics.cpp | 1 + src/scriptengine/script_track.cpp | 353 +++++++++++++---------- src/scriptengine/script_utils.cpp | 11 + src/tracks/track_object.hpp | 50 +++- src/tracks/track_object_presentation.cpp | 19 +- src/tracks/track_object_presentation.hpp | 2 + 7 files changed, 295 insertions(+), 173 deletions(-) diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index 7d4dd1158..1b7dbce87 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -185,10 +185,6 @@ public: * it. */ bool isExplodeKartObject () const { return m_explode_kart; } // ------------------------------------------------------------------------ - /** Returns true if this object should cause a kart that touches it to - * be flattened. */ - bool isFlattenKartObject () const { return m_flatten_kart; } - // ------------------------------------------------------------------------ /** Sets the interaction type */ void setInteraction(std::string interaction); // ------------------------------------------------------------------------ @@ -197,6 +193,34 @@ public: // ------------------------------------------------------------------------ /** Add body to dynamic world */ void addBody(); + // ------------------------------------------------------------------------ + // Methods usable by scripts + + /** + * \addtogroup Scripting + * @{ + * \addtogroup Scripting_Track Track + * @{ + * \addtogroup Scripting_PhysicalObject PhysicalObject (script binding) + * Type returned by trackObject.getPhysicalObject() + * @{ + */ + /** Returns true if this object should cause a kart that touches it to + * be flattened. */ + bool isFlattenKartObject() const { return m_flatten_kart; } + void disable(void *memory) + { + ((PhysicalObject*)(memory))->removeBody(); + } + + //enables track object passed from the script + void enable(void *memory) + { + ((PhysicalObject*)(memory))->addBody(); + } + /** @} */ + /** @} */ + /** @} */ LEAK_CHECK() }; // PhysicalObject diff --git a/src/scriptengine/script_physics.cpp b/src/scriptengine/script_physics.cpp index 60fc0ce68..22adb3a1c 100644 --- a/src/scriptengine/script_physics.cpp +++ b/src/scriptengine/script_physics.cpp @@ -29,6 +29,7 @@ namespace Scripting namespace Physics { //Creates an explosion animation at specified Vec3 location + // TODO: does this even belong in Physics? void createExplosion(asIScriptGeneric *gen) { //TODO: allow different types? sand etc diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index fce2a3dfa..9f7c4294a 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -29,141 +29,68 @@ #include "tracks/track_object_manager.hpp" #include - #include -#include //debug +/** \cond DOXYGEN_IGNORE */ namespace Scripting { - + /** \endcond */ namespace Track { + /** \addtogroup Scripting + * @{ + */ + /** \addtogroup Scripting_Track Track + * @{ + */ + /* void disableAnimation(std::string *name, void *memory) { - std::string *str = name; - std::string type = "mesh"; - World::getWorld()->getTrack()->getTrackObjectManager()->disable(*str, type); - }*/ - //disables track object passed from the script - void disable(void *memory) - { - ((PhysicalObject*)(memory))->removeBody(); - } - //enables track object passed from the script - void enable(void *memory) - { - ((PhysicalObject*)(memory))->addBody(); - } - //pause an animation - void setPaused(bool mode, void *memory) - { - ((ThreeDAnimation*)(memory))->setPaused(mode); - } - //move objects of type TrackObjectPresentation, to the specified location - void movePresentation(Vec3 *new_pos, void *memory) - { - core::vector3df xyz = new_pos->toIrrVector(); - core::vector3df hpr = core::vector3df(0, 0, 0); - core::vector3df scale = core::vector3df(1, 1, 1); - ((TrackObjectPresentation*)(memory))->move(xyz, hpr, scale, false); - } - //stop a sound - void stop(void *memory) - { - ((TrackObjectPresentationSound*)memory)->stopSound(); - } - //play the specified sound once - void playOnce(void *memory) - { - ((TrackObjectPresentationSound*)memory)->triggerSound(false); //false = once - } - //play the specified sound continuously - void playLoop(void *memory) - { - ((TrackObjectPresentationSound*)memory)->triggerSound(true); //true = loop - } - //sets a loop for an animation (skeletal) - void setLoop(int start, int end, void *memory) - { - ((TrackObjectPresentationMesh*)(memory))->setLoop(start,end); - } - //sets the current frame for a skeletal animation - void setCurrentFrame(int frame,void *memory) - { - ((TrackObjectPresentationMesh*)(memory))->setCurrentFrame(frame); - } - //getter for current frame in a skeletal animation - void getCurrentFrame(void *memory) - { - ((TrackObjectPresentationMesh*)(memory))->getCurrentFrame(); - } - //getter for key binding for player action enums - void getKeyBinding(asIScriptGeneric *gen) - { - int Enum_value = (int)gen->GetArgDWord(0); - InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw control; - PlayerAction ScriptAction = (PlayerAction)Enum_value; - control = config->getBindingAsString(ScriptAction); - std::string key = std::string(irr::core::stringc(control).c_str()); - void *key_pointer = &key; - gen->SetReturnObject(key_pointer); - } - //generic track object getter, Entry point of track objects into scripts - void getTrackObject(asIScriptGeneric *gen) - { - std::string *str = (std::string*)gen->GetArgAddress(0); - TrackObject* t_obj = World::getWorld()->getTrack()->getTrackObjectManager()->getTrackObject(*str); - gen->SetReturnObject(t_obj); - } - //runs the script specified by the given string - void runScript(asIScriptGeneric *gen) - { - std::string *str = (std::string*)gen->GetArgAddress(0); - ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - script_engine->runScript(*str); - } - /*TrackObject* getTrackObject(std::string *name) - { - TrackObject* t_obj = World::getWorld()->getTrack()->getTrackObjectManager()->getTrackObject(*name); - return t_obj; + std::string *str = name; + std::string type = "mesh"; + World::getWorld()->getTrack()->getTrackObjectManager()->disable(*str, type); }*/ - - // Displays the message specified in displayMessage( string message ) within the script - void displayMessage(asIScriptGeneric *gen) + /** + * Get a track object by ID. + * @return An object of type @ref Scripting_TrackObject + */ + TrackObject* getTrackObject(std::string* objID) { - std::string *input = (std::string*)gen->GetArgAddress(0); - irr::core::stringw out = StringUtils::utf8_to_wide(input->c_str()); - new TutorialMessageDialog((out), true); + return World::getWorld()->getTrack()->getTrackObjectManager()->getTrackObject(*objID); } - //generic disable method for track objects + + // TODO: assign types for documentation + /** Generic disable method for track objects */ void disableTrackObject(asIScriptGeneric *gen) { std::string *str = (std::string*)gen->GetArgAddress(0); World::getWorld()->getTrack()->getTrackObjectManager()->disable(*str); } - //generic enable method for track objects + + /** Generic enable method for track objects */ void enableTrackObject(asIScriptGeneric *gen) { std::string *str = (std::string*)gen->GetArgAddress(0); World::getWorld()->getTrack()->getTrackObjectManager()->enable(*str); } - //disables an action trigger of specified ID + + /** Disables an action trigger of specified ID */ void disableTrigger(asIScriptGeneric *gen) { std::string *str = (std::string*)gen->GetArgAddress(0); World::getWorld()->getTrack()->getTrackObjectManager()->disable(*str); } - //enables an action trigger of specified ID + + /** Enables an action trigger of specified ID */ void enableTrigger(asIScriptGeneric *gen) { std::string *str = (std::string*)gen->GetArgAddress(0); World::getWorld()->getTrack()->getTrackObjectManager()->enable(*str); } - //Creates a trigger at the specified location + + /** Creates a trigger at the specified location */ void createTrigger(asIScriptGeneric *gen) { std::string *script_name = (std::string*)gen->GetArgAddress(0); @@ -182,77 +109,193 @@ namespace Scripting tobj->setID(*script_name); World::getWorld()->getTrack()->getTrackObjectManager()->insertObject(tobj); } + } + + /** \cond DOXYGEN_IGNORE */ + namespace Track + { + /** \endcond */ + + // ----------- TrackObjectPresentationMesh methods ----------- + // TODO: this method is WRONG, we should in most cases move not the presentation but the entire object + //void movePresentation(Vec3 *new_pos, void *memory) + //{ + // core::vector3df xyz = new_pos->toIrrVector(); + // core::vector3df hpr = core::vector3df(0, 0, 0); + // core::vector3df scale = core::vector3df(1, 1, 1); + // ((TrackObjectPresentation*)(memory))->move(xyz, hpr, scale, false); + //} + + namespace Mesh + { + /** + * \addtogroup Scripting_Mesh Mesh (script binding) + * Type returned by trackObject.getMesh() + * @{ + */ + + /** Sets a loop for a skeletal animation */ + // TODO: can we use a type and avoid void* ? + void setLoop(int start, int end /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) + { + ((TrackObjectPresentationMesh*)(memory))->setLoop(start, end); + } + + /** Sets the current frame for a skeletal animation */ + void setCurrentFrame(int frame /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) + { + ((TrackObjectPresentationMesh*)(memory))->setCurrentFrame(frame); + } + + /** Get current frame in a skeletal animation */ + int getCurrentFrame(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) + { + return ((TrackObjectPresentationMesh*)(memory))->getCurrentFrame(); + } + /** @} */ + } + + // ----------- Animator Object methods ----------- + + namespace Animator + { + /** + * \addtogroup Scripting_Animator Animator (script binding) + * Type returned by trackObject.getIPOAnimator() + * @{ + */ + + /** Pause/resumes a curve-based animation */ + void setPaused(bool mode /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) + { + ((ThreeDAnimation*)(memory))->setPaused(mode); + } + + /** @} */ + } + + // ----------- Sound Object methods ----------- + + namespace SoundEmitter + { + /** + * @addtogroup Scripting_SoundEmitter SoundEmitter (script binding) + * Type returned by trackObject.getSoundEmitter() + * @{ + */ + + // TODO: adjust all signatures to type "void*" parameters if possible + /** Stop a sound */ + void stop(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) + { + ((TrackObjectPresentationSound*)memory)->stopSound(); + } + + /** Play the specified sound once */ + void playOnce(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) + { + ((TrackObjectPresentationSound*)memory)->triggerSound(false); //false = once + } + + /** Play the specified sound continuously */ + void playLoop(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) + { + ((TrackObjectPresentationSound*)memory)->triggerSound(true); //true = loop + } + /** @} */ + } + + // ----------- ParticleEmitter Object methods ----------- + + namespace ParticleEmitter + { + /** + * @addtogroup Scripting_ParticleEmitter ParticleEmitter (script binding) + * Type returned by trackObject.getParticleEmitter() + * @{ + */ + + // TODO: adjust all signatures to type "void*" parameters if possible + /** Stop particle emission */ + void stop(/** \cond DOXYGEN_IGNORE */ void *memory /** \endcond */) + { + ((TrackObjectPresentationParticles*)memory)->stop(); + } + + /** Play the specified sound once */ + void setEmissionRate(float rate /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) + { + ((TrackObjectPresentationParticles*)memory)->setRate(rate); + } + /** @} */ + } + + /** @}*/ + /** @}*/ void registerScriptFunctions(asIScriptEngine *engine) { int r; engine->SetDefaultNamespace("Track"); - r = engine->RegisterGlobalFunction("void disable(string &in)", asFUNCTION(disableTrackObject), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void enable(string &in)", asFUNCTION(enableTrackObject), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void enableTrigger(string &in)", asFUNCTION(enableTrigger), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void disableTrigger(string &in)", asFUNCTION(disableTrigger), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void createTrigger(string &in,Vec3 &in, float distance)", + r = engine->RegisterGlobalFunction("void disableTrackObject(const string &in)", asFUNCTION(disableTrackObject), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("void enableTrackObject(const string &in)", asFUNCTION(enableTrackObject), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("void enableTrigger(const string &in)", asFUNCTION(enableTrigger), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("void disableTrigger(const string &in)", asFUNCTION(disableTrigger), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("void createTrigger(const string &in,Vec3 &in, float distance)", asFUNCTION(createTrigger), asCALL_GENERIC); assert(r >= 0); - - /* - //Test singleton, and various calling conventions - // Register the track object manager as a singleton. The script will access it through the global property - //r = engine->RegisterObjectType("TrackObjectManager", 0, asOBJ_REF | asOBJ_NOHANDLE); assert(r >= 0); - r = engine->RegisterObjectType("TrackObjectManager", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - - // Register the track object manager's methods - TrackObjectManager* track_obj_manager = World::getWorld()->getTrack()->getTrackObjectManager(); - r = engine->RegisterGlobalProperty("TrackObjectManager track_obj_manager", track_obj_manager); assert(r >= 0); - //r = engine->RegisterObjectMethod("TrackObjectManager", "void disable(string name , string type)", asMETHOD(TrackObjectManager, disable), asCALL_THISCALL); assert(r >= 0); - //r = engine->RegisterObjectMethod("TrackObjectManager", "void disable(string &in name)", asFUNCTION(disableAnimation), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterObjectMethod("TrackObjectManager", "void disable(string &in)", asFUNCTION(disableAnimation), asCALL_CDECL_OBJLAST); assert(r >= 0); - */ - //TrackObject + r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterObjectType("TrackObject", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterGlobalFunction("TrackObject @getTrackObject(string &in)", asFUNCTION(getTrackObject), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterObjectMethod("TrackObject", "void setEnable(bool status)", asMETHOD(TrackObject, setEnable), asCALL_THISCALL); assert(r >= 0); - - - //PhysicalObject r = engine->RegisterObjectType("PhysicalObject", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterObjectMethod("TrackObject", "PhysicalObject @getPhysicalObject()", asMETHOD(TrackObject, getPhysicalObjectForScript), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("PhysicalObject", "bool isFlattener()", asMETHOD(PhysicalObject, isFlattenKartObject), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("PhysicalObject", "void disable()", asFUNCTION(disable), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("PhysicalObject", "void enable()", asFUNCTION(enable), asCALL_CDECL_OBJLAST); assert(r >= 0); - - - //Mesh or Skeletal Animation - r = engine->RegisterObjectType("Mesh", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterObjectMethod("TrackObject", "Mesh @getMesh()", asMETHOD(TrackObject, getMesh), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("Mesh", "void setLoop(int start, int end)", asFUNCTION(setLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("Mesh", "int getCurrentFrame()", asFUNCTION(getCurrentFrame), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("Mesh", "void setCurrentFrame(int frame)", asFUNCTION(setCurrentFrame), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("Mesh", "void move(Vec3 &in)", asFUNCTION(movePresentation), asCALL_CDECL_OBJLAST); assert(r >= 0); - - //Particle Emitter + r = engine->RegisterObjectType("Mesh", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); // TrackObjectPresentationMesh r = engine->RegisterObjectType("ParticleEmitter", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterObjectMethod("TrackObject", "ParticleEmitter @getParticleEmitter()", asMETHOD(TrackObject, getParticles), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("ParticleEmitter", "void move(Vec3 &in)", asFUNCTION(movePresentation), asCALL_CDECL_OBJLAST); assert(r >= 0); - //Sound Effect + // TrackObject + r = engine->RegisterObjectMethod("TrackObject", "void setEnable(bool status)", asMETHOD(TrackObject, setEnable), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("TrackObject", "SoundEmitter@ getSoundEmitter()", asMETHOD(TrackObject, getSoundEmitter), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("TrackObject", "PhysicalObject@ getPhysics()", asMETHOD(TrackObject, getPhysics), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("TrackObject", "Mesh@ getMesh()", asMETHOD(TrackObject, getMesh), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("TrackObject", "ParticleEmitter@ getParticleEmitter()", asMETHOD(TrackObject, getParticleEmitter), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("TrackObject", "Animator@ getIPOAnimator()", asMETHOD(TrackObject, getIPOAnimator), asCALL_THISCALL); assert(r >= 0); + // TODO: add move method + + // PhysicalObject + r = engine->RegisterObjectMethod("PhysicalObject", "bool isFlattenKartObject()", asMETHOD(PhysicalObject, isFlattenKartObject), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("PhysicalObject", "void disable()", asMETHOD(PhysicalObject, disable), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("PhysicalObject", "void enable()", asMETHOD(PhysicalObject, enable), asCALL_THISCALL); assert(r >= 0); + + // TrackObjectPresentationMesh (Mesh or Skeletal Animation) + r = engine->RegisterObjectMethod("Mesh", "void setLoop(int start, int end)", asFUNCTION(Mesh::setLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Mesh", "int getCurrentFrame()", asFUNCTION(Mesh::getCurrentFrame), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Mesh", "void setCurrentFrame(int frame)", asFUNCTION(Mesh::setCurrentFrame), asCALL_CDECL_OBJLAST); assert(r >= 0); + //r = engine->RegisterObjectMethod("Mesh", "void move(Vec3 &in)", asFUNCTION(movePresentation), asCALL_CDECL_OBJLAST); assert(r >= 0); + + // Particle Emitter + r = engine->RegisterObjectMethod("ParticleEmitter", "void stop()", asFUNCTION(ParticleEmitter::stop), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("ParticleEmitter", "void setEmissionRate(float)", asFUNCTION(ParticleEmitter::setEmissionRate), asCALL_CDECL_OBJLAST); assert(r >= 0); + + // Sound Effect r = engine->RegisterObjectType("SoundEmitter", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterObjectMethod("TrackObject", "SoundEmitter @getSoundEmitter()", asMETHOD(TrackObject, getSound), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("SoundEmitter", "void move(Vec3 &in)", asFUNCTION(movePresentation), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("SoundEmitter", "void stop()", asFUNCTION(stop), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("SoundEmitter", "void playOnce()", asFUNCTION(playOnce), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("SoundEmitter", "void playLoop()", asFUNCTION(playLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); + //r = engine->RegisterObjectMethod("SoundEmitter", "void move(Vec3 &in)", asFUNCTION(movePresentation), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("SoundEmitter", "void stop()", asFUNCTION(SoundEmitter::stop), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("SoundEmitter", "void playOnce()", asFUNCTION(SoundEmitter::playOnce), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("SoundEmitter", "void playLoop()", asFUNCTION(SoundEmitter::playLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); - //Curve based Animation + // Curve based Animation r = engine->RegisterObjectType("Animator", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterObjectMethod("TrackObject", "Animator @getAnimator()", asMETHOD(TrackObject, getAnimatorForScript), asCALL_THISCALL); assert(r >= 0); //fails due to insufficient visibility to scripts TODO : Decide whether to fix visibility or introduce wrappers //r = engine->RegisterObjectMethod("Animator", "void setPaused(bool mode)", asMETHOD(ThreeDAnimation, setPaused), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("Animator", "void setPaused(bool mode)", asFUNCTION( setPaused ), asCALL_CDECL_OBJLAST); assert(r >= 0); - - r = engine->RegisterGlobalFunction("void runScript(string &in)", asFUNCTION(runScript), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("Animator", "void setPaused(bool mode)", asFUNCTION(Animator::setPaused), asCALL_CDECL_OBJLAST); assert(r >= 0); + // TODO: add method to set current frame + // TODO: add method to launch playback from frame X to frame Y + // TODO: add method to register onAnimationComplete notifications ? } + +/** \cond DOXYGEN_IGNORE */ } } +/** \endcond */ + diff --git a/src/scriptengine/script_utils.cpp b/src/scriptengine/script_utils.cpp index fcb19efad..ca057b936 100644 --- a/src/scriptengine/script_utils.cpp +++ b/src/scriptengine/script_utils.cpp @@ -101,6 +101,15 @@ namespace Scripting return StringUtils::wide_to_utf8(out.c_str()); } + /** Runs the script specified by the given string */ + // TODO: type arguments + void runScript(asIScriptGeneric *gen) + { + std::string *str = (std::string*)gen->GetArgAddress(0); + ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); + script_engine->runScript(*str); + } + /** Log to the console */ void logInfo(std::string* log) { @@ -153,6 +162,8 @@ namespace Scripting r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues2), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues3), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues4), asCALL_CDECL); assert(r >= 0); + + r = engine->RegisterGlobalFunction("void runScript(string &in)", asFUNCTION(runScript), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void logInfo(const string &in)", asFUNCTION(logInfo), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void logWarning(const string &in)", asFUNCTION(logWarning), asCALL_CDECL); assert(r >= 0); diff --git a/src/tracks/track_object.hpp b/src/tracks/track_object.hpp index edc2e4b93..db0c39ebc 100644 --- a/src/tracks/track_object.hpp +++ b/src/tracks/track_object.hpp @@ -109,7 +109,6 @@ public: bool isAbsoluteCoord); virtual void reset(); - void setEnable(bool mode); const core::vector3df& getPosition() const; const core::vector3df getAbsolutePosition() const; const core::vector3df& getRotation() const; @@ -151,8 +150,6 @@ public: const PhysicalObject* getPhysicalObject() const { return m_physical_object; } // ------------------------------------------------------------------------ PhysicalObject* getPhysicalObject() { return m_physical_object; } - //Due to above overload AngelScript cannot decide which function to bind - PhysicalObject* getPhysicalObjectForScript() { return m_physical_object; } // ------------------------------------------------------------------------ const core::vector3df getInitXYZ() const { return m_init_xyz; } // ------------------------------------------------------------------------ @@ -165,20 +162,47 @@ public: // ------------------------------------------------------------------------ template const T* getPresentation() const { return dynamic_cast(m_presentation); } - - //specialized getters for scripts - TrackObjectPresentationMesh* getMesh(){ return getPresentation(); } - - TrackObjectPresentationParticles* getParticles(){ return getPresentation(); } - - TrackObjectPresentationSound* getSound(){ return getPresentation(); } - + // ------------------------------------------------------------------------ + // Methods usable by scripts + /** + * \addtogroup Scripting + * @{ + * \addtogroup Scripting_Track Track + * @{ + * \addtogroup Scripting_TrackObject TrackObject (script binding) + * @{ + */ + /** Should only be used on mesh track objects. + * On the script side, the returned object is of type : @ref Scripting_Mesh + */ + TrackObjectPresentationMesh* getMesh() { return getPresentation(); } + /** Should only be used on particle emitter track objects. + * On the script side, the returned object is of type : @ref Scripting_ParticleEmitter + */ + TrackObjectPresentationParticles* getParticleEmitter() { return getPresentation(); } + /** Should only be used on sound emitter track objects. + * On the script side, the returned object is of type : @ref Scripting_SoundEmitter + */ + TrackObjectPresentationSound* getSoundEmitter(){ return getPresentation(); } + // For angelscript. Needs to be named something different than getAnimator since it's overloaded. + /** Should only be used on TrackObjects that use curve-based animation. + * On the script side, the returned object is of type : @ref Scripting_Animator + */ + ThreeDAnimation* getIPOAnimator() { return m_animator; } + // For angelscript. Needs to be named something different than getPhysicalObject since it's overloaded. + /** Get the physics representation of an object. + * On the script side, the returned object is of type : @ref Scripting_PhysicalObject + */ + PhysicalObject* getPhysics() { return m_physical_object; } + /** Hide or show the object */ + void setEnable(bool mode); + /* @} */ + /* @} */ + /* @} */ // ------------------------------------------------------------------------ ThreeDAnimation* getAnimator() { return m_animator; } // ------------------------------------------------------------------------ const ThreeDAnimation* getAnimator() const { return m_animator; } - //Due to above overload AngelScript cannot decide which function to bind - ThreeDAnimation* getAnimatorForScript() { return m_animator; } // ------------------------------------------------------------------------ void setPaused(bool mode){ m_animator->setPaused(mode); } // ------------------------------------------------------------------------ diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 9780c1242..8008cc94e 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -826,7 +826,24 @@ void TrackObjectPresentationParticles::triggerParticles() m_emitter->setParticleType(m_emitter->getParticlesInfo()); } } // triggerParticles - +// ---------------------------------------------------------------------------- +void TrackObjectPresentationParticles::stop() +{ + if (m_emitter != NULL) + { + m_emitter->setCreationRateAbsolute(0.0f); + m_emitter->clearParticles(); + } +} +// ---------------------------------------------------------------------------- +void TrackObjectPresentationParticles::setRate(float rate) +{ + if (m_emitter != NULL) + { + m_emitter->setCreationRateAbsolute(rate); + m_emitter->setParticleType(m_emitter->getParticlesInfo()); + } +} // ---------------------------------------------------------------------------- TrackObjectPresentationLight::TrackObjectPresentationLight( const XMLNode& xml_node, diff --git a/src/tracks/track_object_presentation.hpp b/src/tracks/track_object_presentation.hpp index 95a6ebaf8..cfda97efa 100644 --- a/src/tracks/track_object_presentation.hpp +++ b/src/tracks/track_object_presentation.hpp @@ -312,6 +312,8 @@ public: virtual void update(float dt) OVERRIDE; void triggerParticles(); + void stop(); + void setRate(float rate); // ------------------------------------------------------------------------ /** Returns the trigger condition for this object. */ std::string& getTriggerCondition() { return m_trigger_condition; } From f269b44208507799aaeb993d255d26b03e153a52 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 10 May 2015 20:04:44 -0400 Subject: [PATCH 24/44] SCripting branch fix --- src/scriptengine/script_track.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index 9f7c4294a..d1107deb8 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -238,6 +238,14 @@ namespace Scripting int r; engine->SetDefaultNamespace("Track"); + + r = engine->RegisterObjectType("TrackObject", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); + r = engine->RegisterObjectType("PhysicalObject", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); + r = engine->RegisterObjectType("Mesh", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); // TrackObjectPresentationMesh + r = engine->RegisterObjectType("ParticleEmitter", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); + r = engine->RegisterObjectType("SoundEmitter", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); + r = engine->RegisterObjectType("Animator", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); + r = engine->RegisterGlobalFunction("void disableTrackObject(const string &in)", asFUNCTION(disableTrackObject), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void enableTrackObject(const string &in)", asFUNCTION(enableTrackObject), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("void enableTrigger(const string &in)", asFUNCTION(enableTrigger), asCALL_GENERIC); assert(r >= 0); @@ -246,11 +254,6 @@ namespace Scripting asFUNCTION(createTrigger), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0); - r = engine->RegisterObjectType("TrackObject", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterObjectType("PhysicalObject", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterObjectType("Mesh", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); // TrackObjectPresentationMesh - r = engine->RegisterObjectType("ParticleEmitter", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - // TrackObject r = engine->RegisterObjectMethod("TrackObject", "void setEnable(bool status)", asMETHOD(TrackObject, setEnable), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("TrackObject", "SoundEmitter@ getSoundEmitter()", asMETHOD(TrackObject, getSoundEmitter), asCALL_THISCALL); assert(r >= 0); @@ -276,16 +279,13 @@ namespace Scripting r = engine->RegisterObjectMethod("ParticleEmitter", "void setEmissionRate(float)", asFUNCTION(ParticleEmitter::setEmissionRate), asCALL_CDECL_OBJLAST); assert(r >= 0); // Sound Effect - r = engine->RegisterObjectType("SoundEmitter", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); //r = engine->RegisterObjectMethod("SoundEmitter", "void move(Vec3 &in)", asFUNCTION(movePresentation), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectMethod("SoundEmitter", "void stop()", asFUNCTION(SoundEmitter::stop), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectMethod("SoundEmitter", "void playOnce()", asFUNCTION(SoundEmitter::playOnce), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectMethod("SoundEmitter", "void playLoop()", asFUNCTION(SoundEmitter::playLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); - // Curve based Animation - r = engine->RegisterObjectType("Animator", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); //fails due to insufficient visibility to scripts TODO : Decide whether to fix visibility or introduce wrappers //r = engine->RegisterObjectMethod("Animator", "void setPaused(bool mode)", asMETHOD(ThreeDAnimation, setPaused), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("Animator", "void setPaused(bool mode)", asFUNCTION(Animator::setPaused), asCALL_CDECL_OBJLAST); assert(r >= 0); From c48037984ddb71e33ddba507bbab57b735dd6413 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Mon, 11 May 2015 19:40:43 -0400 Subject: [PATCH 25/44] Update to latest angelscript --- lib/angelscript/include/angelscript.h | 2602 ++++++------ lib/angelscript/projects/cmake/CMakeLists.txt | 221 +- lib/angelscript/source/as_array.h | 104 +- lib/angelscript/source/as_atomic.cpp | 18 +- lib/angelscript/source/as_builder.cpp | 1172 ++++-- lib/angelscript/source/as_builder.h | 33 +- lib/angelscript/source/as_bytecode.cpp | 79 +- lib/angelscript/source/as_callfunc.cpp | 359 +- lib/angelscript/source/as_callfunc.h | 28 +- lib/angelscript/source/as_callfunc_arm.cpp | 138 +- lib/angelscript/source/as_callfunc_arm_gcc.S | 11 +- .../source/as_callfunc_arm_msvc.asm | 4 +- lib/angelscript/source/as_callfunc_arm_vita.S | 480 +++ lib/angelscript/source/as_callfunc_mips.cpp | 6 +- lib/angelscript/source/as_callfunc_ppc.cpp | 6 +- lib/angelscript/source/as_callfunc_ppc_64.cpp | 9 +- lib/angelscript/source/as_callfunc_sh4.cpp | 6 +- .../source/as_callfunc_x64_gcc.cpp | 65 +- .../source/as_callfunc_x64_mingw.cpp | 42 +- .../source/as_callfunc_x64_msvc.cpp | 56 +- lib/angelscript/source/as_callfunc_x86.cpp | 49 +- lib/angelscript/source/as_callfunc_xenon.cpp | 6 +- lib/angelscript/source/as_compiler.cpp | 3701 +++++++++++------ lib/angelscript/source/as_compiler.h | 47 +- lib/angelscript/source/as_config.h | 217 +- lib/angelscript/source/as_configgroup.cpp | 103 +- lib/angelscript/source/as_configgroup.h | 11 +- lib/angelscript/source/as_context.cpp | 407 +- lib/angelscript/source/as_context.h | 25 +- lib/angelscript/source/as_datatype.cpp | 218 +- lib/angelscript/source/as_datatype.h | 58 +- lib/angelscript/source/as_debug.h | 15 +- lib/angelscript/source/as_gc.cpp | 102 +- lib/angelscript/source/as_gc.h | 5 +- lib/angelscript/source/as_globalproperty.cpp | 160 +- lib/angelscript/source/as_memory.cpp | 93 +- lib/angelscript/source/as_memory.h | 32 +- lib/angelscript/source/as_module.cpp | 599 ++- lib/angelscript/source/as_module.h | 46 +- lib/angelscript/source/as_namespace.h | 5 +- lib/angelscript/source/as_objecttype.cpp | 417 +- lib/angelscript/source/as_objecttype.h | 56 +- lib/angelscript/source/as_parser.cpp | 482 ++- lib/angelscript/source/as_parser.h | 10 +- lib/angelscript/source/as_property.h | 19 +- lib/angelscript/source/as_restore.cpp | 549 ++- lib/angelscript/source/as_restore.h | 9 +- lib/angelscript/source/as_scriptengine.cpp | 2574 +++++++----- lib/angelscript/source/as_scriptengine.h | 183 +- lib/angelscript/source/as_scriptfunction.cpp | 627 +-- lib/angelscript/source/as_scriptfunction.h | 45 +- lib/angelscript/source/as_scriptnode.h | 5 +- lib/angelscript/source/as_scriptobject.cpp | 170 +- lib/angelscript/source/as_scriptobject.h | 44 +- lib/angelscript/source/as_string.cpp | 57 +- lib/angelscript/source/as_string.h | 9 +- lib/angelscript/source/as_string_util.cpp | 2 +- lib/angelscript/source/as_symboltable.h | 119 +- lib/angelscript/source/as_texts.h | 196 +- lib/angelscript/source/as_thread.cpp | 23 +- lib/angelscript/source/as_thread.h | 2 +- lib/angelscript/source/as_tokendef.h | 7 +- lib/angelscript/source/as_tokenizer.cpp | 7 +- lib/angelscript/source/as_typeinfo.cpp | 4 +- lib/angelscript/source/as_typeinfo.h | 5 +- src/scriptengine/script_engine.cpp | 2 +- src/scriptengine/scriptarray.cpp | 316 +- src/scriptengine/scriptarray.hpp | 60 +- src/scriptengine/scriptstdstring.cpp | 221 +- src/scriptengine/scriptstdstring.hpp | 19 +- src/scriptengine/scriptstdstring_utils.cpp | 135 +- 71 files changed, 11405 insertions(+), 6307 deletions(-) create mode 100644 lib/angelscript/source/as_callfunc_arm_vita.S diff --git a/lib/angelscript/include/angelscript.h b/lib/angelscript/include/angelscript.h index ff2ffe13f..7f8c63bce 100644 --- a/lib/angelscript/include/angelscript.h +++ b/lib/angelscript/include/angelscript.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -35,8 +35,7 @@ // The script engine interface // -//TODO Add Build Target for Scripting Language and provide lib and include using that -//This file will then be removed from this directory. + #ifndef ANGELSCRIPT_H #define ANGELSCRIPT_H @@ -59,8 +58,8 @@ BEGIN_AS_NAMESPACE // AngelScript version -#define ANGELSCRIPT_VERSION 22801 -#define ANGELSCRIPT_VERSION_STRING "2.28.1" +#define ANGELSCRIPT_VERSION 23000 +#define ANGELSCRIPT_VERSION_STRING "2.30.0 WIP" // Data types @@ -81,253 +80,273 @@ class asILockableSharedBool; // Return codes enum asERetCodes { - asSUCCESS = 0, - asERROR = -1, - asCONTEXT_ACTIVE = -2, - asCONTEXT_NOT_FINISHED = -3, - asCONTEXT_NOT_PREPARED = -4, - asINVALID_ARG = -5, - asNO_FUNCTION = -6, - asNOT_SUPPORTED = -7, - asINVALID_NAME = -8, - asNAME_TAKEN = -9, - asINVALID_DECLARATION = -10, - asINVALID_OBJECT = -11, - asINVALID_TYPE = -12, - asALREADY_REGISTERED = -13, - asMULTIPLE_FUNCTIONS = -14, - asNO_MODULE = -15, - asNO_GLOBAL_VAR = -16, - asINVALID_CONFIGURATION = -17, - asINVALID_INTERFACE = -18, - asCANT_BIND_ALL_FUNCTIONS = -19, - asLOWER_ARRAY_DIMENSION_NOT_REGISTERED = -20, - asWRONG_CONFIG_GROUP = -21, - asCONFIG_GROUP_IS_IN_USE = -22, - asILLEGAL_BEHAVIOUR_FOR_TYPE = -23, - asWRONG_CALLING_CONV = -24, - asBUILD_IN_PROGRESS = -25, - asINIT_GLOBAL_VARS_FAILED = -26, - asOUT_OF_MEMORY = -27 + asSUCCESS = 0, + asERROR = -1, + asCONTEXT_ACTIVE = -2, + asCONTEXT_NOT_FINISHED = -3, + asCONTEXT_NOT_PREPARED = -4, + asINVALID_ARG = -5, + asNO_FUNCTION = -6, + asNOT_SUPPORTED = -7, + asINVALID_NAME = -8, + asNAME_TAKEN = -9, + asINVALID_DECLARATION = -10, + asINVALID_OBJECT = -11, + asINVALID_TYPE = -12, + asALREADY_REGISTERED = -13, + asMULTIPLE_FUNCTIONS = -14, + asNO_MODULE = -15, + asNO_GLOBAL_VAR = -16, + asINVALID_CONFIGURATION = -17, + asINVALID_INTERFACE = -18, + asCANT_BIND_ALL_FUNCTIONS = -19, + asLOWER_ARRAY_DIMENSION_NOT_REGISTERED = -20, + asWRONG_CONFIG_GROUP = -21, + asCONFIG_GROUP_IS_IN_USE = -22, + asILLEGAL_BEHAVIOUR_FOR_TYPE = -23, + asWRONG_CALLING_CONV = -24, + asBUILD_IN_PROGRESS = -25, + asINIT_GLOBAL_VARS_FAILED = -26, + asOUT_OF_MEMORY = -27, + asMODULE_IS_IN_USE = -28 }; // Engine properties enum asEEngineProp { - asEP_ALLOW_UNSAFE_REFERENCES = 1, - asEP_OPTIMIZE_BYTECODE = 2, - asEP_COPY_SCRIPT_SECTIONS = 3, - asEP_MAX_STACK_SIZE = 4, - asEP_USE_CHARACTER_LITERALS = 5, - asEP_ALLOW_MULTILINE_STRINGS = 6, - asEP_ALLOW_IMPLICIT_HANDLE_TYPES = 7, - asEP_BUILD_WITHOUT_LINE_CUES = 8, - asEP_INIT_GLOBAL_VARS_AFTER_BUILD = 9, - asEP_REQUIRE_ENUM_SCOPE = 10, - asEP_SCRIPT_SCANNER = 11, - asEP_INCLUDE_JIT_INSTRUCTIONS = 12, - asEP_STRING_ENCODING = 13, - asEP_PROPERTY_ACCESSOR_MODE = 14, - asEP_EXPAND_DEF_ARRAY_TO_TMPL = 15, - asEP_AUTO_GARBAGE_COLLECT = 16, - asEP_DISALLOW_GLOBAL_VARS = 17, - asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT = 18, - asEP_COMPILER_WARNINGS = 19, - asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE = 20, + asEP_ALLOW_UNSAFE_REFERENCES = 1, + asEP_OPTIMIZE_BYTECODE = 2, + asEP_COPY_SCRIPT_SECTIONS = 3, + asEP_MAX_STACK_SIZE = 4, + asEP_USE_CHARACTER_LITERALS = 5, + asEP_ALLOW_MULTILINE_STRINGS = 6, + asEP_ALLOW_IMPLICIT_HANDLE_TYPES = 7, + asEP_BUILD_WITHOUT_LINE_CUES = 8, + asEP_INIT_GLOBAL_VARS_AFTER_BUILD = 9, + asEP_REQUIRE_ENUM_SCOPE = 10, + asEP_SCRIPT_SCANNER = 11, + asEP_INCLUDE_JIT_INSTRUCTIONS = 12, + asEP_STRING_ENCODING = 13, + asEP_PROPERTY_ACCESSOR_MODE = 14, + asEP_EXPAND_DEF_ARRAY_TO_TMPL = 15, + asEP_AUTO_GARBAGE_COLLECT = 16, + asEP_DISALLOW_GLOBAL_VARS = 17, + asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT = 18, + asEP_COMPILER_WARNINGS = 19, + asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE = 20, + asEP_ALTER_SYNTAX_NAMED_ARGS = 21, + asEP_DISABLE_INTEGER_DIVISION = 22, + asEP_DISALLOW_EMPTY_LIST_ELEMENTS = 23, + asEP_PRIVATE_PROP_AS_PROTECTED = 24, - asEP_LAST_PROPERTY + asEP_LAST_PROPERTY }; // Calling conventions enum asECallConvTypes { - asCALL_CDECL = 0, - asCALL_STDCALL = 1, - asCALL_THISCALL_ASGLOBAL = 2, - asCALL_THISCALL = 3, - asCALL_CDECL_OBJLAST = 4, - asCALL_CDECL_OBJFIRST = 5, - asCALL_GENERIC = 6 + asCALL_CDECL = 0, + asCALL_STDCALL = 1, + asCALL_THISCALL_ASGLOBAL = 2, + asCALL_THISCALL = 3, + asCALL_CDECL_OBJLAST = 4, + asCALL_CDECL_OBJFIRST = 5, + asCALL_GENERIC = 6, + asCALL_THISCALL_OBJLAST = 7, + asCALL_THISCALL_OBJFIRST = 8 }; // Object type flags enum asEObjTypeFlags { - asOBJ_REF = 0x01, - asOBJ_VALUE = 0x02, - asOBJ_GC = 0x04, - asOBJ_POD = 0x08, - asOBJ_NOHANDLE = 0x10, - asOBJ_SCOPED = 0x20, - asOBJ_TEMPLATE = 0x40, - asOBJ_ASHANDLE = 0x80, - asOBJ_APP_CLASS = 0x100, - asOBJ_APP_CLASS_CONSTRUCTOR = 0x200, - asOBJ_APP_CLASS_DESTRUCTOR = 0x400, - asOBJ_APP_CLASS_ASSIGNMENT = 0x800, - asOBJ_APP_CLASS_COPY_CONSTRUCTOR = 0x1000, - asOBJ_APP_CLASS_C = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR), - asOBJ_APP_CLASS_CD = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR), - asOBJ_APP_CLASS_CA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), - asOBJ_APP_CLASS_CK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), - asOBJ_APP_CLASS_CDA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), - asOBJ_APP_CLASS_CDK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), - asOBJ_APP_CLASS_CAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), - asOBJ_APP_CLASS_CDAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), - asOBJ_APP_CLASS_D = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR), - asOBJ_APP_CLASS_DA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), - asOBJ_APP_CLASS_DK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), - asOBJ_APP_CLASS_DAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), - asOBJ_APP_CLASS_A = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_ASSIGNMENT), - asOBJ_APP_CLASS_AK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), - asOBJ_APP_CLASS_K = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), - asOBJ_APP_PRIMITIVE = 0x2000, - asOBJ_APP_FLOAT = 0x4000, - asOBJ_APP_CLASS_ALLINTS = 0x8000, - asOBJ_APP_CLASS_ALLFLOATS = 0x10000, - asOBJ_NOCOUNT = 0x20000, - asOBJ_APP_CLASS_ALIGN8 = 0x40000, - asOBJ_MASK_VALID_FLAGS = 0x7FFFF, - asOBJ_SCRIPT_OBJECT = 0x80000, - asOBJ_SHARED = 0x100000, - asOBJ_NOINHERIT = 0x200000, - asOBJ_SCRIPT_FUNCTION = 0x400000 + asOBJ_REF = (1<<0), + asOBJ_VALUE = (1<<1), + asOBJ_GC = (1<<2), + asOBJ_POD = (1<<3), + asOBJ_NOHANDLE = (1<<4), + asOBJ_SCOPED = (1<<5), + asOBJ_TEMPLATE = (1<<6), + asOBJ_ASHANDLE = (1<<7), + asOBJ_APP_CLASS = (1<<8), + asOBJ_APP_CLASS_CONSTRUCTOR = (1<<9), + asOBJ_APP_CLASS_DESTRUCTOR = (1<<10), + asOBJ_APP_CLASS_ASSIGNMENT = (1<<11), + asOBJ_APP_CLASS_COPY_CONSTRUCTOR = (1<<12), + asOBJ_APP_CLASS_C = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR), + asOBJ_APP_CLASS_CD = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR), + asOBJ_APP_CLASS_CA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_CK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_CDA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_CDK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_CAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_CDAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_D = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR), + asOBJ_APP_CLASS_DA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_DK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_DAK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_A = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_AK = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_ASSIGNMENT + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_CLASS_K = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_COPY_CONSTRUCTOR), + asOBJ_APP_PRIMITIVE = (1<<13), + asOBJ_APP_FLOAT = (1<<14), + asOBJ_APP_ARRAY = (1<<15), + asOBJ_APP_CLASS_ALLINTS = (1<<16), + asOBJ_APP_CLASS_ALLFLOATS = (1<<17), + asOBJ_NOCOUNT = (1<<18), + asOBJ_APP_CLASS_ALIGN8 = (1<<19), + asOBJ_IMPLICIT_HANDLE = (1<<20), + asOBJ_MASK_VALID_FLAGS = 0x1FFFFF, + // Internal flags + asOBJ_SCRIPT_OBJECT = (1<<21), + asOBJ_SHARED = (1<<22), + asOBJ_NOINHERIT = (1<<23), + asOBJ_SCRIPT_FUNCTION = (1<<24), + asOBJ_LIST_PATTERN = (1<<25), + asOBJ_ENUM = (1<<26), + asOBJ_TEMPLATE_SUBTYPE = (1<<27), + asOBJ_TYPEDEF = (1<<28), + asOBJ_ABSTRACT = (1<<29), + asOBJ_APP_ALIGN16 = (1<<30) }; // Behaviours enum asEBehaviours { - // Value object memory management - asBEHAVE_CONSTRUCT, - asBEHAVE_LIST_CONSTRUCT, - asBEHAVE_DESTRUCT, + // Value object memory management + asBEHAVE_CONSTRUCT, + asBEHAVE_LIST_CONSTRUCT, + asBEHAVE_DESTRUCT, - // Reference object memory management - asBEHAVE_FACTORY, - asBEHAVE_LIST_FACTORY, - asBEHAVE_ADDREF, - asBEHAVE_RELEASE, - asBEHAVE_GET_WEAKREF_FLAG, + // Reference object memory management + asBEHAVE_FACTORY, + asBEHAVE_LIST_FACTORY, + asBEHAVE_ADDREF, + asBEHAVE_RELEASE, + asBEHAVE_GET_WEAKREF_FLAG, - // Object operators - asBEHAVE_VALUE_CAST, - asBEHAVE_IMPLICIT_VALUE_CAST, - asBEHAVE_REF_CAST, - asBEHAVE_IMPLICIT_REF_CAST, - asBEHAVE_TEMPLATE_CALLBACK, + // Object operators +#ifdef AS_DEPRECATED + // Deprecated since 2.30.0, 2014-10-24 + asBEHAVE_VALUE_CAST, + asBEHAVE_IMPLICIT_VALUE_CAST, + // Deprecated since 2.30.0, 2014-12-30 + asBEHAVE_REF_CAST, + asBEHAVE_IMPLICIT_REF_CAST, +#endif + asBEHAVE_TEMPLATE_CALLBACK, - // Garbage collection behaviours - asBEHAVE_FIRST_GC, - asBEHAVE_GETREFCOUNT = asBEHAVE_FIRST_GC, - asBEHAVE_SETGCFLAG, - asBEHAVE_GETGCFLAG, - asBEHAVE_ENUMREFS, - asBEHAVE_RELEASEREFS, - asBEHAVE_LAST_GC = asBEHAVE_RELEASEREFS, + // Garbage collection behaviours + asBEHAVE_FIRST_GC, + asBEHAVE_GETREFCOUNT = asBEHAVE_FIRST_GC, + asBEHAVE_SETGCFLAG, + asBEHAVE_GETGCFLAG, + asBEHAVE_ENUMREFS, + asBEHAVE_RELEASEREFS, + asBEHAVE_LAST_GC = asBEHAVE_RELEASEREFS, - asBEHAVE_MAX + asBEHAVE_MAX }; // Context states enum asEContextState { - asEXECUTION_FINISHED = 0, - asEXECUTION_SUSPENDED = 1, - asEXECUTION_ABORTED = 2, - asEXECUTION_EXCEPTION = 3, - asEXECUTION_PREPARED = 4, - asEXECUTION_UNINITIALIZED = 5, - asEXECUTION_ACTIVE = 6, - asEXECUTION_ERROR = 7 + asEXECUTION_FINISHED = 0, + asEXECUTION_SUSPENDED = 1, + asEXECUTION_ABORTED = 2, + asEXECUTION_EXCEPTION = 3, + asEXECUTION_PREPARED = 4, + asEXECUTION_UNINITIALIZED = 5, + asEXECUTION_ACTIVE = 6, + asEXECUTION_ERROR = 7 }; // Message types enum asEMsgType { - asMSGTYPE_ERROR = 0, - asMSGTYPE_WARNING = 1, - asMSGTYPE_INFORMATION = 2 + asMSGTYPE_ERROR = 0, + asMSGTYPE_WARNING = 1, + asMSGTYPE_INFORMATION = 2 }; // Garbage collector flags enum asEGCFlags { - asGC_FULL_CYCLE = 1, - asGC_ONE_STEP = 2, - asGC_DESTROY_GARBAGE = 4, - asGC_DETECT_GARBAGE = 8 + asGC_FULL_CYCLE = 1, + asGC_ONE_STEP = 2, + asGC_DESTROY_GARBAGE = 4, + asGC_DETECT_GARBAGE = 8 }; // Token classes enum asETokenClass { - asTC_UNKNOWN = 0, - asTC_KEYWORD = 1, - asTC_VALUE = 2, - asTC_IDENTIFIER = 3, - asTC_COMMENT = 4, - asTC_WHITESPACE = 5 + asTC_UNKNOWN = 0, + asTC_KEYWORD = 1, + asTC_VALUE = 2, + asTC_IDENTIFIER = 3, + asTC_COMMENT = 4, + asTC_WHITESPACE = 5 }; // Type id flags enum asETypeIdFlags { - asTYPEID_VOID = 0, - asTYPEID_BOOL = 1, - asTYPEID_INT8 = 2, - asTYPEID_INT16 = 3, - asTYPEID_INT32 = 4, - asTYPEID_INT64 = 5, - asTYPEID_UINT8 = 6, - asTYPEID_UINT16 = 7, - asTYPEID_UINT32 = 8, - asTYPEID_UINT64 = 9, - asTYPEID_FLOAT = 10, - asTYPEID_DOUBLE = 11, - asTYPEID_OBJHANDLE = 0x40000000, - asTYPEID_HANDLETOCONST = 0x20000000, - asTYPEID_MASK_OBJECT = 0x1C000000, - asTYPEID_APPOBJECT = 0x04000000, - asTYPEID_SCRIPTOBJECT = 0x08000000, - asTYPEID_TEMPLATE = 0x10000000, - asTYPEID_MASK_SEQNBR = 0x03FFFFFF + asTYPEID_VOID = 0, + asTYPEID_BOOL = 1, + asTYPEID_INT8 = 2, + asTYPEID_INT16 = 3, + asTYPEID_INT32 = 4, + asTYPEID_INT64 = 5, + asTYPEID_UINT8 = 6, + asTYPEID_UINT16 = 7, + asTYPEID_UINT32 = 8, + asTYPEID_UINT64 = 9, + asTYPEID_FLOAT = 10, + asTYPEID_DOUBLE = 11, + asTYPEID_OBJHANDLE = 0x40000000, + asTYPEID_HANDLETOCONST = 0x20000000, + asTYPEID_MASK_OBJECT = 0x1C000000, + asTYPEID_APPOBJECT = 0x04000000, + asTYPEID_SCRIPTOBJECT = 0x08000000, + asTYPEID_TEMPLATE = 0x10000000, + asTYPEID_MASK_SEQNBR = 0x03FFFFFF }; // Type modifiers enum asETypeModifiers { - asTM_NONE = 0, - asTM_INREF = 1, - asTM_OUTREF = 2, - asTM_INOUTREF = 3, - asTM_CONST = 4 + asTM_NONE = 0, + asTM_INREF = 1, + asTM_OUTREF = 2, + asTM_INOUTREF = 3, + asTM_CONST = 4 }; // GetModule flags enum asEGMFlags { - asGM_ONLY_IF_EXISTS = 0, - asGM_CREATE_IF_NOT_EXISTS = 1, - asGM_ALWAYS_CREATE = 2 + asGM_ONLY_IF_EXISTS = 0, + asGM_CREATE_IF_NOT_EXISTS = 1, + asGM_ALWAYS_CREATE = 2 }; // Compile flags enum asECompileFlags { - asCOMP_ADD_TO_MODULE = 1 + asCOMP_ADD_TO_MODULE = 1 }; // Function types enum asEFuncType { - asFUNC_DUMMY =-1, - asFUNC_SYSTEM = 0, - asFUNC_SCRIPT = 1, - asFUNC_INTERFACE = 2, - asFUNC_VIRTUAL = 3, - asFUNC_FUNCDEF = 4, - asFUNC_IMPORTED = 5, - asFUNC_DELEGATE = 6 + asFUNC_DUMMY =-1, + asFUNC_SYSTEM = 0, + asFUNC_SCRIPT = 1, + asFUNC_INTERFACE = 2, + asFUNC_VIRTUAL = 3, + asFUNC_FUNCDEF = 4, + asFUNC_IMPORTED = 5, + asFUNC_DELEGATE = 6 }; // @@ -341,12 +360,12 @@ typedef unsigned char asBYTE; typedef unsigned short asWORD; typedef unsigned int asUINT; #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__S3E__) - // size_t is not really correct, since it only guaranteed to be large enough to hold the segment size. - // For example, on 16bit systems the size_t may be 16bits only even if pointers are 32bit. But nobody - // is likely to use MSVC6 to compile for 16bit systems anymore, so this should be ok. - typedef size_t asPWORD; + // size_t is not really correct, since it only guaranteed to be large enough to hold the segment size. + // For example, on 16bit systems the size_t may be 16bits only even if pointers are 32bit. But nobody + // is likely to use MSVC6 to compile for 16bit systems anymore, so this should be ok. + typedef size_t asPWORD; #else - typedef uintptr_t asPWORD; + typedef uintptr_t asPWORD; #endif #ifdef __LP64__ typedef unsigned int asDWORD; @@ -354,7 +373,7 @@ typedef unsigned int asUINT; typedef long asINT64; #else typedef unsigned long asDWORD; - #if defined(__GNUC__) || defined(__MWERKS__) + #if defined(__GNUC__) || defined(__MWERKS__) || defined(__SUNPRO_CC) || defined(__psp2__) typedef uint64_t asQWORD; typedef int64_t asINT64; #else @@ -365,9 +384,9 @@ typedef unsigned int asUINT; // Is the target a 64bit system? #if defined(__LP64__) || defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) - #ifndef AS_64BIT_PTR - #define AS_64BIT_PTR - #endif + #ifndef AS_64BIT_PTR + #define AS_64BIT_PTR + #endif #endif typedef void (*asFUNCTION_t)(); @@ -379,6 +398,20 @@ typedef void (*asCLEANMODULEFUNC_t)(asIScriptModule *); typedef void (*asCLEANCONTEXTFUNC_t)(asIScriptContext *); typedef void (*asCLEANFUNCTIONFUNC_t)(asIScriptFunction *); typedef void (*asCLEANOBJECTTYPEFUNC_t)(asIObjectType *); +typedef void (*asCLEANSCRIPTOBJECTFUNC_t)(asIScriptObject *); +typedef asIScriptContext *(*asREQUESTCONTEXTFUNC_t)(asIScriptEngine *, void *); +typedef void (*asRETURNCONTEXTFUNC_t)(asIScriptEngine *, asIScriptContext *, void *); + +// Check if the compiler can use C++11 features +#if !defined(_MSC_VER) || _MSC_VER >= 1700 // MSVC 2012 +#if !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) // gnuc 4.7 +#if !(defined(__GNUC__) && defined(__cplusplus) && __cplusplus < 201103L) // g++ -std=c++11 +#if !defined(__SUNPRO_CC) +#define AS_CAN_USE_CPP11 1 +#endif +#endif +#endif +#endif // This macro does basically the same thing as offsetof defined in stddef.h, but // GNUC should not complain about the usage as I'm not using 0 as the base pointer. @@ -402,29 +435,29 @@ typedef void (asCUnknownClass::*asMETHOD_t)(); struct asSFuncPtr { - asSFuncPtr(asBYTE f = 0) - { - for( size_t n = 0; n < sizeof(ptr.dummy); n++ ) - ptr.dummy[n] = 0; - flag = f; - } + asSFuncPtr(asBYTE f = 0) + { + for( size_t n = 0; n < sizeof(ptr.dummy); n++ ) + ptr.dummy[n] = 0; + flag = f; + } - void CopyMethodPtr(const void *mthdPtr, size_t size) - { - for( size_t n = 0; n < size; n++ ) - ptr.dummy[n] = reinterpret_cast(mthdPtr)[n]; - } + void CopyMethodPtr(const void *mthdPtr, size_t size) + { + for( size_t n = 0; n < size; n++ ) + ptr.dummy[n] = reinterpret_cast(mthdPtr)[n]; + } - union - { - // The largest known method point is 20 bytes (MSVC 64bit), - // but with 8byte alignment this becomes 24 bytes. So we need - // to be able to store at least that much. - char dummy[25]; - struct {asMETHOD_t mthd; char dummy[25-sizeof(asMETHOD_t)];} m; - struct {asFUNCTION_t func; char dummy[25-sizeof(asFUNCTION_t)];} f; - } ptr; - asBYTE flag; // 1 = generic, 2 = global func, 3 = method + union + { + // The largest known method point is 20 bytes (MSVC 64bit), + // but with 8byte alignment this becomes 24 bytes. So we need + // to be able to store at least that much. + char dummy[25]; + struct {asMETHOD_t mthd; char dummy[25-sizeof(asMETHOD_t)];} m; + struct {asFUNCTION_t func; char dummy[25-sizeof(asFUNCTION_t)];} f; + } ptr; + asBYTE flag; // 1 = generic, 2 = global func, 3 = method }; #if defined(__BORLANDC__) @@ -453,30 +486,30 @@ template struct asSFuncPtr { - asSFuncPtr(asBYTE f) - { - for( int n = 0; n < sizeof(ptr.dummy); n++ ) - ptr.dummy[n] = 0; - flag = f; - } + asSFuncPtr(asBYTE f) + { + for( int n = 0; n < sizeof(ptr.dummy); n++ ) + ptr.dummy[n] = 0; + flag = f; + } - union - { - char dummy[25]; // largest known class method pointer - struct {asFUNCTION_t func; char dummy[25-sizeof(asFUNCTION_t)];} f; - } ptr; - asBYTE flag; // 1 = generic, 2 = global func + union + { + char dummy[25]; // largest known class method pointer + struct {asFUNCTION_t func; char dummy[25-sizeof(asFUNCTION_t)];} f; + } ptr; + asBYTE flag; // 1 = generic, 2 = global func }; #endif struct asSMessageInfo { - const char *section; - int row; - int col; - asEMsgType type; - const char *message; + const char *section; + int row; + int col; + asEMsgType type; + const char *message; }; @@ -496,7 +529,7 @@ struct asSMessageInfo #else // statically linked library #define AS_API #endif -#elif defined(__GNUC__) +#elif defined(__GNUC__) #if defined(ANGELSCRIPT_EXPORT) #define AS_API __attribute__((visibility ("default"))) #else @@ -509,538 +542,625 @@ struct asSMessageInfo #ifndef ANGELSCRIPT_DLL_MANUAL_IMPORT extern "C" { - // Engine - AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version); - AS_API const char *asGetLibraryVersion(); - AS_API const char *asGetLibraryOptions(); + // Engine + AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version); + AS_API const char *asGetLibraryVersion(); + AS_API const char *asGetLibraryOptions(); - // Context - AS_API asIScriptContext *asGetActiveContext(); + // Context + AS_API asIScriptContext *asGetActiveContext(); - // Thread support - AS_API int asPrepareMultithread(asIThreadManager *externalMgr = 0); - AS_API void asUnprepareMultithread(); - AS_API asIThreadManager *asGetThreadManager(); - AS_API void asAcquireExclusiveLock(); - AS_API void asReleaseExclusiveLock(); - AS_API void asAcquireSharedLock(); - AS_API void asReleaseSharedLock(); - AS_API int asAtomicInc(int &value); - AS_API int asAtomicDec(int &value); - AS_API int asThreadCleanup(); + // Thread support + AS_API int asPrepareMultithread(asIThreadManager *externalMgr = 0); + AS_API void asUnprepareMultithread(); + AS_API asIThreadManager *asGetThreadManager(); + AS_API void asAcquireExclusiveLock(); + AS_API void asReleaseExclusiveLock(); + AS_API void asAcquireSharedLock(); + AS_API void asReleaseSharedLock(); + AS_API int asAtomicInc(int &value); + AS_API int asAtomicDec(int &value); + AS_API int asThreadCleanup(); - // Memory management - AS_API int asSetGlobalMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc); - AS_API int asResetGlobalMemoryFunctions(); + // Memory management + AS_API int asSetGlobalMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc); + AS_API int asResetGlobalMemoryFunctions(); + AS_API void *asAllocMem(size_t size); + AS_API void asFreeMem(void *mem); - // Auxiliary - AS_API asILockableSharedBool *asCreateLockableSharedBool(); + // Auxiliary + AS_API asILockableSharedBool *asCreateLockableSharedBool(); } #endif // ANGELSCRIPT_DLL_MANUAL_IMPORT +// Determine traits of a type for registration of value types +// Relies on C++11 features so it can not be used with non-compliant compilers +#ifdef AS_CAN_USE_CPP11 + +END_AS_NAMESPACE +#include +BEGIN_AS_NAMESPACE + +template +asUINT asGetTypeTraits() +{ +#if defined(_MSC_VER) || defined(_LIBCPP_TYPE_TRAITS) + // MSVC & XCode/Clang + // C++11 compliant code + bool hasConstructor = std::is_default_constructible::value && !std::is_trivially_default_constructible::value; + bool hasDestructor = std::is_destructible::value && !std::is_trivially_destructible::value; + bool hasAssignmentOperator = std::is_copy_assignable::value && !std::is_trivially_copy_assignable::value; + bool hasCopyConstructor = std::is_copy_constructible::value && !std::is_trivially_copy_constructible::value; +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + // gnuc 4.8+ + // gnuc is using a mix of C++11 standard and pre-standard templates + bool hasConstructor = std::is_default_constructible::value && !std::has_trivial_default_constructor::value; + bool hasDestructor = std::is_destructible::value && !std::is_trivially_destructible::value; + bool hasAssignmentOperator = std::is_copy_assignable::value && !std::has_trivial_copy_assign::value; + bool hasCopyConstructor = std::is_copy_constructible::value && !std::has_trivial_copy_constructor::value; +#else + // Not fully C++11 compliant. The has_trivial checks were used while the standard was still + // being elaborated, but were then removed in favor of the above is_trivially checks + // http://stackoverflow.com/questions/12702103/writing-code-that-works-when-has-trivial-destructor-is-defined-instead-of-is + // https://github.com/mozart/mozart2/issues/51 + bool hasConstructor = std::is_default_constructible::value && !std::has_trivial_default_constructor::value; + bool hasDestructor = std::is_destructible::value && !std::has_trivial_destructor::value; + bool hasAssignmentOperator = std::is_copy_assignable::value && !std::has_trivial_copy_assign::value; + bool hasCopyConstructor = std::is_copy_constructible::value && !std::has_trivial_copy_constructor::value; +#endif + bool isFloat = std::is_floating_point::value; + bool isPrimitive = std::is_integral::value || std::is_pointer::value || std::is_enum::value; + bool isClass = std::is_class::value; + bool isArray = std::is_array::value; + + if( isFloat ) + return asOBJ_APP_FLOAT; + if( isPrimitive ) + return asOBJ_APP_PRIMITIVE; + + if( isClass ) + { + asDWORD flags = asOBJ_APP_CLASS; + if( hasConstructor ) + flags |= asOBJ_APP_CLASS_CONSTRUCTOR; + if( hasDestructor ) + flags |= asOBJ_APP_CLASS_DESTRUCTOR; + if( hasAssignmentOperator ) + flags |= asOBJ_APP_CLASS_ASSIGNMENT; + if( hasCopyConstructor ) + flags |= asOBJ_APP_CLASS_COPY_CONSTRUCTOR; + return flags; + } + + if( isArray ) + return asOBJ_APP_ARRAY; + + // Unknown type traits + return 0; +} + +#endif // c++11 + // Interface declarations class asIScriptEngine { public: - // Memory management - virtual int AddRef() const = 0; - virtual int Release() const = 0; + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; + virtual int ShutDownAndRelease() = 0; - // Engine properties - virtual int SetEngineProperty(asEEngineProp property, asPWORD value) = 0; - virtual asPWORD GetEngineProperty(asEEngineProp property) const = 0; + // Engine properties + virtual int SetEngineProperty(asEEngineProp property, asPWORD value) = 0; + virtual asPWORD GetEngineProperty(asEEngineProp property) const = 0; - // Compiler messages - virtual int SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv) = 0; - virtual int ClearMessageCallback() = 0; - virtual int WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message) = 0; + // Compiler messages + virtual int SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv) = 0; + virtual int ClearMessageCallback() = 0; + virtual int WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message) = 0; - // JIT Compiler - virtual int SetJITCompiler(asIJITCompiler *compiler) = 0; - virtual asIJITCompiler *GetJITCompiler() const = 0; + // JIT Compiler + virtual int SetJITCompiler(asIJITCompiler *compiler) = 0; + virtual asIJITCompiler *GetJITCompiler() const = 0; - // Global functions - virtual int RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall = 0) = 0; - virtual asUINT GetGlobalFunctionCount() const = 0; - virtual asIScriptFunction *GetGlobalFunctionByIndex(asUINT index) const = 0; - virtual asIScriptFunction *GetGlobalFunctionByDecl(const char *declaration) const = 0; + // Global functions + virtual int RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall = 0) = 0; + virtual asUINT GetGlobalFunctionCount() const = 0; + virtual asIScriptFunction *GetGlobalFunctionByIndex(asUINT index) const = 0; + virtual asIScriptFunction *GetGlobalFunctionByDecl(const char *declaration) const = 0; - // Global properties - virtual int RegisterGlobalProperty(const char *declaration, void *pointer) = 0; - virtual asUINT GetGlobalPropertyCount() const = 0; - virtual int GetGlobalPropertyByIndex(asUINT index, const char **name, const char **nameSpace = 0, int *typeId = 0, bool *isConst = 0, const char **configGroup = 0, void **pointer = 0, asDWORD *accessMask = 0) const = 0; - virtual int GetGlobalPropertyIndexByName(const char *name) const = 0; - virtual int GetGlobalPropertyIndexByDecl(const char *decl) const = 0; + // Global properties + virtual int RegisterGlobalProperty(const char *declaration, void *pointer) = 0; + virtual asUINT GetGlobalPropertyCount() const = 0; + virtual int GetGlobalPropertyByIndex(asUINT index, const char **name, const char **nameSpace = 0, int *typeId = 0, bool *isConst = 0, const char **configGroup = 0, void **pointer = 0, asDWORD *accessMask = 0) const = 0; + virtual int GetGlobalPropertyIndexByName(const char *name) const = 0; + virtual int GetGlobalPropertyIndexByDecl(const char *decl) const = 0; - // Object types - virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags) = 0; - virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset) = 0; - virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) = 0; - virtual int RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall = 0) = 0; - virtual int RegisterInterface(const char *name) = 0; - virtual int RegisterInterfaceMethod(const char *intf, const char *declaration) = 0; - virtual asUINT GetObjectTypeCount() const = 0; - virtual asIObjectType *GetObjectTypeByIndex(asUINT index) const = 0; - virtual asIObjectType *GetObjectTypeByName(const char *name) const = 0; + // Object types + virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags) = 0; + virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset) = 0; + virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall = 0) = 0; + virtual int RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall = 0) = 0; + virtual int RegisterInterface(const char *name) = 0; + virtual int RegisterInterfaceMethod(const char *intf, const char *declaration) = 0; + virtual asUINT GetObjectTypeCount() const = 0; + virtual asIObjectType *GetObjectTypeByIndex(asUINT index) const = 0; + virtual asIObjectType *GetObjectTypeByName(const char *name) const = 0; + virtual asIObjectType *GetObjectTypeByDecl(const char *decl) const = 0; - // String factory - virtual int RegisterStringFactory(const char *datatype, const asSFuncPtr &factoryFunc, asDWORD callConv, void *objForThiscall = 0) = 0; - virtual int GetStringFactoryReturnTypeId() const = 0; + // String factory + virtual int RegisterStringFactory(const char *datatype, const asSFuncPtr &factoryFunc, asDWORD callConv, void *objForThiscall = 0) = 0; + virtual int GetStringFactoryReturnTypeId(asDWORD *flags = 0) const = 0; - // Default array type - virtual int RegisterDefaultArrayType(const char *type) = 0; - virtual int GetDefaultArrayTypeId() const = 0; + // Default array type + virtual int RegisterDefaultArrayType(const char *type) = 0; + virtual int GetDefaultArrayTypeId() const = 0; - // Enums - virtual int RegisterEnum(const char *type) = 0; - virtual int RegisterEnumValue(const char *type, const char *name, int value) = 0; - virtual asUINT GetEnumCount() const = 0; - virtual const char *GetEnumByIndex(asUINT index, int *enumTypeId, const char **nameSpace = 0, const char **configGroup = 0, asDWORD *accessMask = 0) const = 0; - virtual int GetEnumValueCount(int enumTypeId) const = 0; - virtual const char *GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const = 0; + // Enums + virtual int RegisterEnum(const char *type) = 0; + virtual int RegisterEnumValue(const char *type, const char *name, int value) = 0; + virtual asUINT GetEnumCount() const = 0; + virtual const char *GetEnumByIndex(asUINT index, int *enumTypeId, const char **nameSpace = 0, const char **configGroup = 0, asDWORD *accessMask = 0) const = 0; + virtual int GetEnumValueCount(int enumTypeId) const = 0; + virtual const char *GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const = 0; - // Funcdefs - virtual int RegisterFuncdef(const char *decl) = 0; - virtual asUINT GetFuncdefCount() const = 0; - virtual asIScriptFunction *GetFuncdefByIndex(asUINT index) const = 0; + // Funcdefs + virtual int RegisterFuncdef(const char *decl) = 0; + virtual asUINT GetFuncdefCount() const = 0; + virtual asIScriptFunction *GetFuncdefByIndex(asUINT index) const = 0; - // Typedefs - virtual int RegisterTypedef(const char *type, const char *decl) = 0; - virtual asUINT GetTypedefCount() const = 0; - virtual const char *GetTypedefByIndex(asUINT index, int *typeId, const char **nameSpace = 0, const char **configGroup = 0, asDWORD *accessMask = 0) const = 0; + // Typedefs + virtual int RegisterTypedef(const char *type, const char *decl) = 0; + virtual asUINT GetTypedefCount() const = 0; + virtual const char *GetTypedefByIndex(asUINT index, int *typeId, const char **nameSpace = 0, const char **configGroup = 0, asDWORD *accessMask = 0) const = 0; - // Configuration groups - virtual int BeginConfigGroup(const char *groupName) = 0; - virtual int EndConfigGroup() = 0; - virtual int RemoveConfigGroup(const char *groupName) = 0; - virtual asDWORD SetDefaultAccessMask(asDWORD defaultMask) = 0; - virtual int SetDefaultNamespace(const char *nameSpace) = 0; - virtual const char *GetDefaultNamespace() const = 0; + // Configuration groups + virtual int BeginConfigGroup(const char *groupName) = 0; + virtual int EndConfigGroup() = 0; + virtual int RemoveConfigGroup(const char *groupName) = 0; + virtual asDWORD SetDefaultAccessMask(asDWORD defaultMask) = 0; + virtual int SetDefaultNamespace(const char *nameSpace) = 0; + virtual const char *GetDefaultNamespace() const = 0; - // Script modules - virtual asIScriptModule *GetModule(const char *module, asEGMFlags flag = asGM_ONLY_IF_EXISTS) = 0; - virtual int DiscardModule(const char *module) = 0; - virtual asUINT GetModuleCount() const = 0; - virtual asIScriptModule *GetModuleByIndex(asUINT index) const = 0; + // Script modules + virtual asIScriptModule *GetModule(const char *module, asEGMFlags flag = asGM_ONLY_IF_EXISTS) = 0; + virtual int DiscardModule(const char *module) = 0; + virtual asUINT GetModuleCount() const = 0; + virtual asIScriptModule *GetModuleByIndex(asUINT index) const = 0; - // Script functions - virtual asIScriptFunction *GetFunctionById(int funcId) const = 0; - virtual asIScriptFunction *GetFuncDefFromTypeId(int typeId) const = 0; + // Script functions + virtual asIScriptFunction *GetFunctionById(int funcId) const = 0; + virtual asIScriptFunction *GetFuncDefFromTypeId(int typeId) const = 0; - // Type identification - virtual asIObjectType *GetObjectTypeById(int typeId) const = 0; - virtual int GetTypeIdByDecl(const char *decl) const = 0; - virtual const char *GetTypeDeclaration(int typeId, bool includeNamespace = false) const = 0; - virtual int GetSizeOfPrimitiveType(int typeId) const = 0; + // Type identification + virtual asIObjectType *GetObjectTypeById(int typeId) const = 0; + virtual int GetTypeIdByDecl(const char *decl) const = 0; + virtual const char *GetTypeDeclaration(int typeId, bool includeNamespace = false) const = 0; + virtual int GetSizeOfPrimitiveType(int typeId) const = 0; - // Script execution - virtual asIScriptContext *CreateContext() = 0; + // Script execution + virtual asIScriptContext *CreateContext() = 0; + virtual void *CreateScriptObject(const asIObjectType *type) = 0; + virtual void *CreateScriptObjectCopy(void *obj, const asIObjectType *type) = 0; + virtual void *CreateUninitializedScriptObject(const asIObjectType *type) = 0; + virtual asIScriptFunction *CreateDelegate(asIScriptFunction *func, void *obj) = 0; + virtual int AssignScriptObject(void *dstObj, void *srcObj, const asIObjectType *type) = 0; + virtual void ReleaseScriptObject(void *obj, const asIObjectType *type) = 0; + virtual void AddRefScriptObject(void *obj, const asIObjectType *type) = 0; + virtual int RefCastObject(void *obj, asIObjectType *fromType, asIObjectType *toType, void **newPtr, bool useOnlyImplicitCast = false) = 0; #ifdef AS_DEPRECATED - // Deprecated since 2.27.0, 2013-07-18 - virtual void *CreateScriptObject(int typeId) = 0; - virtual void *CreateScriptObjectCopy(void *obj, int typeId) = 0; - virtual void *CreateUninitializedScriptObject(int typeId) = 0; - virtual void AssignScriptObject(void *dstObj, void *srcObj, int typeId) = 0; - virtual void ReleaseScriptObject(void *obj, int typeId) = 0; - virtual void AddRefScriptObject(void *obj, int typeId) = 0; + // Deprecated since 2.30.0, 2014-11-04 + virtual bool IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) const = 0; #endif - virtual void *CreateScriptObject(const asIObjectType *type) = 0; - virtual void *CreateScriptObjectCopy(void *obj, const asIObjectType *type) = 0; - virtual void *CreateUninitializedScriptObject(const asIObjectType *type) = 0; - virtual asIScriptFunction *CreateDelegate(asIScriptFunction *func, void *obj) = 0; - virtual void AssignScriptObject(void *dstObj, void *srcObj, const asIObjectType *type) = 0; - virtual void ReleaseScriptObject(void *obj, const asIObjectType *type) = 0; - virtual void AddRefScriptObject(void *obj, const asIObjectType *type) = 0; - virtual bool IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) const = 0; - virtual asILockableSharedBool *GetWeakRefFlagOfScriptObject(void *obj, const asIObjectType *type) const = 0; + virtual asILockableSharedBool *GetWeakRefFlagOfScriptObject(void *obj, const asIObjectType *type) const = 0; - // String interpretation - virtual asETokenClass ParseToken(const char *string, size_t stringLength = 0, int *tokenLength = 0) const = 0; + // Context pooling + virtual asIScriptContext *RequestContext() = 0; + virtual void ReturnContext(asIScriptContext *ctx) = 0; + virtual int SetContextCallbacks(asREQUESTCONTEXTFUNC_t requestCtx, asRETURNCONTEXTFUNC_t returnCtx, void *param = 0) = 0; - // Garbage collection - virtual int GarbageCollect(asDWORD flags = asGC_FULL_CYCLE) = 0; - virtual void GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed = 0, asUINT *totalDetected = 0, asUINT *newObjects = 0, asUINT *totalNewDestroyed = 0) const = 0; - virtual int NotifyGarbageCollectorOfNewObject(void *obj, asIObjectType *type) = 0; - virtual int GetObjectInGC(asUINT idx, asUINT *seqNbr = 0, void **obj = 0, asIObjectType **type = 0) = 0; - virtual void GCEnumCallback(void *reference) = 0; + // String interpretation + virtual asETokenClass ParseToken(const char *string, size_t stringLength = 0, asUINT *tokenLength = 0) const = 0; - // User data - virtual void *SetUserData(void *data, asPWORD type = 0) = 0; - virtual void *GetUserData(asPWORD type = 0) const = 0; - virtual void SetEngineUserDataCleanupCallback(asCLEANENGINEFUNC_t callback, asPWORD type = 0) = 0; - virtual void SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback) = 0; - virtual void SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback) = 0; - virtual void SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback) = 0; - virtual void SetObjectTypeUserDataCleanupCallback(asCLEANOBJECTTYPEFUNC_t callback, asPWORD type = 0) = 0; + // Garbage collection + virtual int GarbageCollect(asDWORD flags = asGC_FULL_CYCLE, asUINT numIterations = 1) = 0; + virtual void GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed = 0, asUINT *totalDetected = 0, asUINT *newObjects = 0, asUINT *totalNewDestroyed = 0) const = 0; + virtual int NotifyGarbageCollectorOfNewObject(void *obj, asIObjectType *type) = 0; + virtual int GetObjectInGC(asUINT idx, asUINT *seqNbr = 0, void **obj = 0, asIObjectType **type = 0) = 0; + virtual void GCEnumCallback(void *reference) = 0; + + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; + virtual void SetEngineUserDataCleanupCallback(asCLEANENGINEFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetObjectTypeUserDataCleanupCallback(asCLEANOBJECTTYPEFUNC_t callback, asPWORD type = 0) = 0; + virtual void SetScriptObjectUserDataCleanupCallback(asCLEANSCRIPTOBJECTFUNC_t callback, asPWORD type = 0) = 0; protected: - virtual ~asIScriptEngine() {} + virtual ~asIScriptEngine() {} }; class asIThreadManager { protected: - virtual ~asIThreadManager() {} + virtual ~asIThreadManager() {} }; class asIScriptModule { public: - virtual asIScriptEngine *GetEngine() const = 0; - virtual void SetName(const char *name) = 0; - virtual const char *GetName() const = 0; - virtual void Discard() = 0; + virtual asIScriptEngine *GetEngine() const = 0; + virtual void SetName(const char *name) = 0; + virtual const char *GetName() const = 0; + virtual void Discard() = 0; - // Compilation - virtual int AddScriptSection(const char *name, const char *code, size_t codeLength = 0, int lineOffset = 0) = 0; - virtual int Build() = 0; - virtual int CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asIScriptFunction **outFunc) = 0; - virtual int CompileGlobalVar(const char *sectionName, const char *code, int lineOffset) = 0; - virtual asDWORD SetAccessMask(asDWORD accessMask) = 0; - virtual int SetDefaultNamespace(const char *nameSpace) = 0; - virtual const char *GetDefaultNamespace() const = 0; + // Compilation + virtual int AddScriptSection(const char *name, const char *code, size_t codeLength = 0, int lineOffset = 0) = 0; + virtual int Build() = 0; + virtual int CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asIScriptFunction **outFunc) = 0; + virtual int CompileGlobalVar(const char *sectionName, const char *code, int lineOffset) = 0; + virtual asDWORD SetAccessMask(asDWORD accessMask) = 0; + virtual int SetDefaultNamespace(const char *nameSpace) = 0; + virtual const char *GetDefaultNamespace() const = 0; - // Functions - virtual asUINT GetFunctionCount() const = 0; - virtual asIScriptFunction *GetFunctionByIndex(asUINT index) const = 0; - virtual asIScriptFunction *GetFunctionByDecl(const char *decl) const = 0; - virtual asIScriptFunction *GetFunctionByName(const char *name) const = 0; - virtual int RemoveFunction(asIScriptFunction *func) = 0; + // Functions + virtual asUINT GetFunctionCount() const = 0; + virtual asIScriptFunction *GetFunctionByIndex(asUINT index) const = 0; + virtual asIScriptFunction *GetFunctionByDecl(const char *decl) const = 0; + virtual asIScriptFunction *GetFunctionByName(const char *name) const = 0; + virtual int RemoveFunction(asIScriptFunction *func) = 0; - // Global variables - virtual int ResetGlobalVars(asIScriptContext *ctx = 0) = 0; - virtual asUINT GetGlobalVarCount() const = 0; - virtual int GetGlobalVarIndexByName(const char *name) const = 0; - virtual int GetGlobalVarIndexByDecl(const char *decl) const = 0; - virtual const char *GetGlobalVarDeclaration(asUINT index, bool includeNamespace = false) const = 0; - virtual int GetGlobalVar(asUINT index, const char **name, const char **nameSpace = 0, int *typeId = 0, bool *isConst = 0) const = 0; - virtual void *GetAddressOfGlobalVar(asUINT index) = 0; - virtual int RemoveGlobalVar(asUINT index) = 0; + // Global variables + virtual int ResetGlobalVars(asIScriptContext *ctx = 0) = 0; + virtual asUINT GetGlobalVarCount() const = 0; + virtual int GetGlobalVarIndexByName(const char *name) const = 0; + virtual int GetGlobalVarIndexByDecl(const char *decl) const = 0; + virtual const char *GetGlobalVarDeclaration(asUINT index, bool includeNamespace = false) const = 0; + virtual int GetGlobalVar(asUINT index, const char **name, const char **nameSpace = 0, int *typeId = 0, bool *isConst = 0) const = 0; + virtual void *GetAddressOfGlobalVar(asUINT index) = 0; + virtual int RemoveGlobalVar(asUINT index) = 0; - // Type identification - virtual asUINT GetObjectTypeCount() const = 0; - virtual asIObjectType *GetObjectTypeByIndex(asUINT index) const = 0; - virtual asIObjectType *GetObjectTypeByName(const char *name) const = 0; - virtual int GetTypeIdByDecl(const char *decl) const = 0; + // Type identification + virtual asUINT GetObjectTypeCount() const = 0; + virtual asIObjectType *GetObjectTypeByIndex(asUINT index) const = 0; + virtual asIObjectType *GetObjectTypeByName(const char *name) const = 0; + virtual asIObjectType *GetObjectTypeByDecl(const char *decl) const = 0; + virtual int GetTypeIdByDecl(const char *decl) const = 0; - // Enums - virtual asUINT GetEnumCount() const = 0; - virtual const char *GetEnumByIndex(asUINT index, int *enumTypeId, const char **nameSpace = 0) const = 0; - virtual int GetEnumValueCount(int enumTypeId) const = 0; - virtual const char *GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const = 0; + // Enums + virtual asUINT GetEnumCount() const = 0; + virtual const char *GetEnumByIndex(asUINT index, int *enumTypeId, const char **nameSpace = 0) const = 0; + virtual int GetEnumValueCount(int enumTypeId) const = 0; + virtual const char *GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) const = 0; - // Typedefs - virtual asUINT GetTypedefCount() const = 0; - virtual const char *GetTypedefByIndex(asUINT index, int *typeId, const char **nameSpace = 0) const = 0; + // Typedefs + virtual asUINT GetTypedefCount() const = 0; + virtual const char *GetTypedefByIndex(asUINT index, int *typeId, const char **nameSpace = 0) const = 0; - // Dynamic binding between modules - virtual asUINT GetImportedFunctionCount() const = 0; - virtual int GetImportedFunctionIndexByDecl(const char *decl) const = 0; - virtual const char *GetImportedFunctionDeclaration(asUINT importIndex) const = 0; - virtual const char *GetImportedFunctionSourceModule(asUINT importIndex) const = 0; - virtual int BindImportedFunction(asUINT importIndex, asIScriptFunction *func) = 0; - virtual int UnbindImportedFunction(asUINT importIndex) = 0; - virtual int BindAllImportedFunctions() = 0; - virtual int UnbindAllImportedFunctions() = 0; + // Dynamic binding between modules + virtual asUINT GetImportedFunctionCount() const = 0; + virtual int GetImportedFunctionIndexByDecl(const char *decl) const = 0; + virtual const char *GetImportedFunctionDeclaration(asUINT importIndex) const = 0; + virtual const char *GetImportedFunctionSourceModule(asUINT importIndex) const = 0; + virtual int BindImportedFunction(asUINT importIndex, asIScriptFunction *func) = 0; + virtual int UnbindImportedFunction(asUINT importIndex) = 0; + virtual int BindAllImportedFunctions() = 0; + virtual int UnbindAllImportedFunctions() = 0; - // Bytecode saving and loading - virtual int SaveByteCode(asIBinaryStream *out, bool stripDebugInfo = false) const = 0; - virtual int LoadByteCode(asIBinaryStream *in, bool *wasDebugInfoStripped = 0) = 0; + // Byte code saving and loading + virtual int SaveByteCode(asIBinaryStream *out, bool stripDebugInfo = false) const = 0; + virtual int LoadByteCode(asIBinaryStream *in, bool *wasDebugInfoStripped = 0) = 0; - // User data - virtual void *SetUserData(void *data) = 0; - virtual void *GetUserData() const = 0; + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; protected: - virtual ~asIScriptModule() {} + virtual ~asIScriptModule() {} }; class asIScriptContext { public: - // Memory management - virtual int AddRef() const = 0; - virtual int Release() const = 0; + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; - // Miscellaneous - virtual asIScriptEngine *GetEngine() const = 0; + // Miscellaneous + virtual asIScriptEngine *GetEngine() const = 0; - // Execution - virtual int Prepare(asIScriptFunction *func) = 0; - virtual int Unprepare() = 0; - virtual int Execute() = 0; - virtual int Abort() = 0; - virtual int Suspend() = 0; - virtual asEContextState GetState() const = 0; - virtual int PushState() = 0; - virtual int PopState() = 0; - virtual bool IsNested(asUINT *nestCount = 0) const = 0; + // Execution + virtual int Prepare(asIScriptFunction *func) = 0; + virtual int Unprepare() = 0; + virtual int Execute() = 0; + virtual int Abort() = 0; + virtual int Suspend() = 0; + virtual asEContextState GetState() const = 0; + virtual int PushState() = 0; + virtual int PopState() = 0; + virtual bool IsNested(asUINT *nestCount = 0) const = 0; - // Object pointer for calling class methods - virtual int SetObject(void *obj) = 0; + // Object pointer for calling class methods + virtual int SetObject(void *obj) = 0; - // Arguments - virtual int SetArgByte(asUINT arg, asBYTE value) = 0; - virtual int SetArgWord(asUINT arg, asWORD value) = 0; - virtual int SetArgDWord(asUINT arg, asDWORD value) = 0; - virtual int SetArgQWord(asUINT arg, asQWORD value) = 0; - virtual int SetArgFloat(asUINT arg, float value) = 0; - virtual int SetArgDouble(asUINT arg, double value) = 0; - virtual int SetArgAddress(asUINT arg, void *addr) = 0; - virtual int SetArgObject(asUINT arg, void *obj) = 0; - virtual void *GetAddressOfArg(asUINT arg) = 0; + // Arguments + virtual int SetArgByte(asUINT arg, asBYTE value) = 0; + virtual int SetArgWord(asUINT arg, asWORD value) = 0; + virtual int SetArgDWord(asUINT arg, asDWORD value) = 0; + virtual int SetArgQWord(asUINT arg, asQWORD value) = 0; + virtual int SetArgFloat(asUINT arg, float value) = 0; + virtual int SetArgDouble(asUINT arg, double value) = 0; + virtual int SetArgAddress(asUINT arg, void *addr) = 0; + virtual int SetArgObject(asUINT arg, void *obj) = 0; + virtual int SetArgVarType(asUINT arg, void *ptr, int typeId) = 0; + virtual void *GetAddressOfArg(asUINT arg) = 0; - // Return value - virtual asBYTE GetReturnByte() = 0; - virtual asWORD GetReturnWord() = 0; - virtual asDWORD GetReturnDWord() = 0; - virtual asQWORD GetReturnQWord() = 0; - virtual float GetReturnFloat() = 0; - virtual double GetReturnDouble() = 0; - virtual void *GetReturnAddress() = 0; - virtual void *GetReturnObject() = 0; - virtual void *GetAddressOfReturnValue() = 0; + // Return value + virtual asBYTE GetReturnByte() = 0; + virtual asWORD GetReturnWord() = 0; + virtual asDWORD GetReturnDWord() = 0; + virtual asQWORD GetReturnQWord() = 0; + virtual float GetReturnFloat() = 0; + virtual double GetReturnDouble() = 0; + virtual void *GetReturnAddress() = 0; + virtual void *GetReturnObject() = 0; + virtual void *GetAddressOfReturnValue() = 0; - // Exception handling - virtual int SetException(const char *string) = 0; - virtual int GetExceptionLineNumber(int *column = 0, const char **sectionName = 0) = 0; - virtual asIScriptFunction *GetExceptionFunction() = 0; - virtual const char * GetExceptionString() = 0; - virtual int SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv) = 0; - virtual void ClearExceptionCallback() = 0; + // Exception handling + virtual int SetException(const char *string) = 0; + virtual int GetExceptionLineNumber(int *column = 0, const char **sectionName = 0) = 0; + virtual asIScriptFunction *GetExceptionFunction() = 0; + virtual const char * GetExceptionString() = 0; + virtual int SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv) = 0; + virtual void ClearExceptionCallback() = 0; - // Debugging - virtual int SetLineCallback(asSFuncPtr callback, void *obj, int callConv) = 0; - virtual void ClearLineCallback() = 0; - virtual asUINT GetCallstackSize() const = 0; - virtual asIScriptFunction *GetFunction(asUINT stackLevel = 0) = 0; - virtual int GetLineNumber(asUINT stackLevel = 0, int *column = 0, const char **sectionName = 0) = 0; - virtual int GetVarCount(asUINT stackLevel = 0) = 0; - virtual const char *GetVarName(asUINT varIndex, asUINT stackLevel = 0) = 0; - virtual const char *GetVarDeclaration(asUINT varIndex, asUINT stackLevel = 0, bool includeNamespace = false) = 0; - virtual int GetVarTypeId(asUINT varIndex, asUINT stackLevel = 0) = 0; - virtual void *GetAddressOfVar(asUINT varIndex, asUINT stackLevel = 0) = 0; - virtual bool IsVarInScope(asUINT varIndex, asUINT stackLevel = 0) = 0; - virtual int GetThisTypeId(asUINT stackLevel = 0) = 0; - virtual void *GetThisPointer(asUINT stackLevel = 0) = 0; - virtual asIScriptFunction *GetSystemFunction() = 0; + // Debugging + virtual int SetLineCallback(asSFuncPtr callback, void *obj, int callConv) = 0; + virtual void ClearLineCallback() = 0; + virtual asUINT GetCallstackSize() const = 0; + virtual asIScriptFunction *GetFunction(asUINT stackLevel = 0) = 0; + virtual int GetLineNumber(asUINT stackLevel = 0, int *column = 0, const char **sectionName = 0) = 0; + virtual int GetVarCount(asUINT stackLevel = 0) = 0; + virtual const char *GetVarName(asUINT varIndex, asUINT stackLevel = 0) = 0; + virtual const char *GetVarDeclaration(asUINT varIndex, asUINT stackLevel = 0, bool includeNamespace = false) = 0; + virtual int GetVarTypeId(asUINT varIndex, asUINT stackLevel = 0) = 0; + virtual void *GetAddressOfVar(asUINT varIndex, asUINT stackLevel = 0) = 0; + virtual bool IsVarInScope(asUINT varIndex, asUINT stackLevel = 0) = 0; + virtual int GetThisTypeId(asUINT stackLevel = 0) = 0; + virtual void *GetThisPointer(asUINT stackLevel = 0) = 0; + virtual asIScriptFunction *GetSystemFunction() = 0; - // User data - virtual void *SetUserData(void *data) = 0; - virtual void *GetUserData() const = 0; + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; protected: - virtual ~asIScriptContext() {} + virtual ~asIScriptContext() {} }; class asIScriptGeneric { public: - // Miscellaneous - virtual asIScriptEngine *GetEngine() const = 0; - virtual asIScriptFunction *GetFunction() const = 0; + // Miscellaneous + virtual asIScriptEngine *GetEngine() const = 0; + virtual asIScriptFunction *GetFunction() const = 0; - // Object - virtual void *GetObject() = 0; - virtual int GetObjectTypeId() const = 0; + // Object + virtual void *GetObject() = 0; + virtual int GetObjectTypeId() const = 0; - // Arguments - virtual int GetArgCount() const = 0; - virtual int GetArgTypeId(asUINT arg, asDWORD *flags = 0) const = 0; - virtual asBYTE GetArgByte(asUINT arg) = 0; - virtual asWORD GetArgWord(asUINT arg) = 0; - virtual asDWORD GetArgDWord(asUINT arg) = 0; - virtual asQWORD GetArgQWord(asUINT arg) = 0; - virtual float GetArgFloat(asUINT arg) = 0; - virtual double GetArgDouble(asUINT arg) = 0; - virtual void *GetArgAddress(asUINT arg) = 0; - virtual void *GetArgObject(asUINT arg) = 0; - virtual void *GetAddressOfArg(asUINT arg) = 0; + // Arguments + virtual int GetArgCount() const = 0; + virtual int GetArgTypeId(asUINT arg, asDWORD *flags = 0) const = 0; + virtual asBYTE GetArgByte(asUINT arg) = 0; + virtual asWORD GetArgWord(asUINT arg) = 0; + virtual asDWORD GetArgDWord(asUINT arg) = 0; + virtual asQWORD GetArgQWord(asUINT arg) = 0; + virtual float GetArgFloat(asUINT arg) = 0; + virtual double GetArgDouble(asUINT arg) = 0; + virtual void *GetArgAddress(asUINT arg) = 0; + virtual void *GetArgObject(asUINT arg) = 0; + virtual void *GetAddressOfArg(asUINT arg) = 0; - // Return value - virtual int GetReturnTypeId(asDWORD *flags = 0) const = 0; - virtual int SetReturnByte(asBYTE val) = 0; - virtual int SetReturnWord(asWORD val) = 0; - virtual int SetReturnDWord(asDWORD val) = 0; - virtual int SetReturnQWord(asQWORD val) = 0; - virtual int SetReturnFloat(float val) = 0; - virtual int SetReturnDouble(double val) = 0; - virtual int SetReturnAddress(void *addr) = 0; - virtual int SetReturnObject(void *obj) = 0; - virtual void *GetAddressOfReturnLocation() = 0; + // Return value + virtual int GetReturnTypeId(asDWORD *flags = 0) const = 0; + virtual int SetReturnByte(asBYTE val) = 0; + virtual int SetReturnWord(asWORD val) = 0; + virtual int SetReturnDWord(asDWORD val) = 0; + virtual int SetReturnQWord(asQWORD val) = 0; + virtual int SetReturnFloat(float val) = 0; + virtual int SetReturnDouble(double val) = 0; + virtual int SetReturnAddress(void *addr) = 0; + virtual int SetReturnObject(void *obj) = 0; + virtual void *GetAddressOfReturnLocation() = 0; protected: - virtual ~asIScriptGeneric() {} + virtual ~asIScriptGeneric() {} }; class asIScriptObject { public: - // Memory management - virtual int AddRef() const = 0; - virtual int Release() const = 0; + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; + virtual asILockableSharedBool *GetWeakRefFlag() const = 0; - // Type info - virtual int GetTypeId() const = 0; - virtual asIObjectType *GetObjectType() const = 0; + // Type info + virtual int GetTypeId() const = 0; + virtual asIObjectType *GetObjectType() const = 0; - // Class properties - virtual asUINT GetPropertyCount() const = 0; - virtual int GetPropertyTypeId(asUINT prop) const = 0; - virtual const char *GetPropertyName(asUINT prop) const = 0; - virtual void *GetAddressOfProperty(asUINT prop) = 0; + // Class properties + virtual asUINT GetPropertyCount() const = 0; + virtual int GetPropertyTypeId(asUINT prop) const = 0; + virtual const char *GetPropertyName(asUINT prop) const = 0; + virtual void *GetAddressOfProperty(asUINT prop) = 0; - virtual asIScriptEngine *GetEngine() const = 0; - virtual int CopyFrom(asIScriptObject *other) = 0; + // Miscellaneous + virtual asIScriptEngine *GetEngine() const = 0; + virtual int CopyFrom(asIScriptObject *other) = 0; + + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; protected: - virtual ~asIScriptObject() {} + virtual ~asIScriptObject() {} }; class asIObjectType { public: - virtual asIScriptEngine *GetEngine() const = 0; - virtual const char *GetConfigGroup() const = 0; - virtual asDWORD GetAccessMask() const = 0; - virtual asIScriptModule *GetModule() const = 0; + // Miscellaneous + virtual asIScriptEngine *GetEngine() const = 0; + virtual const char *GetConfigGroup() const = 0; + virtual asDWORD GetAccessMask() const = 0; + virtual asIScriptModule *GetModule() const = 0; - // Memory management - virtual int AddRef() const = 0; - virtual int Release() const = 0; + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; - // Type info - virtual const char *GetName() const = 0; - virtual const char *GetNamespace() const = 0; - virtual asIObjectType *GetBaseType() const = 0; - virtual bool DerivesFrom(const asIObjectType *objType) const = 0; - virtual asDWORD GetFlags() const = 0; - virtual asUINT GetSize() const = 0; - virtual int GetTypeId() const = 0; - virtual int GetSubTypeId(asUINT subTypeIndex = 0) const = 0; - virtual asIObjectType *GetSubType(asUINT subTypeIndex = 0) const = 0; - virtual asUINT GetSubTypeCount() const = 0; + // Type info + virtual const char *GetName() const = 0; + virtual const char *GetNamespace() const = 0; + virtual asIObjectType *GetBaseType() const = 0; + virtual bool DerivesFrom(const asIObjectType *objType) const = 0; + virtual asDWORD GetFlags() const = 0; + virtual asUINT GetSize() const = 0; + virtual int GetTypeId() const = 0; + virtual int GetSubTypeId(asUINT subTypeIndex = 0) const = 0; + virtual asIObjectType *GetSubType(asUINT subTypeIndex = 0) const = 0; + virtual asUINT GetSubTypeCount() const = 0; - // Interfaces - virtual asUINT GetInterfaceCount() const = 0; - virtual asIObjectType *GetInterface(asUINT index) const = 0; - virtual bool Implements(const asIObjectType *objType) const = 0; + // Interfaces + virtual asUINT GetInterfaceCount() const = 0; + virtual asIObjectType *GetInterface(asUINT index) const = 0; + virtual bool Implements(const asIObjectType *objType) const = 0; - // Factories - virtual asUINT GetFactoryCount() const = 0; - virtual asIScriptFunction *GetFactoryByIndex(asUINT index) const = 0; - virtual asIScriptFunction *GetFactoryByDecl(const char *decl) const = 0; + // Factories + virtual asUINT GetFactoryCount() const = 0; + virtual asIScriptFunction *GetFactoryByIndex(asUINT index) const = 0; + virtual asIScriptFunction *GetFactoryByDecl(const char *decl) const = 0; - // Methods - virtual asUINT GetMethodCount() const = 0; - virtual asIScriptFunction *GetMethodByIndex(asUINT index, bool getVirtual = true) const = 0; - virtual asIScriptFunction *GetMethodByName(const char *name, bool getVirtual = true) const = 0; - virtual asIScriptFunction *GetMethodByDecl(const char *decl, bool getVirtual = true) const = 0; + // Methods + virtual asUINT GetMethodCount() const = 0; + virtual asIScriptFunction *GetMethodByIndex(asUINT index, bool getVirtual = true) const = 0; + virtual asIScriptFunction *GetMethodByName(const char *name, bool getVirtual = true) const = 0; + virtual asIScriptFunction *GetMethodByDecl(const char *decl, bool getVirtual = true) const = 0; - // Properties - virtual asUINT GetPropertyCount() const = 0; - virtual int GetProperty(asUINT index, const char **name, int *typeId = 0, bool *isPrivate = 0, int *offset = 0, bool *isReference = 0, asDWORD *accessMask = 0) const = 0; - virtual const char *GetPropertyDeclaration(asUINT index, bool includeNamespace = false) const = 0; + // Properties + virtual asUINT GetPropertyCount() const = 0; + virtual int GetProperty(asUINT index, const char **name, int *typeId = 0, bool *isPrivate = 0, bool *isProtected = 0, int *offset = 0, bool *isReference = 0, asDWORD *accessMask = 0) const = 0; + virtual const char *GetPropertyDeclaration(asUINT index, bool includeNamespace = false) const = 0; - // Behaviours - virtual asUINT GetBehaviourCount() const = 0; - virtual asIScriptFunction *GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const = 0; + // Behaviours + virtual asUINT GetBehaviourCount() const = 0; + virtual asIScriptFunction *GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const = 0; - // User data - virtual void *SetUserData(void *data, asPWORD type = 0) = 0; - virtual void *GetUserData(asPWORD type = 0) const = 0; + // User data + virtual void *SetUserData(void *data, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; protected: - virtual ~asIObjectType() {} + virtual ~asIObjectType() {} }; class asIScriptFunction { public: - virtual asIScriptEngine *GetEngine() const = 0; + virtual asIScriptEngine *GetEngine() const = 0; - // Memory management - virtual int AddRef() const = 0; - virtual int Release() const = 0; + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; - // Miscellaneous - virtual int GetId() const = 0; - virtual asEFuncType GetFuncType() const = 0; - virtual const char *GetModuleName() const = 0; - virtual asIScriptModule *GetModule() const = 0; - virtual const char *GetScriptSectionName() const = 0; - virtual const char *GetConfigGroup() const = 0; - virtual asDWORD GetAccessMask() const = 0; + // Miscellaneous + virtual int GetId() const = 0; + virtual asEFuncType GetFuncType() const = 0; + virtual const char *GetModuleName() const = 0; + virtual asIScriptModule *GetModule() const = 0; + virtual const char *GetScriptSectionName() const = 0; + virtual const char *GetConfigGroup() const = 0; + virtual asDWORD GetAccessMask() const = 0; - // Function signature - virtual asIObjectType *GetObjectType() const = 0; - virtual const char *GetObjectName() const = 0; - virtual const char *GetName() const = 0; - virtual const char *GetNamespace() const = 0; - virtual const char *GetDeclaration(bool includeObjectName = true, bool includeNamespace = false) const = 0; - virtual bool IsReadOnly() const = 0; - virtual bool IsPrivate() const = 0; - virtual bool IsFinal() const = 0; - virtual bool IsOverride() const = 0; - virtual bool IsShared() const = 0; - virtual asUINT GetParamCount() const = 0; - virtual int GetParamTypeId(asUINT index, asDWORD *flags = 0) const = 0; - virtual int GetReturnTypeId(asDWORD *flags = 0) const = 0; + // Function signature + virtual asIObjectType *GetObjectType() const = 0; + virtual const char *GetObjectName() const = 0; + virtual const char *GetName() const = 0; + virtual const char *GetNamespace() const = 0; + virtual const char *GetDeclaration(bool includeObjectName = true, bool includeNamespace = false, bool includeParamNames = false) const = 0; + virtual bool IsReadOnly() const = 0; + virtual bool IsPrivate() const = 0; + virtual bool IsProtected() const = 0; + virtual bool IsFinal() const = 0; + virtual bool IsOverride() const = 0; + virtual bool IsShared() const = 0; + virtual asUINT GetParamCount() const = 0; + virtual int GetParam(asUINT index, int *typeId, asDWORD *flags = 0, const char **name = 0, const char **defaultArg = 0) const = 0; +#ifdef AS_DEPRECATED + // Deprecated since 2.29.0, 2014-04-06 + virtual int GetParamTypeId(asUINT index, asDWORD *flags = 0) const = 0; +#endif + virtual int GetReturnTypeId(asDWORD *flags = 0) const = 0; - // Type id for function pointers - virtual int GetTypeId() const = 0; - virtual bool IsCompatibleWithTypeId(int typeId) const = 0; + // Type id for function pointers + virtual int GetTypeId() const = 0; + virtual bool IsCompatibleWithTypeId(int typeId) const = 0; - // Delegates - virtual void *GetDelegateObject() const = 0; - virtual asIObjectType *GetDelegateObjectType() const = 0; - virtual asIScriptFunction *GetDelegateFunction() const = 0; + // Delegates + virtual void *GetDelegateObject() const = 0; + virtual asIObjectType *GetDelegateObjectType() const = 0; + virtual asIScriptFunction *GetDelegateFunction() const = 0; - // Debug information - virtual asUINT GetVarCount() const = 0; - virtual int GetVar(asUINT index, const char **name, int *typeId = 0) const = 0; - virtual const char *GetVarDecl(asUINT index, bool includeNamespace = false) const = 0; - virtual int FindNextLineWithCode(int line) const = 0; + // Debug information + virtual asUINT GetVarCount() const = 0; + virtual int GetVar(asUINT index, const char **name, int *typeId = 0) const = 0; + virtual const char *GetVarDecl(asUINT index, bool includeNamespace = false) const = 0; + virtual int FindNextLineWithCode(int line) const = 0; - // For JIT compilation - virtual asDWORD *GetByteCode(asUINT *length = 0) = 0; + // For JIT compilation + virtual asDWORD *GetByteCode(asUINT *length = 0) = 0; - // User data - virtual void *SetUserData(void *userData) = 0; - virtual void *GetUserData() const = 0; + // User data + virtual void *SetUserData(void *userData, asPWORD type = 0) = 0; + virtual void *GetUserData(asPWORD type = 0) const = 0; protected: - virtual ~asIScriptFunction() {}; + virtual ~asIScriptFunction() {}; }; class asIBinaryStream { public: - virtual void Read(void *ptr, asUINT size) = 0; - virtual void Write(const void *ptr, asUINT size) = 0; + virtual void Read(void *ptr, asUINT size) = 0; + virtual void Write(const void *ptr, asUINT size) = 0; public: - virtual ~asIBinaryStream() {} + virtual ~asIBinaryStream() {} }; class asILockableSharedBool { public: - // Memory management - virtual int AddRef() const = 0; - virtual int Release() const = 0; + // Memory management + virtual int AddRef() const = 0; + virtual int Release() const = 0; - // Value - virtual bool Get() const = 0; - virtual void Set(bool val) = 0; - - // Thread management - virtual void Lock() const = 0; - virtual void Unlock() const = 0; + // Value + virtual bool Get() const = 0; + virtual void Set(bool val) = 0; + + // Thread management + virtual void Lock() const = 0; + virtual void Unlock() const = 0; protected: - virtual ~asILockableSharedBool() {} + virtual ~asILockableSharedBool() {} }; //----------------------------------------------------------------- @@ -1051,30 +1171,30 @@ protected: template inline asSFuncPtr asFunctionPtr(T func) { - // Mark this as a global function - asSFuncPtr p(2); + // Mark this as a global function + asSFuncPtr p(2); #ifdef AS_64BIT_PTR - // The size_t cast is to avoid a compiler warning with asFUNCTION(0) - // on 64bit, as 0 is interpreted as a 32bit int value - p.ptr.f.func = reinterpret_cast(size_t(func)); + // The size_t cast is to avoid a compiler warning with asFUNCTION(0) + // on 64bit, as 0 is interpreted as a 32bit int value + p.ptr.f.func = reinterpret_cast(size_t(func)); #else - // MSVC6 doesn't like the size_t cast above so I - // solved this with a separate code for 32bit. - p.ptr.f.func = reinterpret_cast(func); + // MSVC6 doesn't like the size_t cast above so I + // solved this with a separate code for 32bit. + p.ptr.f.func = reinterpret_cast(func); #endif - return p; + return p; } // Specialization for functions using the generic calling convention template<> inline asSFuncPtr asFunctionPtr(asGENFUNC_t func) { - // Mark this as a generic function - asSFuncPtr p(1); - p.ptr.f.func = reinterpret_cast(func); - return p; + // Mark this as a generic function + asSFuncPtr p(1); + p.ptr.f.func = reinterpret_cast(func); + return p; } #ifndef AS_NO_CLASS_METHODS @@ -1090,31 +1210,31 @@ const int SINGLE_PTR_SIZE = sizeof(asSIMPLEMETHOD_t); template struct asSMethodPtr { - template - static asSFuncPtr Convert(M Mthd) - { - // This version of the function should never be executed, nor compiled, - // as it would mean that the size of the method pointer cannot be determined. + template + static asSFuncPtr Convert(M Mthd) + { + // This version of the function should never be executed, nor compiled, + // as it would mean that the size of the method pointer cannot be determined. - int ERROR_UnsupportedMethodPtr[N-100]; + int ERROR_UnsupportedMethodPtr[N-100]; - asSFuncPtr p(0); - return p; - } + asSFuncPtr p(0); + return p; + } }; // Template specialization template <> struct asSMethodPtr { - template - static asSFuncPtr Convert(M Mthd) - { - // Mark this as a class method - asSFuncPtr p(3); - p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE); - return p; - } + template + static asSFuncPtr Convert(M Mthd) + { + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE); + return p; + } }; #if defined(_MSC_VER) && !defined(__MWERKS__) @@ -1123,84 +1243,84 @@ struct asSMethodPtr template <> struct asSMethodPtr { - template - static asSFuncPtr Convert(M Mthd) - { - // Mark this as a class method - asSFuncPtr p(3); - p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+sizeof(int)); - return p; - } + template + static asSFuncPtr Convert(M Mthd) + { + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+sizeof(int)); + return p; + } }; template <> struct asSMethodPtr { - template - static asSFuncPtr Convert(M Mthd) - { - // On 32bit platforms with is where a class with virtual inheritance falls. - // On 64bit platforms we can also fall here if 8byte data alignments is used. + template + static asSFuncPtr Convert(M Mthd) + { + // On 32bit platforms with is where a class with virtual inheritance falls. + // On 64bit platforms we can also fall here if 8byte data alignments is used. - // Mark this as a class method - asSFuncPtr p(3); - p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+2*sizeof(int)); + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+2*sizeof(int)); - // Microsoft has a terrible optimization on class methods with virtual inheritance. - // They are hardcoding an important offset, which is not coming in the method pointer. + // Microsoft has a terrible optimization on class methods with virtual inheritance. + // They are hardcoding an important offset, which is not coming in the method pointer. #if defined(_MSC_VER) && !defined(AS_64BIT_PTR) - // Method pointers for virtual inheritance is not supported, - // as it requires the location of the vbase table, which is - // only available to the C++ compiler, but not in the method - // pointer. + // Method pointers for virtual inheritance is not supported, + // as it requires the location of the vbase table, which is + // only available to the C++ compiler, but not in the method + // pointer. - // You can get around this by forward declaring the class and - // storing the sizeof its method pointer in a constant. Example: + // You can get around this by forward declaring the class and + // storing the sizeof its method pointer in a constant. Example: - // class ClassWithVirtualInheritance; - // const int ClassWithVirtualInheritance_workaround = sizeof(void ClassWithVirtualInheritance::*()); + // class ClassWithVirtualInheritance; + // const int ClassWithVirtualInheritance_workaround = sizeof(void ClassWithVirtualInheritance::*()); - // This will force the compiler to use the unknown type - // for the class, which falls under the next case + // This will force the compiler to use the unknown type + // for the class, which falls under the next case - // Copy the virtual table index to the 4th dword so that AngelScript - // can properly detect and deny the use of methods with virtual inheritance. - *(reinterpret_cast(&p)+3) = *(reinterpret_cast(&p)+2); + // Copy the virtual table index to the 4th dword so that AngelScript + // can properly detect and deny the use of methods with virtual inheritance. + *(reinterpret_cast(&p)+3) = *(reinterpret_cast(&p)+2); #endif - return p; - } + return p; + } }; template <> struct asSMethodPtr { - template - static asSFuncPtr Convert(M Mthd) - { - // Mark this as a class method - asSFuncPtr p(3); - p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+3*sizeof(int)); - return p; - } + template + static asSFuncPtr Convert(M Mthd) + { + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+3*sizeof(int)); + return p; + } }; template <> struct asSMethodPtr { - template - static asSFuncPtr Convert(M Mthd) - { - // On 64bit platforms with 8byte data alignment - // the unknown class method pointers will come here. + template + static asSFuncPtr Convert(M Mthd) + { + // On 64bit platforms with 8byte data alignment + // the unknown class method pointers will come here. - // Mark this as a class method - asSFuncPtr p(3); - p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+4*sizeof(int)); - return p; - } + // Mark this as a class method + asSFuncPtr p(3); + p.CopyMethodPtr(&Mthd, SINGLE_PTR_SIZE+4*sizeof(int)); + return p; + } }; #endif @@ -1212,14 +1332,14 @@ struct asSMethodPtr struct asSVMRegisters { - asDWORD *programPointer; // points to current bytecode instruction - asDWORD *stackFramePointer; // function stack frame - asDWORD *stackPointer; // top of stack (grows downward) - asQWORD valueRegister; // temp register for primitives - void *objectRegister; // temp register for objects and handles - asIObjectType *objectType; // type of object held in object register - bool doProcessSuspend; // whether or not the JIT should break out when it encounters a suspend instruction - asIScriptContext *ctx; // the active context + asDWORD *programPointer; // points to current bytecode instruction + asDWORD *stackFramePointer; // function stack frame + asDWORD *stackPointer; // top of stack (grows downward) + asQWORD valueRegister; // temp register for primitives + void *objectRegister; // temp register for objects and handles + asIObjectType *objectType; // type of object held in object register + bool doProcessSuspend; // whether or not the JIT should break out when it encounters a suspend instruction + asIScriptContext *ctx; // the active context }; typedef void (*asJITFunction)(asSVMRegisters *registers, asPWORD jitArg); @@ -1227,303 +1347,303 @@ typedef void (*asJITFunction)(asSVMRegisters *registers, asPWORD jitArg); class asIJITCompiler { public: - virtual int CompileFunction(asIScriptFunction *function, asJITFunction *output) = 0; - virtual void ReleaseJITFunction(asJITFunction func) = 0; + virtual int CompileFunction(asIScriptFunction *function, asJITFunction *output) = 0; + virtual void ReleaseJITFunction(asJITFunction func) = 0; public: - virtual ~asIJITCompiler() {} + virtual ~asIJITCompiler() {} }; // Byte code instructions enum asEBCInstr { - asBC_PopPtr = 0, - asBC_PshGPtr = 1, - asBC_PshC4 = 2, - asBC_PshV4 = 3, - asBC_PSF = 4, - asBC_SwapPtr = 5, - asBC_NOT = 6, - asBC_PshG4 = 7, - asBC_LdGRdR4 = 8, - asBC_CALL = 9, - asBC_RET = 10, - asBC_JMP = 11, - asBC_JZ = 12, - asBC_JNZ = 13, - asBC_JS = 14, - asBC_JNS = 15, - asBC_JP = 16, - asBC_JNP = 17, - asBC_TZ = 18, - asBC_TNZ = 19, - asBC_TS = 20, - asBC_TNS = 21, - asBC_TP = 22, - asBC_TNP = 23, - asBC_NEGi = 24, - asBC_NEGf = 25, - asBC_NEGd = 26, - asBC_INCi16 = 27, - asBC_INCi8 = 28, - asBC_DECi16 = 29, - asBC_DECi8 = 30, - asBC_INCi = 31, - asBC_DECi = 32, - asBC_INCf = 33, - asBC_DECf = 34, - asBC_INCd = 35, - asBC_DECd = 36, - asBC_IncVi = 37, - asBC_DecVi = 38, - asBC_BNOT = 39, - asBC_BAND = 40, - asBC_BOR = 41, - asBC_BXOR = 42, - asBC_BSLL = 43, - asBC_BSRL = 44, - asBC_BSRA = 45, - asBC_COPY = 46, - asBC_PshC8 = 47, - asBC_PshVPtr = 48, - asBC_RDSPtr = 49, - asBC_CMPd = 50, - asBC_CMPu = 51, - asBC_CMPf = 52, - asBC_CMPi = 53, - asBC_CMPIi = 54, - asBC_CMPIf = 55, - asBC_CMPIu = 56, - asBC_JMPP = 57, - asBC_PopRPtr = 58, - asBC_PshRPtr = 59, - asBC_STR = 60, - asBC_CALLSYS = 61, - asBC_CALLBND = 62, - asBC_SUSPEND = 63, - asBC_ALLOC = 64, - asBC_FREE = 65, - asBC_LOADOBJ = 66, - asBC_STOREOBJ = 67, - asBC_GETOBJ = 68, - asBC_REFCPY = 69, - asBC_CHKREF = 70, - asBC_GETOBJREF = 71, - asBC_GETREF = 72, - asBC_PshNull = 73, - asBC_ClrVPtr = 74, - asBC_OBJTYPE = 75, - asBC_TYPEID = 76, - asBC_SetV4 = 77, - asBC_SetV8 = 78, - asBC_ADDSi = 79, - asBC_CpyVtoV4 = 80, - asBC_CpyVtoV8 = 81, - asBC_CpyVtoR4 = 82, - asBC_CpyVtoR8 = 83, - asBC_CpyVtoG4 = 84, - asBC_CpyRtoV4 = 85, - asBC_CpyRtoV8 = 86, - asBC_CpyGtoV4 = 87, - asBC_WRTV1 = 88, - asBC_WRTV2 = 89, - asBC_WRTV4 = 90, - asBC_WRTV8 = 91, - asBC_RDR1 = 92, - asBC_RDR2 = 93, - asBC_RDR4 = 94, - asBC_RDR8 = 95, - asBC_LDG = 96, - asBC_LDV = 97, - asBC_PGA = 98, - asBC_CmpPtr = 99, - asBC_VAR = 100, - asBC_iTOf = 101, - asBC_fTOi = 102, - asBC_uTOf = 103, - asBC_fTOu = 104, - asBC_sbTOi = 105, - asBC_swTOi = 106, - asBC_ubTOi = 107, - asBC_uwTOi = 108, - asBC_dTOi = 109, - asBC_dTOu = 110, - asBC_dTOf = 111, - asBC_iTOd = 112, - asBC_uTOd = 113, - asBC_fTOd = 114, - asBC_ADDi = 115, - asBC_SUBi = 116, - asBC_MULi = 117, - asBC_DIVi = 118, - asBC_MODi = 119, - asBC_ADDf = 120, - asBC_SUBf = 121, - asBC_MULf = 122, - asBC_DIVf = 123, - asBC_MODf = 124, - asBC_ADDd = 125, - asBC_SUBd = 126, - asBC_MULd = 127, - asBC_DIVd = 128, - asBC_MODd = 129, - asBC_ADDIi = 130, - asBC_SUBIi = 131, - asBC_MULIi = 132, - asBC_ADDIf = 133, - asBC_SUBIf = 134, - asBC_MULIf = 135, - asBC_SetG4 = 136, - asBC_ChkRefS = 137, - asBC_ChkNullV = 138, - asBC_CALLINTF = 139, - asBC_iTOb = 140, - asBC_iTOw = 141, - asBC_SetV1 = 142, - asBC_SetV2 = 143, - asBC_Cast = 144, - asBC_i64TOi = 145, - asBC_uTOi64 = 146, - asBC_iTOi64 = 147, - asBC_fTOi64 = 148, - asBC_dTOi64 = 149, - asBC_fTOu64 = 150, - asBC_dTOu64 = 151, - asBC_i64TOf = 152, - asBC_u64TOf = 153, - asBC_i64TOd = 154, - asBC_u64TOd = 155, - asBC_NEGi64 = 156, - asBC_INCi64 = 157, - asBC_DECi64 = 158, - asBC_BNOT64 = 159, - asBC_ADDi64 = 160, - asBC_SUBi64 = 161, - asBC_MULi64 = 162, - asBC_DIVi64 = 163, - asBC_MODi64 = 164, - asBC_BAND64 = 165, - asBC_BOR64 = 166, - asBC_BXOR64 = 167, - asBC_BSLL64 = 168, - asBC_BSRL64 = 169, - asBC_BSRA64 = 170, - asBC_CMPi64 = 171, - asBC_CMPu64 = 172, - asBC_ChkNullS = 173, - asBC_ClrHi = 174, - asBC_JitEntry = 175, - asBC_CallPtr = 176, - asBC_FuncPtr = 177, - asBC_LoadThisR = 178, - asBC_PshV8 = 179, - asBC_DIVu = 180, - asBC_MODu = 181, - asBC_DIVu64 = 182, - asBC_MODu64 = 183, - asBC_LoadRObjR = 184, - asBC_LoadVObjR = 185, - asBC_RefCpyV = 186, - asBC_JLowZ = 187, - asBC_JLowNZ = 188, - asBC_AllocMem = 189, - asBC_SetListSize = 190, - asBC_PshListElmnt = 191, - asBC_SetListType = 192, - asBC_POWi = 193, - asBC_POWu = 194, - asBC_POWf = 195, - asBC_POWd = 196, - asBC_POWdi = 197, - asBC_POWi64 = 198, - asBC_POWu64 = 199, + asBC_PopPtr = 0, + asBC_PshGPtr = 1, + asBC_PshC4 = 2, + asBC_PshV4 = 3, + asBC_PSF = 4, + asBC_SwapPtr = 5, + asBC_NOT = 6, + asBC_PshG4 = 7, + asBC_LdGRdR4 = 8, + asBC_CALL = 9, + asBC_RET = 10, + asBC_JMP = 11, + asBC_JZ = 12, + asBC_JNZ = 13, + asBC_JS = 14, + asBC_JNS = 15, + asBC_JP = 16, + asBC_JNP = 17, + asBC_TZ = 18, + asBC_TNZ = 19, + asBC_TS = 20, + asBC_TNS = 21, + asBC_TP = 22, + asBC_TNP = 23, + asBC_NEGi = 24, + asBC_NEGf = 25, + asBC_NEGd = 26, + asBC_INCi16 = 27, + asBC_INCi8 = 28, + asBC_DECi16 = 29, + asBC_DECi8 = 30, + asBC_INCi = 31, + asBC_DECi = 32, + asBC_INCf = 33, + asBC_DECf = 34, + asBC_INCd = 35, + asBC_DECd = 36, + asBC_IncVi = 37, + asBC_DecVi = 38, + asBC_BNOT = 39, + asBC_BAND = 40, + asBC_BOR = 41, + asBC_BXOR = 42, + asBC_BSLL = 43, + asBC_BSRL = 44, + asBC_BSRA = 45, + asBC_COPY = 46, + asBC_PshC8 = 47, + asBC_PshVPtr = 48, + asBC_RDSPtr = 49, + asBC_CMPd = 50, + asBC_CMPu = 51, + asBC_CMPf = 52, + asBC_CMPi = 53, + asBC_CMPIi = 54, + asBC_CMPIf = 55, + asBC_CMPIu = 56, + asBC_JMPP = 57, + asBC_PopRPtr = 58, + asBC_PshRPtr = 59, + asBC_STR = 60, + asBC_CALLSYS = 61, + asBC_CALLBND = 62, + asBC_SUSPEND = 63, + asBC_ALLOC = 64, + asBC_FREE = 65, + asBC_LOADOBJ = 66, + asBC_STOREOBJ = 67, + asBC_GETOBJ = 68, + asBC_REFCPY = 69, + asBC_CHKREF = 70, + asBC_GETOBJREF = 71, + asBC_GETREF = 72, + asBC_PshNull = 73, + asBC_ClrVPtr = 74, + asBC_OBJTYPE = 75, + asBC_TYPEID = 76, + asBC_SetV4 = 77, + asBC_SetV8 = 78, + asBC_ADDSi = 79, + asBC_CpyVtoV4 = 80, + asBC_CpyVtoV8 = 81, + asBC_CpyVtoR4 = 82, + asBC_CpyVtoR8 = 83, + asBC_CpyVtoG4 = 84, + asBC_CpyRtoV4 = 85, + asBC_CpyRtoV8 = 86, + asBC_CpyGtoV4 = 87, + asBC_WRTV1 = 88, + asBC_WRTV2 = 89, + asBC_WRTV4 = 90, + asBC_WRTV8 = 91, + asBC_RDR1 = 92, + asBC_RDR2 = 93, + asBC_RDR4 = 94, + asBC_RDR8 = 95, + asBC_LDG = 96, + asBC_LDV = 97, + asBC_PGA = 98, + asBC_CmpPtr = 99, + asBC_VAR = 100, + asBC_iTOf = 101, + asBC_fTOi = 102, + asBC_uTOf = 103, + asBC_fTOu = 104, + asBC_sbTOi = 105, + asBC_swTOi = 106, + asBC_ubTOi = 107, + asBC_uwTOi = 108, + asBC_dTOi = 109, + asBC_dTOu = 110, + asBC_dTOf = 111, + asBC_iTOd = 112, + asBC_uTOd = 113, + asBC_fTOd = 114, + asBC_ADDi = 115, + asBC_SUBi = 116, + asBC_MULi = 117, + asBC_DIVi = 118, + asBC_MODi = 119, + asBC_ADDf = 120, + asBC_SUBf = 121, + asBC_MULf = 122, + asBC_DIVf = 123, + asBC_MODf = 124, + asBC_ADDd = 125, + asBC_SUBd = 126, + asBC_MULd = 127, + asBC_DIVd = 128, + asBC_MODd = 129, + asBC_ADDIi = 130, + asBC_SUBIi = 131, + asBC_MULIi = 132, + asBC_ADDIf = 133, + asBC_SUBIf = 134, + asBC_MULIf = 135, + asBC_SetG4 = 136, + asBC_ChkRefS = 137, + asBC_ChkNullV = 138, + asBC_CALLINTF = 139, + asBC_iTOb = 140, + asBC_iTOw = 141, + asBC_SetV1 = 142, + asBC_SetV2 = 143, + asBC_Cast = 144, + asBC_i64TOi = 145, + asBC_uTOi64 = 146, + asBC_iTOi64 = 147, + asBC_fTOi64 = 148, + asBC_dTOi64 = 149, + asBC_fTOu64 = 150, + asBC_dTOu64 = 151, + asBC_i64TOf = 152, + asBC_u64TOf = 153, + asBC_i64TOd = 154, + asBC_u64TOd = 155, + asBC_NEGi64 = 156, + asBC_INCi64 = 157, + asBC_DECi64 = 158, + asBC_BNOT64 = 159, + asBC_ADDi64 = 160, + asBC_SUBi64 = 161, + asBC_MULi64 = 162, + asBC_DIVi64 = 163, + asBC_MODi64 = 164, + asBC_BAND64 = 165, + asBC_BOR64 = 166, + asBC_BXOR64 = 167, + asBC_BSLL64 = 168, + asBC_BSRL64 = 169, + asBC_BSRA64 = 170, + asBC_CMPi64 = 171, + asBC_CMPu64 = 172, + asBC_ChkNullS = 173, + asBC_ClrHi = 174, + asBC_JitEntry = 175, + asBC_CallPtr = 176, + asBC_FuncPtr = 177, + asBC_LoadThisR = 178, + asBC_PshV8 = 179, + asBC_DIVu = 180, + asBC_MODu = 181, + asBC_DIVu64 = 182, + asBC_MODu64 = 183, + asBC_LoadRObjR = 184, + asBC_LoadVObjR = 185, + asBC_RefCpyV = 186, + asBC_JLowZ = 187, + asBC_JLowNZ = 188, + asBC_AllocMem = 189, + asBC_SetListSize = 190, + asBC_PshListElmnt = 191, + asBC_SetListType = 192, + asBC_POWi = 193, + asBC_POWu = 194, + asBC_POWf = 195, + asBC_POWd = 196, + asBC_POWdi = 197, + asBC_POWi64 = 198, + asBC_POWu64 = 199, - asBC_MAXBYTECODE = 200, + asBC_MAXBYTECODE = 200, - // Temporary tokens. Can't be output to the final program - asBC_VarDecl = 251, - asBC_Block = 252, - asBC_ObjInfo = 253, - asBC_LINE = 254, - asBC_LABEL = 255 + // Temporary tokens. Can't be output to the final program + asBC_VarDecl = 251, + asBC_Block = 252, + asBC_ObjInfo = 253, + asBC_LINE = 254, + asBC_LABEL = 255 }; // Instruction types enum asEBCType { - asBCTYPE_INFO = 0, - asBCTYPE_NO_ARG = 1, - asBCTYPE_W_ARG = 2, - asBCTYPE_wW_ARG = 3, - asBCTYPE_DW_ARG = 4, - asBCTYPE_rW_DW_ARG = 5, - asBCTYPE_QW_ARG = 6, - asBCTYPE_DW_DW_ARG = 7, - asBCTYPE_wW_rW_rW_ARG = 8, - asBCTYPE_wW_QW_ARG = 9, - asBCTYPE_wW_rW_ARG = 10, - asBCTYPE_rW_ARG = 11, - asBCTYPE_wW_DW_ARG = 12, - asBCTYPE_wW_rW_DW_ARG = 13, - asBCTYPE_rW_rW_ARG = 14, - asBCTYPE_wW_W_ARG = 15, - asBCTYPE_QW_DW_ARG = 16, - asBCTYPE_rW_QW_ARG = 17, - asBCTYPE_W_DW_ARG = 18, - asBCTYPE_rW_W_DW_ARG = 19, - asBCTYPE_rW_DW_DW_ARG = 20 + asBCTYPE_INFO = 0, + asBCTYPE_NO_ARG = 1, + asBCTYPE_W_ARG = 2, + asBCTYPE_wW_ARG = 3, + asBCTYPE_DW_ARG = 4, + asBCTYPE_rW_DW_ARG = 5, + asBCTYPE_QW_ARG = 6, + asBCTYPE_DW_DW_ARG = 7, + asBCTYPE_wW_rW_rW_ARG = 8, + asBCTYPE_wW_QW_ARG = 9, + asBCTYPE_wW_rW_ARG = 10, + asBCTYPE_rW_ARG = 11, + asBCTYPE_wW_DW_ARG = 12, + asBCTYPE_wW_rW_DW_ARG = 13, + asBCTYPE_rW_rW_ARG = 14, + asBCTYPE_wW_W_ARG = 15, + asBCTYPE_QW_DW_ARG = 16, + asBCTYPE_rW_QW_ARG = 17, + asBCTYPE_W_DW_ARG = 18, + asBCTYPE_rW_W_DW_ARG = 19, + asBCTYPE_rW_DW_DW_ARG = 20 }; // Instruction type sizes const int asBCTypeSize[21] = { - 0, // asBCTYPE_INFO - 1, // asBCTYPE_NO_ARG - 1, // asBCTYPE_W_ARG - 1, // asBCTYPE_wW_ARG - 2, // asBCTYPE_DW_ARG - 2, // asBCTYPE_rW_DW_ARG - 3, // asBCTYPE_QW_ARG - 3, // asBCTYPE_DW_DW_ARG - 2, // asBCTYPE_wW_rW_rW_ARG - 3, // asBCTYPE_wW_QW_ARG - 2, // asBCTYPE_wW_rW_ARG - 1, // asBCTYPE_rW_ARG - 2, // asBCTYPE_wW_DW_ARG - 3, // asBCTYPE_wW_rW_DW_ARG - 2, // asBCTYPE_rW_rW_ARG - 2, // asBCTYPE_wW_W_ARG - 4, // asBCTYPE_QW_DW_ARG - 3, // asBCTYPE_rW_QW_ARG - 2, // asBCTYPE_W_DW_ARG - 3, // asBCTYPE_rW_W_DW_ARG - 3 // asBCTYPE_rW_DW_DW_ARG + 0, // asBCTYPE_INFO + 1, // asBCTYPE_NO_ARG + 1, // asBCTYPE_W_ARG + 1, // asBCTYPE_wW_ARG + 2, // asBCTYPE_DW_ARG + 2, // asBCTYPE_rW_DW_ARG + 3, // asBCTYPE_QW_ARG + 3, // asBCTYPE_DW_DW_ARG + 2, // asBCTYPE_wW_rW_rW_ARG + 3, // asBCTYPE_wW_QW_ARG + 2, // asBCTYPE_wW_rW_ARG + 1, // asBCTYPE_rW_ARG + 2, // asBCTYPE_wW_DW_ARG + 3, // asBCTYPE_wW_rW_DW_ARG + 2, // asBCTYPE_rW_rW_ARG + 2, // asBCTYPE_wW_W_ARG + 4, // asBCTYPE_QW_DW_ARG + 3, // asBCTYPE_rW_QW_ARG + 2, // asBCTYPE_W_DW_ARG + 3, // asBCTYPE_rW_W_DW_ARG + 3 // asBCTYPE_rW_DW_DW_ARG }; // Instruction info struct asSBCInfo { - asEBCInstr bc; - asEBCType type; - int stackInc; - const char *name; + asEBCInstr bc; + asEBCType type; + int stackInc; + const char *name; }; #ifndef AS_64BIT_PTR - #define asBCTYPE_PTR_ARG asBCTYPE_DW_ARG - #define asBCTYPE_PTR_DW_ARG asBCTYPE_DW_DW_ARG - #define asBCTYPE_wW_PTR_ARG asBCTYPE_wW_DW_ARG - #define asBCTYPE_rW_PTR_ARG asBCTYPE_rW_DW_ARG - #ifndef AS_PTR_SIZE - #define AS_PTR_SIZE 1 - #endif + #define asBCTYPE_PTR_ARG asBCTYPE_DW_ARG + #define asBCTYPE_PTR_DW_ARG asBCTYPE_DW_DW_ARG + #define asBCTYPE_wW_PTR_ARG asBCTYPE_wW_DW_ARG + #define asBCTYPE_rW_PTR_ARG asBCTYPE_rW_DW_ARG + #ifndef AS_PTR_SIZE + #define AS_PTR_SIZE 1 + #endif #else - #define asBCTYPE_PTR_ARG asBCTYPE_QW_ARG - #define asBCTYPE_PTR_DW_ARG asBCTYPE_QW_DW_ARG - #define asBCTYPE_wW_PTR_ARG asBCTYPE_wW_QW_ARG - #define asBCTYPE_rW_PTR_ARG asBCTYPE_rW_QW_ARG - #ifndef AS_PTR_SIZE - #define AS_PTR_SIZE 2 - #endif + #define asBCTYPE_PTR_ARG asBCTYPE_QW_ARG + #define asBCTYPE_PTR_DW_ARG asBCTYPE_QW_DW_ARG + #define asBCTYPE_wW_PTR_ARG asBCTYPE_wW_QW_ARG + #define asBCTYPE_rW_PTR_ARG asBCTYPE_rW_QW_ARG + #ifndef AS_PTR_SIZE + #define AS_PTR_SIZE 2 + #endif #endif #define asBCINFO(b,t,s) {asBC_##b, asBCTYPE_##t, s, #b} @@ -1531,264 +1651,264 @@ struct asSBCInfo const asSBCInfo asBCInfo[256] = { - asBCINFO(PopPtr, NO_ARG, -AS_PTR_SIZE), - asBCINFO(PshGPtr, PTR_ARG, AS_PTR_SIZE), - asBCINFO(PshC4, DW_ARG, 1), - asBCINFO(PshV4, rW_ARG, 1), - asBCINFO(PSF, rW_ARG, AS_PTR_SIZE), - asBCINFO(SwapPtr, NO_ARG, 0), - asBCINFO(NOT, rW_ARG, 0), - asBCINFO(PshG4, PTR_ARG, 1), - asBCINFO(LdGRdR4, wW_PTR_ARG, 0), - asBCINFO(CALL, DW_ARG, 0xFFFF), - asBCINFO(RET, W_ARG, 0xFFFF), - asBCINFO(JMP, DW_ARG, 0), - asBCINFO(JZ, DW_ARG, 0), - asBCINFO(JNZ, DW_ARG, 0), - asBCINFO(JS, DW_ARG, 0), - asBCINFO(JNS, DW_ARG, 0), - asBCINFO(JP, DW_ARG, 0), - asBCINFO(JNP, DW_ARG, 0), - asBCINFO(TZ, NO_ARG, 0), - asBCINFO(TNZ, NO_ARG, 0), - asBCINFO(TS, NO_ARG, 0), - asBCINFO(TNS, NO_ARG, 0), - asBCINFO(TP, NO_ARG, 0), - asBCINFO(TNP, NO_ARG, 0), - asBCINFO(NEGi, rW_ARG, 0), - asBCINFO(NEGf, rW_ARG, 0), - asBCINFO(NEGd, rW_ARG, 0), - asBCINFO(INCi16, NO_ARG, 0), - asBCINFO(INCi8, NO_ARG, 0), - asBCINFO(DECi16, NO_ARG, 0), - asBCINFO(DECi8, NO_ARG, 0), - asBCINFO(INCi, NO_ARG, 0), - asBCINFO(DECi, NO_ARG, 0), - asBCINFO(INCf, NO_ARG, 0), - asBCINFO(DECf, NO_ARG, 0), - asBCINFO(INCd, NO_ARG, 0), - asBCINFO(DECd, NO_ARG, 0), - asBCINFO(IncVi, rW_ARG, 0), - asBCINFO(DecVi, rW_ARG, 0), - asBCINFO(BNOT, rW_ARG, 0), - asBCINFO(BAND, wW_rW_rW_ARG, 0), - asBCINFO(BOR, wW_rW_rW_ARG, 0), - asBCINFO(BXOR, wW_rW_rW_ARG, 0), - asBCINFO(BSLL, wW_rW_rW_ARG, 0), - asBCINFO(BSRL, wW_rW_rW_ARG, 0), - asBCINFO(BSRA, wW_rW_rW_ARG, 0), - asBCINFO(COPY, W_DW_ARG, -AS_PTR_SIZE), - asBCINFO(PshC8, QW_ARG, 2), - asBCINFO(PshVPtr, rW_ARG, AS_PTR_SIZE), - asBCINFO(RDSPtr, NO_ARG, 0), - asBCINFO(CMPd, rW_rW_ARG, 0), - asBCINFO(CMPu, rW_rW_ARG, 0), - asBCINFO(CMPf, rW_rW_ARG, 0), - asBCINFO(CMPi, rW_rW_ARG, 0), - asBCINFO(CMPIi, rW_DW_ARG, 0), - asBCINFO(CMPIf, rW_DW_ARG, 0), - asBCINFO(CMPIu, rW_DW_ARG, 0), - asBCINFO(JMPP, rW_ARG, 0), - asBCINFO(PopRPtr, NO_ARG, -AS_PTR_SIZE), - asBCINFO(PshRPtr, NO_ARG, AS_PTR_SIZE), - asBCINFO(STR, W_ARG, 1+AS_PTR_SIZE), - asBCINFO(CALLSYS, DW_ARG, 0xFFFF), - asBCINFO(CALLBND, DW_ARG, 0xFFFF), - asBCINFO(SUSPEND, NO_ARG, 0), - asBCINFO(ALLOC, PTR_DW_ARG, 0xFFFF), - asBCINFO(FREE, wW_PTR_ARG, 0), - asBCINFO(LOADOBJ, rW_ARG, 0), - asBCINFO(STOREOBJ, wW_ARG, 0), - asBCINFO(GETOBJ, W_ARG, 0), - asBCINFO(REFCPY, PTR_ARG, -AS_PTR_SIZE), - asBCINFO(CHKREF, NO_ARG, 0), - asBCINFO(GETOBJREF, W_ARG, 0), - asBCINFO(GETREF, W_ARG, 0), - asBCINFO(PshNull, NO_ARG, AS_PTR_SIZE), - asBCINFO(ClrVPtr, wW_ARG, 0), - asBCINFO(OBJTYPE, PTR_ARG, AS_PTR_SIZE), - asBCINFO(TYPEID, DW_ARG, 1), - asBCINFO(SetV4, wW_DW_ARG, 0), - asBCINFO(SetV8, wW_QW_ARG, 0), - asBCINFO(ADDSi, W_DW_ARG, 0), - asBCINFO(CpyVtoV4, wW_rW_ARG, 0), - asBCINFO(CpyVtoV8, wW_rW_ARG, 0), - asBCINFO(CpyVtoR4, rW_ARG, 0), - asBCINFO(CpyVtoR8, rW_ARG, 0), - asBCINFO(CpyVtoG4, rW_PTR_ARG, 0), - asBCINFO(CpyRtoV4, wW_ARG, 0), - asBCINFO(CpyRtoV8, wW_ARG, 0), - asBCINFO(CpyGtoV4, wW_PTR_ARG, 0), - asBCINFO(WRTV1, rW_ARG, 0), - asBCINFO(WRTV2, rW_ARG, 0), - asBCINFO(WRTV4, rW_ARG, 0), - asBCINFO(WRTV8, rW_ARG, 0), - asBCINFO(RDR1, wW_ARG, 0), - asBCINFO(RDR2, wW_ARG, 0), - asBCINFO(RDR4, wW_ARG, 0), - asBCINFO(RDR8, wW_ARG, 0), - asBCINFO(LDG, PTR_ARG, 0), - asBCINFO(LDV, rW_ARG, 0), - asBCINFO(PGA, PTR_ARG, AS_PTR_SIZE), - asBCINFO(CmpPtr, rW_rW_ARG, 0), - asBCINFO(VAR, rW_ARG, AS_PTR_SIZE), - asBCINFO(iTOf, rW_ARG, 0), - asBCINFO(fTOi, rW_ARG, 0), - asBCINFO(uTOf, rW_ARG, 0), - asBCINFO(fTOu, rW_ARG, 0), - asBCINFO(sbTOi, rW_ARG, 0), - asBCINFO(swTOi, rW_ARG, 0), - asBCINFO(ubTOi, rW_ARG, 0), - asBCINFO(uwTOi, rW_ARG, 0), - asBCINFO(dTOi, wW_rW_ARG, 0), - asBCINFO(dTOu, wW_rW_ARG, 0), - asBCINFO(dTOf, wW_rW_ARG, 0), - asBCINFO(iTOd, wW_rW_ARG, 0), - asBCINFO(uTOd, wW_rW_ARG, 0), - asBCINFO(fTOd, wW_rW_ARG, 0), - asBCINFO(ADDi, wW_rW_rW_ARG, 0), - asBCINFO(SUBi, wW_rW_rW_ARG, 0), - asBCINFO(MULi, wW_rW_rW_ARG, 0), - asBCINFO(DIVi, wW_rW_rW_ARG, 0), - asBCINFO(MODi, wW_rW_rW_ARG, 0), - asBCINFO(ADDf, wW_rW_rW_ARG, 0), - asBCINFO(SUBf, wW_rW_rW_ARG, 0), - asBCINFO(MULf, wW_rW_rW_ARG, 0), - asBCINFO(DIVf, wW_rW_rW_ARG, 0), - asBCINFO(MODf, wW_rW_rW_ARG, 0), - asBCINFO(ADDd, wW_rW_rW_ARG, 0), - asBCINFO(SUBd, wW_rW_rW_ARG, 0), - asBCINFO(MULd, wW_rW_rW_ARG, 0), - asBCINFO(DIVd, wW_rW_rW_ARG, 0), - asBCINFO(MODd, wW_rW_rW_ARG, 0), - asBCINFO(ADDIi, wW_rW_DW_ARG, 0), - asBCINFO(SUBIi, wW_rW_DW_ARG, 0), - asBCINFO(MULIi, wW_rW_DW_ARG, 0), - asBCINFO(ADDIf, wW_rW_DW_ARG, 0), - asBCINFO(SUBIf, wW_rW_DW_ARG, 0), - asBCINFO(MULIf, wW_rW_DW_ARG, 0), - asBCINFO(SetG4, PTR_DW_ARG, 0), - asBCINFO(ChkRefS, NO_ARG, 0), - asBCINFO(ChkNullV, rW_ARG, 0), - asBCINFO(CALLINTF, DW_ARG, 0xFFFF), - asBCINFO(iTOb, rW_ARG, 0), - asBCINFO(iTOw, rW_ARG, 0), - asBCINFO(SetV1, wW_DW_ARG, 0), - asBCINFO(SetV2, wW_DW_ARG, 0), - asBCINFO(Cast, DW_ARG, -AS_PTR_SIZE), - asBCINFO(i64TOi, wW_rW_ARG, 0), - asBCINFO(uTOi64, wW_rW_ARG, 0), - asBCINFO(iTOi64, wW_rW_ARG, 0), - asBCINFO(fTOi64, wW_rW_ARG, 0), - asBCINFO(dTOi64, rW_ARG, 0), - asBCINFO(fTOu64, wW_rW_ARG, 0), - asBCINFO(dTOu64, rW_ARG, 0), - asBCINFO(i64TOf, wW_rW_ARG, 0), - asBCINFO(u64TOf, wW_rW_ARG, 0), - asBCINFO(i64TOd, rW_ARG, 0), - asBCINFO(u64TOd, rW_ARG, 0), - asBCINFO(NEGi64, rW_ARG, 0), - asBCINFO(INCi64, NO_ARG, 0), - asBCINFO(DECi64, NO_ARG, 0), - asBCINFO(BNOT64, rW_ARG, 0), - asBCINFO(ADDi64, wW_rW_rW_ARG, 0), - asBCINFO(SUBi64, wW_rW_rW_ARG, 0), - asBCINFO(MULi64, wW_rW_rW_ARG, 0), - asBCINFO(DIVi64, wW_rW_rW_ARG, 0), - asBCINFO(MODi64, wW_rW_rW_ARG, 0), - asBCINFO(BAND64, wW_rW_rW_ARG, 0), - asBCINFO(BOR64, wW_rW_rW_ARG, 0), - asBCINFO(BXOR64, wW_rW_rW_ARG, 0), - asBCINFO(BSLL64, wW_rW_rW_ARG, 0), - asBCINFO(BSRL64, wW_rW_rW_ARG, 0), - asBCINFO(BSRA64, wW_rW_rW_ARG, 0), - asBCINFO(CMPi64, rW_rW_ARG, 0), - asBCINFO(CMPu64, rW_rW_ARG, 0), - asBCINFO(ChkNullS, W_ARG, 0), - asBCINFO(ClrHi, NO_ARG, 0), - asBCINFO(JitEntry, PTR_ARG, 0), - asBCINFO(CallPtr, rW_ARG, 0xFFFF), - asBCINFO(FuncPtr, PTR_ARG, AS_PTR_SIZE), - asBCINFO(LoadThisR, W_DW_ARG, 0), - asBCINFO(PshV8, rW_ARG, 2), - asBCINFO(DIVu, wW_rW_rW_ARG, 0), - asBCINFO(MODu, wW_rW_rW_ARG, 0), - asBCINFO(DIVu64, wW_rW_rW_ARG, 0), - asBCINFO(MODu64, wW_rW_rW_ARG, 0), - asBCINFO(LoadRObjR, rW_W_DW_ARG, 0), - asBCINFO(LoadVObjR, rW_W_DW_ARG, 0), - asBCINFO(RefCpyV, wW_PTR_ARG, 0), - asBCINFO(JLowZ, DW_ARG, 0), - asBCINFO(JLowNZ, DW_ARG, 0), - asBCINFO(AllocMem, wW_DW_ARG, 0), - asBCINFO(SetListSize, rW_DW_DW_ARG, 0), - asBCINFO(PshListElmnt, rW_DW_ARG, AS_PTR_SIZE), - asBCINFO(SetListType, rW_DW_DW_ARG, 0), - asBCINFO(POWi, wW_rW_rW_ARG, 0), - asBCINFO(POWu, wW_rW_rW_ARG, 0), - asBCINFO(POWf, wW_rW_rW_ARG, 0), - asBCINFO(POWd, wW_rW_rW_ARG, 0), - asBCINFO(POWdi, wW_rW_rW_ARG, 0), - asBCINFO(POWi64, wW_rW_rW_ARG, 0), - asBCINFO(POWu64, wW_rW_rW_ARG, 0), + asBCINFO(PopPtr, NO_ARG, -AS_PTR_SIZE), + asBCINFO(PshGPtr, PTR_ARG, AS_PTR_SIZE), + asBCINFO(PshC4, DW_ARG, 1), + asBCINFO(PshV4, rW_ARG, 1), + asBCINFO(PSF, rW_ARG, AS_PTR_SIZE), + asBCINFO(SwapPtr, NO_ARG, 0), + asBCINFO(NOT, rW_ARG, 0), + asBCINFO(PshG4, PTR_ARG, 1), + asBCINFO(LdGRdR4, wW_PTR_ARG, 0), + asBCINFO(CALL, DW_ARG, 0xFFFF), + asBCINFO(RET, W_ARG, 0xFFFF), + asBCINFO(JMP, DW_ARG, 0), + asBCINFO(JZ, DW_ARG, 0), + asBCINFO(JNZ, DW_ARG, 0), + asBCINFO(JS, DW_ARG, 0), + asBCINFO(JNS, DW_ARG, 0), + asBCINFO(JP, DW_ARG, 0), + asBCINFO(JNP, DW_ARG, 0), + asBCINFO(TZ, NO_ARG, 0), + asBCINFO(TNZ, NO_ARG, 0), + asBCINFO(TS, NO_ARG, 0), + asBCINFO(TNS, NO_ARG, 0), + asBCINFO(TP, NO_ARG, 0), + asBCINFO(TNP, NO_ARG, 0), + asBCINFO(NEGi, rW_ARG, 0), + asBCINFO(NEGf, rW_ARG, 0), + asBCINFO(NEGd, rW_ARG, 0), + asBCINFO(INCi16, NO_ARG, 0), + asBCINFO(INCi8, NO_ARG, 0), + asBCINFO(DECi16, NO_ARG, 0), + asBCINFO(DECi8, NO_ARG, 0), + asBCINFO(INCi, NO_ARG, 0), + asBCINFO(DECi, NO_ARG, 0), + asBCINFO(INCf, NO_ARG, 0), + asBCINFO(DECf, NO_ARG, 0), + asBCINFO(INCd, NO_ARG, 0), + asBCINFO(DECd, NO_ARG, 0), + asBCINFO(IncVi, rW_ARG, 0), + asBCINFO(DecVi, rW_ARG, 0), + asBCINFO(BNOT, rW_ARG, 0), + asBCINFO(BAND, wW_rW_rW_ARG, 0), + asBCINFO(BOR, wW_rW_rW_ARG, 0), + asBCINFO(BXOR, wW_rW_rW_ARG, 0), + asBCINFO(BSLL, wW_rW_rW_ARG, 0), + asBCINFO(BSRL, wW_rW_rW_ARG, 0), + asBCINFO(BSRA, wW_rW_rW_ARG, 0), + asBCINFO(COPY, W_DW_ARG, -AS_PTR_SIZE), + asBCINFO(PshC8, QW_ARG, 2), + asBCINFO(PshVPtr, rW_ARG, AS_PTR_SIZE), + asBCINFO(RDSPtr, NO_ARG, 0), + asBCINFO(CMPd, rW_rW_ARG, 0), + asBCINFO(CMPu, rW_rW_ARG, 0), + asBCINFO(CMPf, rW_rW_ARG, 0), + asBCINFO(CMPi, rW_rW_ARG, 0), + asBCINFO(CMPIi, rW_DW_ARG, 0), + asBCINFO(CMPIf, rW_DW_ARG, 0), + asBCINFO(CMPIu, rW_DW_ARG, 0), + asBCINFO(JMPP, rW_ARG, 0), + asBCINFO(PopRPtr, NO_ARG, -AS_PTR_SIZE), + asBCINFO(PshRPtr, NO_ARG, AS_PTR_SIZE), + asBCINFO(STR, W_ARG, 1+AS_PTR_SIZE), + asBCINFO(CALLSYS, DW_ARG, 0xFFFF), + asBCINFO(CALLBND, DW_ARG, 0xFFFF), + asBCINFO(SUSPEND, NO_ARG, 0), + asBCINFO(ALLOC, PTR_DW_ARG, 0xFFFF), + asBCINFO(FREE, wW_PTR_ARG, 0), + asBCINFO(LOADOBJ, rW_ARG, 0), + asBCINFO(STOREOBJ, wW_ARG, 0), + asBCINFO(GETOBJ, W_ARG, 0), + asBCINFO(REFCPY, PTR_ARG, -AS_PTR_SIZE), + asBCINFO(CHKREF, NO_ARG, 0), + asBCINFO(GETOBJREF, W_ARG, 0), + asBCINFO(GETREF, W_ARG, 0), + asBCINFO(PshNull, NO_ARG, AS_PTR_SIZE), + asBCINFO(ClrVPtr, wW_ARG, 0), + asBCINFO(OBJTYPE, PTR_ARG, AS_PTR_SIZE), + asBCINFO(TYPEID, DW_ARG, 1), + asBCINFO(SetV4, wW_DW_ARG, 0), + asBCINFO(SetV8, wW_QW_ARG, 0), + asBCINFO(ADDSi, W_DW_ARG, 0), + asBCINFO(CpyVtoV4, wW_rW_ARG, 0), + asBCINFO(CpyVtoV8, wW_rW_ARG, 0), + asBCINFO(CpyVtoR4, rW_ARG, 0), + asBCINFO(CpyVtoR8, rW_ARG, 0), + asBCINFO(CpyVtoG4, rW_PTR_ARG, 0), + asBCINFO(CpyRtoV4, wW_ARG, 0), + asBCINFO(CpyRtoV8, wW_ARG, 0), + asBCINFO(CpyGtoV4, wW_PTR_ARG, 0), + asBCINFO(WRTV1, rW_ARG, 0), + asBCINFO(WRTV2, rW_ARG, 0), + asBCINFO(WRTV4, rW_ARG, 0), + asBCINFO(WRTV8, rW_ARG, 0), + asBCINFO(RDR1, wW_ARG, 0), + asBCINFO(RDR2, wW_ARG, 0), + asBCINFO(RDR4, wW_ARG, 0), + asBCINFO(RDR8, wW_ARG, 0), + asBCINFO(LDG, PTR_ARG, 0), + asBCINFO(LDV, rW_ARG, 0), + asBCINFO(PGA, PTR_ARG, AS_PTR_SIZE), + asBCINFO(CmpPtr, rW_rW_ARG, 0), + asBCINFO(VAR, rW_ARG, AS_PTR_SIZE), + asBCINFO(iTOf, rW_ARG, 0), + asBCINFO(fTOi, rW_ARG, 0), + asBCINFO(uTOf, rW_ARG, 0), + asBCINFO(fTOu, rW_ARG, 0), + asBCINFO(sbTOi, rW_ARG, 0), + asBCINFO(swTOi, rW_ARG, 0), + asBCINFO(ubTOi, rW_ARG, 0), + asBCINFO(uwTOi, rW_ARG, 0), + asBCINFO(dTOi, wW_rW_ARG, 0), + asBCINFO(dTOu, wW_rW_ARG, 0), + asBCINFO(dTOf, wW_rW_ARG, 0), + asBCINFO(iTOd, wW_rW_ARG, 0), + asBCINFO(uTOd, wW_rW_ARG, 0), + asBCINFO(fTOd, wW_rW_ARG, 0), + asBCINFO(ADDi, wW_rW_rW_ARG, 0), + asBCINFO(SUBi, wW_rW_rW_ARG, 0), + asBCINFO(MULi, wW_rW_rW_ARG, 0), + asBCINFO(DIVi, wW_rW_rW_ARG, 0), + asBCINFO(MODi, wW_rW_rW_ARG, 0), + asBCINFO(ADDf, wW_rW_rW_ARG, 0), + asBCINFO(SUBf, wW_rW_rW_ARG, 0), + asBCINFO(MULf, wW_rW_rW_ARG, 0), + asBCINFO(DIVf, wW_rW_rW_ARG, 0), + asBCINFO(MODf, wW_rW_rW_ARG, 0), + asBCINFO(ADDd, wW_rW_rW_ARG, 0), + asBCINFO(SUBd, wW_rW_rW_ARG, 0), + asBCINFO(MULd, wW_rW_rW_ARG, 0), + asBCINFO(DIVd, wW_rW_rW_ARG, 0), + asBCINFO(MODd, wW_rW_rW_ARG, 0), + asBCINFO(ADDIi, wW_rW_DW_ARG, 0), + asBCINFO(SUBIi, wW_rW_DW_ARG, 0), + asBCINFO(MULIi, wW_rW_DW_ARG, 0), + asBCINFO(ADDIf, wW_rW_DW_ARG, 0), + asBCINFO(SUBIf, wW_rW_DW_ARG, 0), + asBCINFO(MULIf, wW_rW_DW_ARG, 0), + asBCINFO(SetG4, PTR_DW_ARG, 0), + asBCINFO(ChkRefS, NO_ARG, 0), + asBCINFO(ChkNullV, rW_ARG, 0), + asBCINFO(CALLINTF, DW_ARG, 0xFFFF), + asBCINFO(iTOb, rW_ARG, 0), + asBCINFO(iTOw, rW_ARG, 0), + asBCINFO(SetV1, wW_DW_ARG, 0), + asBCINFO(SetV2, wW_DW_ARG, 0), + asBCINFO(Cast, DW_ARG, -AS_PTR_SIZE), + asBCINFO(i64TOi, wW_rW_ARG, 0), + asBCINFO(uTOi64, wW_rW_ARG, 0), + asBCINFO(iTOi64, wW_rW_ARG, 0), + asBCINFO(fTOi64, wW_rW_ARG, 0), + asBCINFO(dTOi64, rW_ARG, 0), + asBCINFO(fTOu64, wW_rW_ARG, 0), + asBCINFO(dTOu64, rW_ARG, 0), + asBCINFO(i64TOf, wW_rW_ARG, 0), + asBCINFO(u64TOf, wW_rW_ARG, 0), + asBCINFO(i64TOd, rW_ARG, 0), + asBCINFO(u64TOd, rW_ARG, 0), + asBCINFO(NEGi64, rW_ARG, 0), + asBCINFO(INCi64, NO_ARG, 0), + asBCINFO(DECi64, NO_ARG, 0), + asBCINFO(BNOT64, rW_ARG, 0), + asBCINFO(ADDi64, wW_rW_rW_ARG, 0), + asBCINFO(SUBi64, wW_rW_rW_ARG, 0), + asBCINFO(MULi64, wW_rW_rW_ARG, 0), + asBCINFO(DIVi64, wW_rW_rW_ARG, 0), + asBCINFO(MODi64, wW_rW_rW_ARG, 0), + asBCINFO(BAND64, wW_rW_rW_ARG, 0), + asBCINFO(BOR64, wW_rW_rW_ARG, 0), + asBCINFO(BXOR64, wW_rW_rW_ARG, 0), + asBCINFO(BSLL64, wW_rW_rW_ARG, 0), + asBCINFO(BSRL64, wW_rW_rW_ARG, 0), + asBCINFO(BSRA64, wW_rW_rW_ARG, 0), + asBCINFO(CMPi64, rW_rW_ARG, 0), + asBCINFO(CMPu64, rW_rW_ARG, 0), + asBCINFO(ChkNullS, W_ARG, 0), + asBCINFO(ClrHi, NO_ARG, 0), + asBCINFO(JitEntry, PTR_ARG, 0), + asBCINFO(CallPtr, rW_ARG, 0xFFFF), + asBCINFO(FuncPtr, PTR_ARG, AS_PTR_SIZE), + asBCINFO(LoadThisR, W_DW_ARG, 0), + asBCINFO(PshV8, rW_ARG, 2), + asBCINFO(DIVu, wW_rW_rW_ARG, 0), + asBCINFO(MODu, wW_rW_rW_ARG, 0), + asBCINFO(DIVu64, wW_rW_rW_ARG, 0), + asBCINFO(MODu64, wW_rW_rW_ARG, 0), + asBCINFO(LoadRObjR, rW_W_DW_ARG, 0), + asBCINFO(LoadVObjR, rW_W_DW_ARG, 0), + asBCINFO(RefCpyV, wW_PTR_ARG, 0), + asBCINFO(JLowZ, DW_ARG, 0), + asBCINFO(JLowNZ, DW_ARG, 0), + asBCINFO(AllocMem, wW_DW_ARG, 0), + asBCINFO(SetListSize, rW_DW_DW_ARG, 0), + asBCINFO(PshListElmnt, rW_DW_ARG, AS_PTR_SIZE), + asBCINFO(SetListType, rW_DW_DW_ARG, 0), + asBCINFO(POWi, wW_rW_rW_ARG, 0), + asBCINFO(POWu, wW_rW_rW_ARG, 0), + asBCINFO(POWf, wW_rW_rW_ARG, 0), + asBCINFO(POWd, wW_rW_rW_ARG, 0), + asBCINFO(POWdi, wW_rW_rW_ARG, 0), + asBCINFO(POWi64, wW_rW_rW_ARG, 0), + asBCINFO(POWu64, wW_rW_rW_ARG, 0), - asBCINFO_DUMMY(200), - asBCINFO_DUMMY(201), - asBCINFO_DUMMY(202), - asBCINFO_DUMMY(203), - asBCINFO_DUMMY(204), - asBCINFO_DUMMY(205), - asBCINFO_DUMMY(206), - asBCINFO_DUMMY(207), - asBCINFO_DUMMY(208), - asBCINFO_DUMMY(209), - asBCINFO_DUMMY(210), - asBCINFO_DUMMY(211), - asBCINFO_DUMMY(212), - asBCINFO_DUMMY(213), - asBCINFO_DUMMY(214), - asBCINFO_DUMMY(215), - asBCINFO_DUMMY(216), - asBCINFO_DUMMY(217), - asBCINFO_DUMMY(218), - asBCINFO_DUMMY(219), - asBCINFO_DUMMY(220), - asBCINFO_DUMMY(221), - asBCINFO_DUMMY(222), - asBCINFO_DUMMY(223), - asBCINFO_DUMMY(224), - asBCINFO_DUMMY(225), - asBCINFO_DUMMY(226), - asBCINFO_DUMMY(227), - asBCINFO_DUMMY(228), - asBCINFO_DUMMY(229), - asBCINFO_DUMMY(230), - asBCINFO_DUMMY(231), - asBCINFO_DUMMY(232), - asBCINFO_DUMMY(233), - asBCINFO_DUMMY(234), - asBCINFO_DUMMY(235), - asBCINFO_DUMMY(236), - asBCINFO_DUMMY(237), - asBCINFO_DUMMY(238), - asBCINFO_DUMMY(239), - asBCINFO_DUMMY(240), - asBCINFO_DUMMY(241), - asBCINFO_DUMMY(242), - asBCINFO_DUMMY(243), - asBCINFO_DUMMY(244), - asBCINFO_DUMMY(245), - asBCINFO_DUMMY(246), - asBCINFO_DUMMY(247), - asBCINFO_DUMMY(248), - asBCINFO_DUMMY(249), - asBCINFO_DUMMY(250), + asBCINFO_DUMMY(200), + asBCINFO_DUMMY(201), + asBCINFO_DUMMY(202), + asBCINFO_DUMMY(203), + asBCINFO_DUMMY(204), + asBCINFO_DUMMY(205), + asBCINFO_DUMMY(206), + asBCINFO_DUMMY(207), + asBCINFO_DUMMY(208), + asBCINFO_DUMMY(209), + asBCINFO_DUMMY(210), + asBCINFO_DUMMY(211), + asBCINFO_DUMMY(212), + asBCINFO_DUMMY(213), + asBCINFO_DUMMY(214), + asBCINFO_DUMMY(215), + asBCINFO_DUMMY(216), + asBCINFO_DUMMY(217), + asBCINFO_DUMMY(218), + asBCINFO_DUMMY(219), + asBCINFO_DUMMY(220), + asBCINFO_DUMMY(221), + asBCINFO_DUMMY(222), + asBCINFO_DUMMY(223), + asBCINFO_DUMMY(224), + asBCINFO_DUMMY(225), + asBCINFO_DUMMY(226), + asBCINFO_DUMMY(227), + asBCINFO_DUMMY(228), + asBCINFO_DUMMY(229), + asBCINFO_DUMMY(230), + asBCINFO_DUMMY(231), + asBCINFO_DUMMY(232), + asBCINFO_DUMMY(233), + asBCINFO_DUMMY(234), + asBCINFO_DUMMY(235), + asBCINFO_DUMMY(236), + asBCINFO_DUMMY(237), + asBCINFO_DUMMY(238), + asBCINFO_DUMMY(239), + asBCINFO_DUMMY(240), + asBCINFO_DUMMY(241), + asBCINFO_DUMMY(242), + asBCINFO_DUMMY(243), + asBCINFO_DUMMY(244), + asBCINFO_DUMMY(245), + asBCINFO_DUMMY(246), + asBCINFO_DUMMY(247), + asBCINFO_DUMMY(248), + asBCINFO_DUMMY(249), + asBCINFO_DUMMY(250), - asBCINFO(VarDecl, W_ARG, 0), - asBCINFO(Block, INFO, 0), - asBCINFO(ObjInfo, rW_DW_ARG, 0), - asBCINFO(LINE, INFO, 0), - asBCINFO(LABEL, INFO, 0) + asBCINFO(VarDecl, W_ARG, 0), + asBCINFO(Block, INFO, 0), + asBCINFO(ObjInfo, rW_DW_ARG, 0), + asBCINFO(LINE, INFO, 0), + asBCINFO(LABEL, INFO, 0) }; // Macros to access bytecode instruction arguments diff --git a/lib/angelscript/projects/cmake/CMakeLists.txt b/lib/angelscript/projects/cmake/CMakeLists.txt index cce6b8d2e..b3e3cea1e 100644 --- a/lib/angelscript/projects/cmake/CMakeLists.txt +++ b/lib/angelscript/projects/cmake/CMakeLists.txt @@ -1,116 +1,155 @@ +cmake_minimum_required(VERSION 2.6) set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE) cmake_policy(SET CMP0003 NEW) +project(angelscript) + +option(BUILD_SHARED_LIBS "Build shared library" OFF) + +if(APPLE) + option(BUILD_FRAMEWORK "Build Framework bundle for OSX" OFF) +endif() + +set(ANGELSCRIPT_VERSION_MAJOR 2) +set(ANGELSCRIPT_VERSION_MINOR 30) +set(ANGELSCRIPT_VERSION_PATCH 0) +set(PROJECT_VERSION ${ANGELSCRIPT_VERSION_MAJOR}.${ANGELSCRIPT_VERSION_MINOR}.${ANGELSCRIPT_VERSION_PATCH}) + +find_package(Threads) + +set(ANGELSCRIPT_HEADERS + ../../include/angelscript.h + ../../source/as_array.h + ../../source/as_builder.h + ../../source/as_bytecode.h + ../../source/as_callfunc.h + ../../source/as_compiler.h + ../../source/as_config.h + ../../source/as_configgroup.h + ../../source/as_context.h + ../../source/as_criticalsection.h + ../../source/as_datatype.h + ../../source/as_debug.h + ../../source/as_generic.h + ../../source/as_map.h + ../../source/as_memory.h + ../../source/as_module.h + ../../source/as_objecttype.h + ../../source/as_outputbuffer.h + ../../source/as_parser.h + ../../source/as_property.h + ../../source/as_restore.h + ../../source/as_scriptcode.h + ../../source/as_scriptengine.h + ../../source/as_scriptfunction.h + ../../source/as_scriptnode.h + ../../source/as_scriptobject.h + ../../source/as_string.h + ../../source/as_string_util.h + ../../source/as_texts.h + ../../source/as_thread.h + ../../source/as_tokendef.h + ../../source/as_tokenizer.h + ../../source/as_typeinfo.h + ../../source/as_variablescope.h +) + set(ANGELSCRIPT_SOURCE - ../../source/as_atomic.cpp - ../../source/as_builder.cpp - ../../source/as_bytecode.cpp - ../../source/as_callfunc.cpp - ../../source/as_callfunc_x86.cpp - ../../source/as_callfunc_x64_gcc.cpp - ../../source/as_callfunc_x64_msvc.cpp - ../../source/as_callfunc_x64_mingw.cpp - ../../source/as_compiler.cpp - ../../source/as_configgroup.cpp - ../../source/as_context.cpp - ../../source/as_datatype.cpp - ../../source/as_gc.cpp - ../../source/as_generic.cpp - ../../source/as_globalproperty.cpp - ../../source/as_memory.cpp - ../../source/as_module.cpp - ../../source/as_objecttype.cpp - ../../source/as_outputbuffer.cpp - ../../source/as_parser.cpp - ../../source/as_restore.cpp - ../../source/as_scriptcode.cpp - ../../source/as_scriptengine.cpp - ../../source/as_scriptfunction.cpp - ../../source/as_scriptnode.cpp - ../../source/as_scriptobject.cpp - ../../source/as_string.cpp - ../../source/as_string_util.cpp - ../../source/as_thread.cpp - ../../source/as_tokenizer.cpp - ../../source/as_typeinfo.cpp - ../../source/as_variablescope.cpp + ../../source/as_atomic.cpp + ../../source/as_builder.cpp + ../../source/as_bytecode.cpp + ../../source/as_callfunc.cpp + ../../source/as_callfunc_x86.cpp + ../../source/as_callfunc_x64_gcc.cpp + ../../source/as_callfunc_x64_msvc.cpp + ../../source/as_callfunc_x64_mingw.cpp + ../../source/as_compiler.cpp + ../../source/as_configgroup.cpp + ../../source/as_context.cpp + ../../source/as_datatype.cpp + ../../source/as_gc.cpp + ../../source/as_generic.cpp + ../../source/as_globalproperty.cpp + ../../source/as_memory.cpp + ../../source/as_module.cpp + ../../source/as_objecttype.cpp + ../../source/as_outputbuffer.cpp + ../../source/as_parser.cpp + ../../source/as_restore.cpp + ../../source/as_scriptcode.cpp + ../../source/as_scriptengine.cpp + ../../source/as_scriptfunction.cpp + ../../source/as_scriptnode.cpp + ../../source/as_scriptobject.cpp + ../../source/as_string.cpp + ../../source/as_string_util.cpp + ../../source/as_thread.cpp + ../../source/as_tokenizer.cpp + ../../source/as_typeinfo.cpp + ../../source/as_variablescope.cpp ) if(MSVC AND CMAKE_CL_64) - enable_language(ASM_MASM) - if(CMAKE_ASM_MASM_COMPILER_WORKS) - set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} ../../source/as_callfunc_x64_msvc_asm.asm) - else() - message(FATAL ERROR "MSVC x86_64 target requires a working assembler") - endif() + enable_language(ASM_MASM) + if(CMAKE_ASM_MASM_COMPILER_WORKS) + set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} ../../source/as_callfunc_x64_msvc_asm.asm) + else() + message(FATAL ERROR "MSVC x86_64 target requires a working assembler") + endif() endif() if(ANDROID) - enable_language(ASM) - if(CMAKE_ASM_COMPILER_WORKS) - set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} ../../source/as_callfunc_arm.cpp ../../source/as_callfunc_arm_gcc.S) - else() - message(FATAL ERROR "Android target requires a working assembler") - endif(CMAKE_ASM_COMPILER_WORKS) + enable_language(ASM) + if(CMAKE_ASM_COMPILER_WORKS) + set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} ../../source/as_callfunc_arm.cpp ../../source/as_callfunc_arm_gcc.S) + else() + message(FATAL ERROR "Android target requires a working assembler") + endif() endif() -set(ANGELSCRIPT_HEADERS - ../../include/angelscript.h - ../../source/as_array.h - ../../source/as_builder.h - ../../source/as_bytecode.h - ../../source/as_callfunc.h - ../../source/as_compiler.h - ../../source/as_config.h - ../../source/as_configgroup.h - ../../source/as_context.h - ../../source/as_criticalsection.h - ../../source/as_datatype.h - ../../source/as_debug.h - ../../source/as_generic.h - ../../source/as_map.h - ../../source/as_memory.h - ../../source/as_module.h - ../../source/as_objecttype.h - ../../source/as_outputbuffer.h - ../../source/as_parser.h - ../../source/as_property.h - ../../source/as_restore.h - ../../source/as_scriptcode.h - ../../source/as_scriptengine.h - ../../source/as_scriptfunction.h - ../../source/as_scriptnode.h - ../../source/as_scriptobject.h - ../../source/as_string.h - ../../source/as_string_util.h - ../../source/as_texts.h - ../../source/as_thread.h - ../../source/as_tokendef.h - ../../source/as_tokenizer.h - ../../source/as_typeinfo.h - ../../source/as_variablescope.h -) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../include) -add_definitions("-D_CRT_SECURE_NO_WARNINGS -DANGELSCRIPT_EXPORT -D_LIB") +if(MSVC) + set(CMAKE_DEBUG_POSTFIX "d") + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +add_definitions(-DANGELSCRIPT_EXPORT -D_LIB) # Fix x64 issues on Linux if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" AND NOT APPLE) - add_definitions(-fPIC) + add_definitions(-fPIC) endif() -add_library(angelscript STATIC ${ANGELSCRIPT_SOURCE} ${ANGELSCRIPT_HEADERS}) +if(NOT BUILD_FRAMEWORK) + set(ANGELSCRIPT_LIBRARY_NAME angelscript) +else() + set(ANGELSCRIPT_LIBRARY_NAME Angelscript) # OS X frameworks should have capitalized name + set(BUILD_SHARED_LIBS TRUE) +endif() -#set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../lib) +add_library(${ANGELSCRIPT_LIBRARY_NAME} ${ANGELSCRIPT_SOURCE} ${ANGELSCRIPT_HEADERS}) +set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../lib) +target_link_libraries(${ANGELSCRIPT_LIBRARY_NAME} ${CMAKE_THREAD_LIBS_INIT}) -#find_package(Threads) -#target_link_libraries(Angelscript ${CMAKE_THREAD_LIBS_INIT}) +set_target_properties(${ANGELSCRIPT_LIBRARY_NAME} PROPERTIES VERSION ${PROJECT_VERSION}) -#if(MSVC) -# set_target_properties(Angelscript PROPERTIES COMPILE_FLAGS "/MP") -#endif(MSVC) +if(BUILD_FRAMEWORK) + set_target_properties(${ANGELSCRIPT_LIBRARY_NAME} PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION ${PROJECT_VERSION} + MACOSX_FRAMEWORK_IDENTIFIER com.angelcode.Angelscript + MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${PROJECT_VERSION} + MACOSX_FRAMEWORK_BUNDLE_VERSION ${PROJECT_VERSION} + XCODE_ATTRIBUTE_INSTALL_PATH "@rpath" + PUBLIC_HEADER ../../include/angelscript.h + ) +endif() -#set(RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../bin) +if(MSVC) + set_target_properties(${ANGELSCRIPT_LIBRARY_NAME} PROPERTIES COMPILE_FLAGS "/MP") +endif() + +set(RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../bin) diff --git a/lib/angelscript/source/as_array.h b/lib/angelscript/source/as_array.h index 5f47de478..e1487e48d 100644 --- a/lib/angelscript/source/as_array.h +++ b/lib/angelscript/source/as_array.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -47,46 +47,46 @@ template class asCArray public: asCArray(); asCArray(const asCArray &); - asCArray(size_t reserve); + asCArray(asUINT reserve); ~asCArray(); - void Allocate(size_t numElements, bool keepData); - void AllocateNoConstruct(size_t numElements, bool keepData); - size_t GetCapacity() const; + void Allocate(asUINT numElements, bool keepData); + void AllocateNoConstruct(asUINT numElements, bool keepData); + asUINT GetCapacity() const; void PushLast(const T &element); T PopLast(); - bool SetLength(size_t numElements); - bool SetLengthNoConstruct(size_t numElements); - size_t GetLength() const; + bool SetLength(asUINT numElements); + bool SetLengthNoConstruct(asUINT numElements); + asUINT GetLength() const; - void Copy(const T*, size_t count); + void Copy(const T*, asUINT count); asCArray &operator =(const asCArray &); void SwapWith(asCArray &other); - const T &operator [](size_t index) const; - T &operator [](size_t index); + const T &operator [](asUINT index) const; + T &operator [](asUINT index); T *AddressOf(); const T *AddressOf() const; - void Concatenate(const asCArray &); + bool Concatenate(const asCArray &); void Concatenate(T*, unsigned int count); bool Exists(const T &element) const; int IndexOf(const T &element) const; - void RemoveIndex(size_t index); // Removes the entry without reordering the array + void RemoveIndex(asUINT index); // Removes the entry without reordering the array void RemoveValue(const T &element); // Removes the value without reordering the array - void RemoveIndexUnordered(size_t index); // Removes the entry without keeping the order + void RemoveIndexUnordered(asUINT index); // Removes the entry without keeping the order bool operator==(const asCArray &) const; bool operator!=(const asCArray &) const; protected: T *array; - size_t length; - size_t maxLength; - char buf[8]; + asUINT length; // 32bits is enough for all uses of this array + asUINT maxLength; + char buf[2*4*AS_PTR_SIZE]; // Avoid dynamically allocated memory for tiny arrays }; // Implementation @@ -122,7 +122,7 @@ asCArray::asCArray(const asCArray ©) } template -asCArray::asCArray(size_t reserve) +asCArray::asCArray(asUINT reserve) { array = 0; length = 0; @@ -139,13 +139,13 @@ asCArray::~asCArray(void) } template -size_t asCArray::GetLength() const +asUINT asCArray::GetLength() const { return length; } template -const T &asCArray::operator [](size_t index) const +const T &asCArray::operator [](asUINT index) const { asASSERT(index < length); @@ -153,7 +153,7 @@ const T &asCArray::operator [](size_t index) const } template -T &asCArray::operator [](size_t index) +T &asCArray::operator [](asUINT index) { asASSERT(index < length); @@ -189,7 +189,7 @@ T asCArray::PopLast() } template -void asCArray::Allocate(size_t numElements, bool keepData) +void asCArray::Allocate(asUINT numElements, bool keepData) { // We have 4 situations // 1. The previous array is 8 bytes or smaller and the new array is also 8 bytes or smaller @@ -200,7 +200,7 @@ void asCArray::Allocate(size_t numElements, bool keepData) T *tmp = 0; if( numElements ) { - if( sizeof(T)*numElements <= 8 ) + if( sizeof(T)*numElements <= sizeof(buf) ) // Use the internal buffer tmp = reinterpret_cast(buf); else @@ -217,20 +217,20 @@ void asCArray::Allocate(size_t numElements, bool keepData) if( array == tmp ) { // Construct only the newly allocated elements - for( size_t n = length; n < numElements; n++ ) + for( asUINT n = length; n < numElements; n++ ) new (&tmp[n]) T(); } else { // Construct all elements - for( size_t n = 0; n < numElements; n++ ) + for( asUINT n = 0; n < numElements; n++ ) new (&tmp[n]) T(); } } if( array ) { - size_t oldLength = length; + asUINT oldLength = length; if( array == tmp ) { @@ -243,7 +243,7 @@ void asCArray::Allocate(size_t numElements, bool keepData) length = 0; // Call the destructor for elements that are no longer used - for( size_t n = length; n < oldLength; n++ ) + for( asUINT n = length; n < oldLength; n++ ) array[n].~T(); } else @@ -253,14 +253,14 @@ void asCArray::Allocate(size_t numElements, bool keepData) if( length > numElements ) length = numElements; - for( size_t n = 0; n < length; n++ ) + for( asUINT n = 0; n < length; n++ ) tmp[n] = array[n]; } else length = 0; // Call the destructor for all elements - for( size_t n = 0; n < oldLength; n++ ) + for( asUINT n = 0; n < oldLength; n++ ) array[n].~T(); if( array != reinterpret_cast(buf) ) @@ -273,7 +273,7 @@ void asCArray::Allocate(size_t numElements, bool keepData) } template -void asCArray::AllocateNoConstruct(size_t numElements, bool keepData) +void asCArray::AllocateNoConstruct(asUINT numElements, bool keepData) { // We have 4 situations // 1. The previous array is 8 bytes or smaller and the new array is also 8 bytes or smaller @@ -284,7 +284,7 @@ void asCArray::AllocateNoConstruct(size_t numElements, bool keepData) T *tmp = 0; if( numElements ) { - if( sizeof(T)*numElements <= 8 ) + if( sizeof(T)*numElements <= sizeof(buf) ) // Use the internal buffer tmp = reinterpret_cast(buf); else @@ -333,13 +333,13 @@ void asCArray::AllocateNoConstruct(size_t numElements, bool keepData) } template -size_t asCArray::GetCapacity() const +asUINT asCArray::GetCapacity() const { return maxLength; } template -bool asCArray::SetLength(size_t numElements) +bool asCArray::SetLength(asUINT numElements) { if( numElements > maxLength ) { @@ -356,7 +356,7 @@ bool asCArray::SetLength(size_t numElements) } template -bool asCArray::SetLengthNoConstruct(size_t numElements) +bool asCArray::SetLengthNoConstruct(asUINT numElements) { if( numElements > maxLength ) { @@ -373,7 +373,7 @@ bool asCArray::SetLengthNoConstruct(size_t numElements) } template -void asCArray::Copy(const T *data, size_t count) +void asCArray::Copy(const T *data, asUINT count) { if( maxLength < count ) { @@ -385,7 +385,7 @@ void asCArray::Copy(const T *data, size_t count) } } - for( size_t n = 0; n < count; n++ ) + for( asUINT n = 0; n < count; n++ ) array[n] = data[n]; length = count; @@ -403,8 +403,8 @@ template void asCArray::SwapWith(asCArray &other) { T *tmpArray = array; - size_t tmpLength = length; - size_t tmpMaxLength = maxLength; + asUINT tmpLength = length; + asUINT tmpMaxLength = maxLength; char tmpBuf[sizeof(buf)]; memcpy(tmpBuf, buf, sizeof(buf)); @@ -430,7 +430,7 @@ bool asCArray::operator ==(const asCArray &other) const { if( length != other.length ) return false; - for( size_t n = 0; n < length; n++ ) + for( asUINT n = 0; n < length; n++ ) if( array[n] != other.array[n] ) return false; @@ -443,16 +443,28 @@ bool asCArray::operator !=(const asCArray &other) const return !(*this == other); } + +// Returns false if the concatenation wasn't successful due to out of memory template -void asCArray::Concatenate(const asCArray &other) +bool asCArray::Concatenate(const asCArray &other) { if( maxLength < length + other.length ) + { Allocate(length + other.length, true); + if( maxLength < length + other.length ) + { + // Out of memory + return false; + } + } - for( size_t n = 0; n < other.length; n++ ) + for( asUINT n = 0; n < other.length; n++ ) array[length+n] = other.array[n]; length += other.length; + + // Success + return true; } template @@ -471,18 +483,18 @@ bool asCArray::Exists(const T &e) const template int asCArray::IndexOf(const T &e) const { - for( size_t n = 0; n < length; n++ ) + for( asUINT n = 0; n < length; n++ ) if( array[n] == e ) return static_cast(n); return -1; } template -void asCArray::RemoveIndex(size_t index) +void asCArray::RemoveIndex(asUINT index) { if( index < length ) { - for( size_t n = index; n < length-1; n++ ) + for( asUINT n = index; n < length-1; n++ ) array[n] = array[n+1]; PopLast(); @@ -492,7 +504,7 @@ void asCArray::RemoveIndex(size_t index) template void asCArray::RemoveValue(const T &e) { - for( size_t n = 0; n < length; n++ ) + for( asUINT n = 0; n < length; n++ ) { if( array[n] == e ) { @@ -503,7 +515,7 @@ void asCArray::RemoveValue(const T &e) } template -void asCArray::RemoveIndexUnordered(size_t index) +void asCArray::RemoveIndexUnordered(asUINT index) { if( index == length - 1 ) PopLast(); diff --git a/lib/angelscript/source/as_atomic.cpp b/lib/angelscript/source/as_atomic.cpp index 7b635f806..e110b3fc9 100644 --- a/lib/angelscript/source/as_atomic.cpp +++ b/lib/angelscript/source/as_atomic.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -45,21 +45,37 @@ asCAtomic::asCAtomic() asDWORD asCAtomic::get() const { + // A very high ref count is highly unlikely. It most likely a problem with + // memory that has been overwritten or is being accessed after it was deleted. + asASSERT(value < 1000000); + return value; } void asCAtomic::set(asDWORD val) { + // A very high ref count is highly unlikely. It most likely a problem with + // memory that has been overwritten or is being accessed after it was deleted. + asASSERT(value < 1000000); + value = val; } asDWORD asCAtomic::atomicInc() { + // A very high ref count is highly unlikely. It most likely a problem with + // memory that has been overwritten or is being accessed after it was deleted. + asASSERT(value < 1000000); + return asAtomicInc((int&)value); } asDWORD asCAtomic::atomicDec() { + // A very high ref count is highly unlikely. It most likely a problem with + // memory that has been overwritten or is being accessed after it was deleted. + asASSERT(value < 1000000); + return asAtomicDec((int&)value); } diff --git a/lib/angelscript/source/as_builder.cpp b/lib/angelscript/source/as_builder.cpp index 5c1cd7ca7..a7c630411 100644 --- a/lib/angelscript/source/as_builder.cpp +++ b/lib/angelscript/source/as_builder.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -55,8 +55,8 @@ BEGIN_AS_NAMESPACE template<> void asCSymbolTable::GetKey(const sGlobalVariableDescription *entry, asSNameSpaceNamePair &key) const { - asSNameSpace *ns = entry->property->nameSpace; - asCString name = entry->property->name; + asSNameSpace *ns = entry->ns; + asCString name = entry->name; key = asSNameSpaceNamePair(ns, name); } @@ -80,10 +80,10 @@ private: #endif -asCBuilder::asCBuilder(asCScriptEngine *engine, asCModule *module) +asCBuilder::asCBuilder(asCScriptEngine *_engine, asCModule *_module) { - this->engine = engine; - this->module = module; + this->engine = _engine; + this->module = _module; silent = false; } @@ -98,9 +98,7 @@ asCBuilder::~asCBuilder() if( functions[n] ) { if( functions[n]->node ) - { functions[n]->node->Destroy(engine); - } asDELETE(functions[n],sFunctionDescription); } @@ -125,9 +123,7 @@ asCBuilder::~asCBuilder() for( n = 0; n < scripts.GetLength(); n++ ) { if( scripts[n] ) - { asDELETE(scripts[n],asCScriptCode); - } scripts[n] = 0; } @@ -138,9 +134,7 @@ asCBuilder::~asCBuilder() if( classDeclarations[n] ) { if( classDeclarations[n]->node ) - { classDeclarations[n]->node->Destroy(engine); - } asDELETE(classDeclarations[n],sClassDeclaration); classDeclarations[n] = 0; @@ -152,9 +146,7 @@ asCBuilder::~asCBuilder() if( interfaceDeclarations[n] ) { if( interfaceDeclarations[n]->node ) - { interfaceDeclarations[n]->node->Destroy(engine); - } asDELETE(interfaceDeclarations[n],sClassDeclaration); interfaceDeclarations[n] = 0; @@ -166,9 +158,7 @@ asCBuilder::~asCBuilder() if( namedTypeDeclarations[n] ) { if( namedTypeDeclarations[n]->node ) - { namedTypeDeclarations[n]->node->Destroy(engine); - } asDELETE(namedTypeDeclarations[n],sClassDeclaration); namedTypeDeclarations[n] = 0; @@ -206,7 +196,7 @@ void asCBuilder::Reset() { numErrors = 0; numWarnings = 0; - preMessage.isSet = false; + engine->preMessage.isSet = false; #ifndef AS_NO_COMPILER // Clear the cache of known types @@ -236,15 +226,83 @@ int asCBuilder::AddCode(const char *name, const char *code, int codeLength, int return 0; } +void asCBuilder::EvaluateTemplateInstances(asUINT startIdx, bool keepSilent) +{ + // Backup the original message stream + bool msgCallback = engine->msgCallback; + asSSystemFunctionInterface msgCallbackFunc = engine->msgCallbackFunc; + void *msgCallbackObj = engine->msgCallbackObj; + + // Set the new temporary message stream + asCOutputBuffer outBuffer; + if( keepSilent ) + engine->SetMessageCallback(asMETHOD(asCOutputBuffer, Callback), &outBuffer, asCALL_THISCALL); + + // Evaluate each of the template instances that have been created since the start of the build + // TODO: This is not exactly correct, since another thread may have created template instances in parallel + for( asUINT n = startIdx; n < engine->templateInstanceTypes.GetLength(); n++ ) + { + bool dontGarbageCollect = false; + asCObjectType *tmpl = engine->templateInstanceTypes[n]; + asCScriptFunction *callback = engine->scriptFunctions[tmpl->beh.templateCallback]; + if( callback && !engine->CallGlobalFunctionRetBool(tmpl, &dontGarbageCollect, callback->sysFuncIntf, callback) ) + { + asCString sub = tmpl->templateSubTypes[0].Format(engine->nameSpaces[0]); + for( asUINT n = 1; n < tmpl->templateSubTypes.GetLength(); n++ ) + { + sub += ","; + sub += tmpl->templateSubTypes[n].Format(engine->nameSpaces[0]); + } + asCString str; + str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, tmpl->name.AddressOf(), sub.AddressOf()); + WriteError(tmpl->scriptSectionIdx >= 0 ? engine->scriptSectionNames[tmpl->scriptSectionIdx]->AddressOf() : "", str, tmpl->declaredAt&0xFFFFF, (tmpl->declaredAt>>20)&0xFFF); + } + else + { + // If the callback said this template instance won't be garbage collected then remove the flag + if( dontGarbageCollect ) + tmpl->flags &= ~asOBJ_GC; + } + } + + // Restore message callback + if( keepSilent ) + { + engine->msgCallback = msgCallback; + engine->msgCallbackFunc = msgCallbackFunc; + engine->msgCallbackObj = msgCallbackObj; + } +} + int asCBuilder::Build() { Reset(); + // The template callbacks must only be called after the subtypes have a known structure, + // otherwise the callback may think it is not possible to create the template instance, + // even though it is. + // TODO: This flag shouldn't be set globally in the engine, as it would mean that another + // thread requesting a template instance in parallel to the compilation wouldn't + // evaluate the template instance. + engine->deferValidationOfTemplateTypes = true; + asUINT numTempl = (asUINT)engine->templateInstanceTypes.GetLength(); + ParseScripts(); + // Compile the types first CompileInterfaces(); - CompileClasses(); + CompileClasses(numTempl); + + // Evaluate the template instances one last time, this time with error messages, as we know + // all classes have been fully built and it is known which ones will need garbage collection. + EvaluateTemplateInstances(numTempl, false); + engine->deferValidationOfTemplateTypes = false; + + // Then the global variables. Here the variables declared with auto + // will be resolved, so they can be accessed properly in the functions CompileGlobalVariables(); + + // Finally the global functions and class methods CompileFunctions(); // TODO: Attempt to reorder the initialization of global variables so that @@ -341,6 +399,64 @@ int asCBuilder::ValidateDefaultArgs(asCScriptCode *script, asCScriptNode *node, } #ifndef AS_NO_COMPILER +// This function will verify if the newly created function will conflict another overload due to having +// identical function arguments that are not default args, e.g: foo(int) and foo(int, int=0) +int asCBuilder::CheckForConflictsDueToDefaultArgs(asCScriptCode *script, asCScriptNode *node, asCScriptFunction *func, asCObjectType *objType) +{ + // TODO: Implement for global functions too + if( func->objectType == 0 || objType == 0 ) return 0; + + asCArray funcs; + GetObjectMethodDescriptions(func->name.AddressOf(), objType, funcs, false); + for( asUINT n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *func2 = engine->scriptFunctions[funcs[n]]; + if( func == func2 ) + continue; + + if( func->IsReadOnly() != func2->IsReadOnly() ) + continue; + + bool match = true; + asUINT p = 0; + for( ; p < func->parameterTypes.GetLength() && p < func2->parameterTypes.GetLength(); p++ ) + { + // Only verify until the first argument with default args + if( (func->defaultArgs.GetLength() > p && func->defaultArgs[p]) || + (func2->defaultArgs.GetLength() > p && func2->defaultArgs[p]) ) + break; + + if( func->parameterTypes[p] != func2->parameterTypes[p] || + func->inOutFlags[p] != func2->inOutFlags[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( !((p >= func->parameterTypes.GetLength() && p < func2->defaultArgs.GetLength() && func2->defaultArgs[p]) || + (p >= func2->parameterTypes.GetLength() && p < func->defaultArgs.GetLength() && func->defaultArgs[p])) ) + { + // The argument lists match for the full length of the shorter, but the next + // argument on the longer does not have a default arg so there is no conflict + match = false; + } + } + + if( match ) + { + WriteWarning(TXT_OVERLOAD_CONFLICTS_DUE_TO_DEFAULT_ARGS, script, node); + WriteInfo(func->GetDeclaration(), script, node); + WriteInfo(func2->GetDeclaration(), script, node); + break; + } + } + + return 0; +} + int asCBuilder::CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asCScriptFunction **outFunc) { asASSERT(outFunc != 0); @@ -378,13 +494,12 @@ int asCBuilder::CompileFunction(const char *sectionName, const char *code, int l node = node->firstChild; // Create the function - bool isConstructor, isDestructor, isPrivate, isFinal, isOverride, isShared; + bool isConstructor, isDestructor, isPrivate, isProtected, isFinal, isOverride, isShared; asCScriptFunction *func = asNEW(asCScriptFunction)(engine, compileFlags & asCOMP_ADD_TO_MODULE ? module : 0, asFUNC_SCRIPT); if( func == 0 ) return asOUT_OF_MEMORY; - asCArray parameterNames; - GetParsedFunctionDetails(node, scripts[0], 0, func->name, func->returnType, parameterNames, func->parameterTypes, func->inOutFlags, func->defaultArgs, func->isReadOnly, isConstructor, isDestructor, isPrivate, isFinal, isOverride, isShared, module->defaultNamespace); + GetParsedFunctionDetails(node, scripts[0], 0, func->name, func->returnType, func->parameterNames, func->parameterTypes, func->inOutFlags, func->defaultArgs, func->isReadOnly, isConstructor, isDestructor, isPrivate, isProtected, isFinal, isOverride, isShared, module->defaultNamespace); func->id = engine->GetNextScriptFunctionId(); func->scriptData->scriptSectionIdx = engine->GetScriptSectionNameIndex(sectionName ? sectionName : ""); int row, col; @@ -396,7 +511,7 @@ int asCBuilder::CompileFunction(const char *sectionName, const char *code, int l int r = ValidateDefaultArgs(script, node, func); if( r < 0 ) { - func->Release(); + func->ReleaseInternal(); return asERROR; } @@ -406,23 +521,23 @@ int asCBuilder::CompileFunction(const char *sectionName, const char *code, int l int r = CheckNameConflict(func->name.AddressOf(), node, scripts[0], module->defaultNamespace); if( r < 0 ) { - func->Orphan(module); + func->ReleaseInternal(); return asERROR; } module->globalFunctions.Put(func); - func->AddRef(); + module->AddScriptFunction(func); } else - engine->SetScriptFunction(func); + engine->AddScriptFunction(func); // Fill in the function info for the builder too node->DisconnectParent(); sFunctionDescription *funcDesc = asNEW(sFunctionDescription); if( funcDesc == 0 ) { - func->Release(); + func->ReleaseInternal(); return asOUT_OF_MEMORY; } @@ -431,11 +546,11 @@ int asCBuilder::CompileFunction(const char *sectionName, const char *code, int l funcDesc->node = node; funcDesc->name = func->name; funcDesc->funcId = func->id; - funcDesc->paramNames = parameterNames; + funcDesc->paramNames = func->parameterNames; funcDesc->isExistingShared = false; asCCompiler compiler(engine); - compiler.CompileFunction(this, functions[0]->script, parameterNames, functions[0]->node, func, 0); + compiler.CompileFunction(this, functions[0]->script, func->parameterNames, functions[0]->node, func, 0); if( numWarnings > 0 && engine->ep.compilerWarnings == 2 ) WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0); @@ -447,11 +562,10 @@ int asCBuilder::CompileFunction(const char *sectionName, const char *code, int l { module->globalFunctions.Erase(module->globalFunctions.GetIndex(func)); module->scriptFunctions.RemoveValue(func); - func->Release(); - func->Orphan(module); + func->ReleaseInternal(); } - func->Release(); + func->ReleaseInternal(); return asERROR; } @@ -493,9 +607,7 @@ void asCBuilder::ParseScripts() // Register the complete function definitions for( n = 0; n < funcDefs.GetLength(); n++ ) - { CompleteFuncDef(funcDefs[n]); - } // Register script methods found in the interfaces for( n = 0; n < interfaceDeclarations.GetLength(); n++ ) @@ -565,20 +677,20 @@ void asCBuilder::ParseScripts() // As the class has another constructor we shouldn't provide the default constructor if( decl->objType->beh.construct ) { - engine->scriptFunctions[decl->objType->beh.construct]->Release(); + engine->scriptFunctions[decl->objType->beh.construct]->ReleaseInternal(); decl->objType->beh.construct = 0; decl->objType->beh.constructors.RemoveIndex(0); } if( decl->objType->beh.factory ) { - engine->scriptFunctions[decl->objType->beh.factory]->Release(); + engine->scriptFunctions[decl->objType->beh.factory]->ReleaseInternal(); decl->objType->beh.factory = 0; decl->objType->beh.factories.RemoveIndex(0); } // Only remove the opAssign method if the script hasn't provided one if( decl->objType->beh.copy == engine->scriptTypeBehaviours.beh.copy ) { - engine->scriptFunctions[decl->objType->beh.copy]->Release(); + engine->scriptFunctions[decl->objType->beh.copy]->ReleaseInternal(); decl->objType->beh.copy = 0; } } @@ -745,7 +857,7 @@ void asCBuilder::CompileFunctions() // When compiling a constructor need to pass the class declaration for member initializations compiler.CompileFunction(this, current->script, current->paramNames, current->node, func, classDecl); - preMessage.isSet = false; + engine->preMessage.isSet = false; } else if( current->objType && current->name == current->objType->name ) { @@ -763,7 +875,7 @@ void asCBuilder::CompileFunctions() // automatically if not implemented by the user. compiler.CompileDefaultConstructor(this, current->script, node, func, classDecl); - preMessage.isSet = false; + engine->preMessage.isSet = false; } else { @@ -815,7 +927,7 @@ int asCBuilder::ParseTemplateDecl(const char *decl, asCString *name, asCArrayfirstChild; name->Assign(&decl[node->tokenPos], node->tokenLength); - while( (node = node->next) ) + while( (node = node->next) != 0 ) { asCString subtypeName; subtypeName.Assign(&decl[node->tokenPos], node->tokenLength); @@ -864,8 +976,8 @@ int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &nam name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength); // Validate that the type really can be a registered property - // We cannot use CanBeInstanciated, as it is allowed to register - // properties of type that cannot otherwise be instanciated + // We cannot use CanBeInstantiated, as it is allowed to register + // properties of type that cannot otherwise be instantiated if( type.GetFuncDef() && !type.IsObjectHandle() ) { // Function definitions must always be handles @@ -912,32 +1024,20 @@ asCObjectProperty *asCBuilder::GetObjectProperty(asCDataType &obj, const char *p } #endif -asCGlobalProperty *asCBuilder::GetGlobalProperty(const char *prop, asSNameSpace *ns, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue, bool *isAppProp) +bool asCBuilder::DoesGlobalPropertyExist(const char *prop, asSNameSpace *ns, asCGlobalProperty **outProp, sGlobalVariableDescription **outDesc, bool *isAppProp) { - if( isCompiled ) *isCompiled = true; - if( isPureConstant ) *isPureConstant = false; - if( isAppProp ) *isAppProp = false; + if( outProp ) *outProp = 0; + if( outDesc ) *outDesc = 0; + if( isAppProp ) *isAppProp = false; // Check application registered properties asCString name(prop); asCGlobalProperty *globProp = engine->registeredGlobalProps.GetFirst(ns, name); if( globProp ) { - if( module ) - { - // Determine if the module has access to the property - if( module->accessMask & globProp->accessMask ) - { - if( isAppProp ) *isAppProp = true; - return globProp; - } - } - else - { - // We're not compiling a module right now, so it must be a registered global property - if( isAppProp ) *isAppProp = true; - return globProp; - } + if( isAppProp ) *isAppProp = true; + if( outProp ) *outProp = globProp; + return true; } #ifndef AS_NO_COMPILER @@ -945,18 +1045,55 @@ asCGlobalProperty *asCBuilder::GetGlobalProperty(const char *prop, asSNameSpace sGlobalVariableDescription* desc = globVariables.GetFirst(ns, prop); if( desc && !desc->isEnumValue ) { - if( isCompiled ) *isCompiled = desc->isCompiled; - if( isPureConstant ) *isPureConstant = desc->isPureConstant; - if( constantValue ) *constantValue = desc->constantValue; - return desc->property; + if( outProp ) *outProp = desc->property; + if( outDesc ) *outDesc = desc; + return true; } -#else - UNUSED_VAR(constantValue); #endif // Check previously compiled global variables if( module ) - return module->scriptGlobals.GetFirst(ns, prop); + { + globProp = module->scriptGlobals.GetFirst(ns, prop); + if( globProp ) + { + if( outProp ) *outProp = globProp; + return true; + } + } + + return false; +} + +asCGlobalProperty *asCBuilder::GetGlobalProperty(const char *prop, asSNameSpace *ns, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue, bool *isAppProp) +{ + if( isCompiled ) *isCompiled = true; + if( isPureConstant ) *isPureConstant = false; + if( isAppProp ) *isAppProp = false; + if( constantValue ) *constantValue = 0; + + asCGlobalProperty *globProp = 0; + sGlobalVariableDescription *globDesc = 0; + if( DoesGlobalPropertyExist(prop, ns, &globProp, &globDesc, isAppProp) ) + { +#ifndef AS_NO_COMPILER + if( globDesc ) + { + // The property was declared in this build call, check if it has been compiled successfully already + if( isCompiled ) *isCompiled = globDesc->isCompiled; + if( isPureConstant ) *isPureConstant = globDesc->isPureConstant; + if( constantValue ) *constantValue = globDesc->constantValue; + } + else +#endif + if( isAppProp ) + { + // Don't return the property if the module doesn't have access to it + if( !(module->accessMask & globProp->accessMask) ) + globProp = 0; + } + return globProp; + } return 0; } @@ -982,7 +1119,10 @@ int asCBuilder::ParseFunctionDeclaration(asCObjectType *objType, const char *dec // Determine scope asCScriptNode *n = node->firstChild->next->next; asCString scope = GetScopeFromNode(n, &source, &n); - func->nameSpace = engine->FindNameSpace(scope.AddressOf()); + if( scope == "::" ) + func->nameSpace = engine->nameSpaces[0]; + else + func->nameSpace = engine->FindNameSpace(scope.AddressOf()); if( func->nameSpace == 0 ) return asINVALID_DECLARATION; @@ -1024,11 +1164,13 @@ int asCBuilder::ParseFunctionDeclaration(asCObjectType *objType, const char *dec // Preallocate memory func->parameterTypes.Allocate(paramCount, false); + func->parameterNames.SetLength(paramCount); func->inOutFlags.Allocate(paramCount, false); func->defaultArgs.Allocate(paramCount, false); if( paramAutoHandles ) paramAutoHandles->Allocate(paramCount, false); n = paramList->firstChild; + asUINT index = 0; while( n ) { asETypeModifiers inOutFlags; @@ -1063,8 +1205,12 @@ int asCBuilder::ParseFunctionDeclaration(asCObjectType *objType, const char *dec // Move to next parameter n = n->next->next; - if( n && n->nodeType == snIdentifier ) + if( n && n->nodeType == snIdentifier ) + { + func->parameterNames[index] = asCString(&source.code[n->tokenPos], n->tokenLength); n = n->next; + } + ++index; if( n && n->nodeType == snExpression ) { @@ -1214,10 +1360,8 @@ int asCBuilder::CheckNameConflict(const char *name, asCScriptNode *node, asCScri return -1; } - // TODO: Must verify global properties in all config groups, whether the module has access or not // Check against global properties - asCGlobalProperty *prop = GetGlobalProperty(name, ns, 0, 0, 0, 0); - if( prop ) + if( DoesGlobalPropertyExist(name, ns) ) { if( code ) { @@ -1314,6 +1458,10 @@ sMixinClass *asCBuilder::GetMixinClass(const char *name, asSNameSpace *ns) int asCBuilder::RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns) { + // TODO: 2.30.0: redesign: Allow funcdefs to be explicitly declared as 'shared'. In this case + // an error should be given if any of the arguments/return type is not + // shared. + // Find the name asASSERT( node->firstChild->nodeType == snDataType ); asCScriptNode *n = node->firstChild->next->next; @@ -1355,11 +1503,11 @@ int asCBuilder::RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNam void asCBuilder::CompleteFuncDef(sFuncDef *funcDef) { - asCArray parameterNames; asCArray defaultArgs; bool isConstMethod; bool isConstructor; bool isDestructor; + bool isProtected; bool isPrivate; bool isOverride; bool isFinal; @@ -1368,7 +1516,7 @@ void asCBuilder::CompleteFuncDef(sFuncDef *funcDef) asCScriptFunction *func = module->funcDefs[funcDef->idx]; asASSERT( func ); - GetParsedFunctionDetails(funcDef->node, funcDef->script, 0, funcDef->name, func->returnType, parameterNames, func->parameterTypes, func->inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isOverride, isFinal, isShared, func->nameSpace); + GetParsedFunctionDetails(funcDef->node, funcDef->script, 0, funcDef->name, func->returnType, func->parameterNames, func->parameterTypes, func->inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared, func->nameSpace); // There should not be any defaultArgs, but if there are any we need to delete them to avoid leaks for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) @@ -1390,14 +1538,11 @@ void asCBuilder::CompleteFuncDef(sFuncDef *funcDef) // Replace our funcdef for the existing one funcDef->idx = f2->id; module->funcDefs[module->funcDefs.IndexOf(func)] = f2; - f2->AddRef(); + f2->AddRefInternal(); engine->funcDefs.RemoveValue(func); - func->Release(); - - // funcdefs aren't destroyed when the refCount reaches zero so we need to manually delete them - asDELETE(func, asCScriptFunction); + func->ReleaseInternal(); break; } } @@ -1412,14 +1557,16 @@ int asCBuilder::RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file, asSN // What data type is it? asCDataType type = CreateDataTypeFromNode(node->firstChild, file, ns); - if( !type.CanBeInstanciated() ) + if( !type.CanBeInstantiated() ) { asCString str; - // TODO: Change to "'type' cannot be declared as variable" - str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf()); - - int r, c; - file->ConvertPosToRowCol(node->tokenPos, &r, &c); + if( type.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, type.Format(ns).AddressOf()); + else if( type.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, type.Format(ns).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format(ns).AddressOf()); WriteError(str, file, node); } @@ -1445,12 +1592,14 @@ int asCBuilder::RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file, asSN gvar->isCompiled = false; gvar->datatype = type; gvar->isEnumValue = false; + gvar->ns = ns; // TODO: Give error message if wrong asASSERT(!gvar->datatype.IsReference()); - gvar->property = module->AllocateGlobalProperty(name.AddressOf(), gvar->datatype, ns); - gvar->index = gvar->property->id; + // Allocation is done when the variable is compiled, to allow for autos + gvar->property = 0; + gvar->index = 0; globVariables.Put(gvar); @@ -1532,24 +1681,58 @@ int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file, asSNameS asCScriptNode *n = node->firstChild; bool isFinal = false; bool isShared = false; + bool isAbstract = false; - if( n->tokenType == ttIdentifier && file->TokenEquals(n->tokenPos, n->tokenLength, FINAL_TOKEN) ) + // Check the class modifiers + while( n->tokenType == ttIdentifier ) { - isFinal = true; - n = n->next; - } - - if( n->tokenType == ttIdentifier && file->TokenEquals(n->tokenPos, n->tokenLength, SHARED_TOKEN) ) - { - isShared = true; - n = n->next; - - // Check for final again - if( n->tokenType == ttIdentifier && file->TokenEquals(n->tokenPos, n->tokenLength, FINAL_TOKEN) ) + if( file->TokenEquals(n->tokenPos, n->tokenLength, FINAL_TOKEN) ) { - isFinal = true; - n = n->next; + if( isAbstract ) + WriteError(TXT_CLASS_CANT_BE_FINAL_AND_ABSTRACT, file, n); + else + { + if( isFinal ) + { + asCString msg; + msg.Format(TXT_ATTR_s_INFORMED_MULTIPLE_TIMES, asCString(&file->code[n->tokenPos], n->tokenLength).AddressOf()); + WriteWarning(msg, file, n); + } + isFinal = true; + } } + else if( file->TokenEquals(n->tokenPos, n->tokenLength, SHARED_TOKEN) ) + { + if( isShared ) + { + asCString msg; + msg.Format(TXT_ATTR_s_INFORMED_MULTIPLE_TIMES, asCString(&file->code[n->tokenPos], n->tokenLength).AddressOf()); + WriteWarning(msg, file, n); + } + isShared = true; + } + else if( file->TokenEquals(n->tokenPos, n->tokenLength, ABSTRACT_TOKEN) ) + { + if( isFinal ) + WriteError(TXT_CLASS_CANT_BE_FINAL_AND_ABSTRACT, file, n); + else + { + if( isAbstract ) + { + asCString msg; + msg.Format(TXT_ATTR_s_INFORMED_MULTIPLE_TIMES, asCString(&file->code[n->tokenPos], n->tokenLength).AddressOf()); + WriteWarning(msg, file, n); + } + isAbstract = true; + } + } + else + { + // This is the name of the class + break; + } + + n = n->next; } asCString name(&file->code[n->tokenPos], n->tokenLength); @@ -1576,9 +1759,9 @@ int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file, asSNameS // creating a new one. if( isShared ) { - for( asUINT n = 0; n < engine->classTypes.GetLength(); n++ ) + for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ ) { - asCObjectType *st = engine->classTypes[n]; + asCObjectType *st = engine->sharedScriptTypes[n]; if( st && st->IsShared() && st->name == name && @@ -1589,7 +1772,7 @@ int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file, asSNameS decl->isExistingShared = true; decl->objType = st; module->classTypes.PushLast(st); - st->AddRef(); + st->AddRefInternal(); return 0; } } @@ -1605,7 +1788,7 @@ int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file, asSNameS // is known, can the flag be cleared for those objects that truly cannot // form circular references. This is important because a template // callback may be called with a script class before the compilation - // complete, and until it is known, the callback must assume the class + // completes, and until it is known, the callback must assume the class // is garbage collected. st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT | asOBJ_GC; @@ -1615,6 +1798,9 @@ int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file, asSNameS if( isFinal ) st->flags |= asOBJ_NOINHERIT; + if( isAbstract ) + st->flags |= asOBJ_ABSTRACT; + if( node->tokenType == ttHandle ) st->flags |= asOBJ_IMPLICIT_HANDLE; @@ -1623,28 +1809,29 @@ int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file, asSNameS st->nameSpace = ns; st->module = module; module->classTypes.PushLast(st); - engine->classTypes.PushLast(st); - st->AddRef(); + if( isShared ) + { + engine->sharedScriptTypes.PushLast(st); + st->AddRefInternal(); + } decl->objType = st; // Use the default script class behaviours st->beh = engine->scriptTypeBehaviours.beh; // TODO: Move this to asCObjectType so that the asCRestore can reuse it - engine->scriptFunctions[st->beh.addref]->AddRef(); - engine->scriptFunctions[st->beh.release]->AddRef(); - engine->scriptFunctions[st->beh.gcEnumReferences]->AddRef(); - engine->scriptFunctions[st->beh.gcGetFlag]->AddRef(); - engine->scriptFunctions[st->beh.gcGetRefCount]->AddRef(); - engine->scriptFunctions[st->beh.gcReleaseAllReferences]->AddRef(); - engine->scriptFunctions[st->beh.gcSetFlag]->AddRef(); - engine->scriptFunctions[st->beh.copy]->AddRef(); - engine->scriptFunctions[st->beh.factory]->AddRef(); - engine->scriptFunctions[st->beh.construct]->AddRef(); + engine->scriptFunctions[st->beh.addref]->AddRefInternal(); + engine->scriptFunctions[st->beh.release]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcEnumReferences]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcGetFlag]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcGetRefCount]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcReleaseAllReferences]->AddRefInternal(); + engine->scriptFunctions[st->beh.gcSetFlag]->AddRefInternal(); + engine->scriptFunctions[st->beh.copy]->AddRefInternal(); + engine->scriptFunctions[st->beh.factory]->AddRefInternal(); + engine->scriptFunctions[st->beh.construct]->AddRefInternal(); // TODO: weak: Should not do this if the class has been declared with noweak - engine->scriptFunctions[st->beh.getWeakRefFlag]->AddRef(); - for( asUINT i = 1; i < st->beh.operators.GetLength(); i += 2 ) - engine->scriptFunctions[st->beh.operators[i]]->AddRef(); + engine->scriptFunctions[st->beh.getWeakRefFlag]->AddRefInternal(); return 0; } @@ -1685,9 +1872,9 @@ int asCBuilder::RegisterInterface(asCScriptNode *node, asCScriptCode *file, asSN // creating a new one. if( isShared ) { - for( asUINT n = 0; n < engine->classTypes.GetLength(); n++ ) + for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ ) { - asCObjectType *st = engine->classTypes[n]; + asCObjectType *st = engine->sharedScriptTypes[n]; if( st && st->IsShared() && st->name == name && @@ -1698,7 +1885,7 @@ int asCBuilder::RegisterInterface(asCScriptNode *node, asCScriptCode *file, asSN decl->isExistingShared = true; decl->objType = st; module->classTypes.PushLast(st); - st->AddRef(); + st->AddRefInternal(); return 0; } } @@ -1714,20 +1901,24 @@ int asCBuilder::RegisterInterface(asCScriptNode *node, asCScriptCode *file, asSN if( isShared ) st->flags |= asOBJ_SHARED; - st->size = 0; // Cannot be instanciated + st->size = 0; // Cannot be instantiated st->name = name; st->nameSpace = ns; + st->module = module; module->classTypes.PushLast(st); - engine->classTypes.PushLast(st); - st->AddRef(); + if( isShared ) + { + engine->sharedScriptTypes.PushLast(st); + st->AddRefInternal(); + } decl->objType = st; // Use the default script class behaviours st->beh.construct = 0; st->beh.addref = engine->scriptTypeBehaviours.beh.addref; - engine->scriptFunctions[st->beh.addref]->AddRef(); + engine->scriptFunctions[st->beh.addref]->AddRefInternal(); st->beh.release = engine->scriptTypeBehaviours.beh.release; - engine->scriptFunctions[st->beh.release]->AddRef(); + engine->scriptFunctions[st->beh.release]->AddRefInternal(); st->beh.copy = 0; return 0; @@ -1793,7 +1984,7 @@ void asCBuilder::CompileGlobalVariables() { int r, c; gvar->script->ConvertPosToRowCol(gvar->declaredAtNode->tokenPos, &r, &c); - asCString str = gvar->datatype.Format(); + asCString str = gvar->datatype.Format(gvar->ns); str += " " + gvar->name; str.Format(TXT_COMPILING_s, str.AddressOf()); WriteInfo(gvar->script->name, str, r, c, true); @@ -1841,7 +2032,7 @@ void asCBuilder::CompileGlobalVariables() int row, col; gvar->script->ConvertPosToRowCol(gvar->declaredAtNode->tokenPos, &row, &col); - asCString str = gvar->datatype.Format(); + asCString str = gvar->datatype.Format(gvar->ns); str += " " + gvar->name; str.Format(TXT_COMPILING_s, str.AddressOf()); WriteInfo(gvar->script->name, str, row, col, true); @@ -1875,7 +2066,7 @@ void asCBuilder::CompileGlobalVariables() } // Set the namespace that should be used for this function - initFunc->nameSpace = gvar->property->nameSpace; + initFunc->nameSpace = gvar->ns; asCCompiler comp(engine); int r = comp.CompileGlobalVariable(this, gvar->script, gvar->initializationNode, gvar, initFunc); @@ -1913,7 +2104,7 @@ void asCBuilder::CompileGlobalVariables() { // Create the init function for this variable initFunc->id = engine->GetNextScriptFunctionId(); - engine->SetScriptFunction(initFunc); + engine->AddScriptFunction(initFunc); // Finalize the init function for this variable initFunc->returnType = asCDataType::CreatePrimitive(ttVoid, false); @@ -1927,7 +2118,7 @@ void asCBuilder::CompileGlobalVariables() gvar->property->SetInitFunc(initFunc); - initFunc->Release(); + initFunc->ReleaseInternal(); initFunc = 0; } else if( initFunc ) @@ -1966,7 +2157,7 @@ void asCBuilder::CompileGlobalVariables() accumWarnings += numWarnings; } - preMessage.isSet = false; + engine->preMessage.isSet = false; } if( !compileSucceeded ) @@ -2037,7 +2228,8 @@ void asCBuilder::CompileGlobalVariables() asDELETE(gvar, sGlobalVariableDescription); } - it++; + else + it++; } } @@ -2082,7 +2274,7 @@ void asCBuilder::AddInterfaceFromMixinToClass(sClassDeclaration *decl, asCScript asCObjectType *objType = GetObjectType(name.AddressOf(), ns); // Check that the object type is an interface - if( objType && objType->size == 0 && (objType->flags & asOBJ_SCRIPT_OBJECT) ) + if( objType && objType->IsInterface() ) { // Only add the interface if the class doesn't already implement it if( !decl->objType->Implements(objType) ) @@ -2187,12 +2379,12 @@ void asCBuilder::CompileInterfaces() objType = GetObjectType(name.AddressOf(), ns); if( objType ) break; - ns = GetParentNameSpace(ns); + ns = engine->GetParentNameSpace(ns); } // Check that the object type is an interface bool ok = true; - if( objType && objType->size == 0 && (objType->flags & asOBJ_SCRIPT_OBJECT) ) + if( objType && objType->IsInterface() ) { // Check that the implemented interface is shared if the base interface is shared if( intfType->IsShared() && !objType->IsShared() ) @@ -2313,14 +2505,15 @@ void asCBuilder::CompileInterfaces() { // Add the method intfType->methods.PushLast(baseFunc->id); - baseFunc->AddRef(); + baseFunc->AddRefInternal(); } } } } } -void asCBuilder::CompileClasses() +// numTempl is the number of template instances that existed in the engine before the build begun +void asCBuilder::CompileClasses(asUINT numTempl) { asUINT n; asCArray toValidate((int)classDeclarations.GetLength()); @@ -2335,16 +2528,10 @@ void asCBuilder::CompileClasses() bool multipleInheritance = false; asCScriptNode *node = decl->node->firstChild; - if( decl->objType->IsShared() ) + while( file->TokenEquals(node->tokenPos, node->tokenLength, FINAL_TOKEN) || + file->TokenEquals(node->tokenPos, node->tokenLength, SHARED_TOKEN) || + file->TokenEquals(node->tokenPos, node->tokenLength, ABSTRACT_TOKEN) ) { - // Skip the keyword 'shared' - asASSERT(node->tokenType == ttIdentifier); - node = node->next; - } - if( decl->objType->flags & asOBJ_NOINHERIT ) - { - // skip the keyword 'final' - asASSERT(node->tokenType == ttIdentifier); node = node->next; } @@ -2365,6 +2552,7 @@ void asCBuilder::CompileClasses() // Find the object type for the interface asCObjectType *objType = 0; sMixinClass *mixin = 0; + asSNameSpace *origNs = ns; while( ns ) { objType = GetObjectType(name.AddressOf(), ns); @@ -2374,13 +2562,16 @@ void asCBuilder::CompileClasses() if( objType || mixin ) break; - ns = GetParentNameSpace(ns); + ns = engine->GetParentNameSpace(ns); } if( objType == 0 && mixin == 0 ) { asCString str; - str.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, name.AddressOf()); + if( origNs->name == "" ) + str.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_GLOBAL_NS, name.AddressOf()); + else + str.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_NS_s, name.AddressOf(), origNs->name.AddressOf()); WriteError(str, file, node); } else if( mixin ) @@ -2388,7 +2579,7 @@ void asCBuilder::CompileClasses() AddInterfaceFromMixinToClass(decl, node, mixin); } else if( !(objType->flags & asOBJ_SCRIPT_OBJECT) || - objType->flags & asOBJ_NOINHERIT ) + (objType->flags & asOBJ_NOINHERIT) ) { // Either the class is not a script class or interface // or the class has been declared as 'final' @@ -2452,7 +2643,7 @@ void asCBuilder::CompileClasses() { // Set the base class decl->objType->derivedFrom = objType; - objType->AddRef(); + objType->AddRefInternal(); } } } @@ -2507,6 +2698,8 @@ void asCBuilder::CompileClasses() // added to the shared class that wasn't there in the previous // compilation. We do not care if something that is there in the previous // declaration is not included in the new declaration though. + + asASSERT( decl->objType->interfaces.GetLength() == decl->objType->interfaceVFTOffsets.GetLength() ); } // Methods included from mixin classes should take precedence over inherited methods @@ -2529,7 +2722,7 @@ void asCBuilder::CompileClasses() // Copy properties from base class to derived class for( asUINT p = 0; p < baseType->properties.GetLength(); p++ ) { - asCObjectProperty *prop = AddPropertyToClass(decl, baseType->properties[p]->name, baseType->properties[p]->type, baseType->properties[p]->isPrivate); + asCObjectProperty *prop = AddPropertyToClass(decl, baseType->properties[p]->name, baseType->properties[p]->type, baseType->properties[p]->isPrivate, baseType->properties[p]->isProtected, true); // The properties must maintain the same offset asASSERT(prop && prop->byteOffset == baseType->properties[p]->byteOffset); UNUSED_VAR(prop); @@ -2545,28 +2738,52 @@ void asCBuilder::CompileClasses() for( asUINT d = 0; d < decl->objType->methods.GetLength(); d++ ) { derivedFunc = GetFunctionDescription(decl->objType->methods[d]); - if( derivedFunc->name == baseFunc->name && - derivedFunc->IsSignatureExceptNameAndReturnTypeEqual(baseFunc) ) + if( baseFunc->name == "opConv" || baseFunc->name == "opImplConv" || + baseFunc->name == "opCast" || baseFunc->name == "opImplCast" ) { - if( baseFunc->returnType != derivedFunc->returnType ) + // For the opConv and opCast methods, the return type can differ if they are different methods + if( derivedFunc->name == baseFunc->name && + derivedFunc->IsSignatureExceptNameEqual(baseFunc) ) { - asCString msg; - msg.Format(TXT_DERIVED_METHOD_MUST_HAVE_SAME_RETTYPE_s, baseFunc->GetDeclaration()); - WriteError(msg, decl->script, decl->node); - } + if( baseFunc->IsFinal() ) + { + asCString msg; + msg.Format(TXT_METHOD_CANNOT_OVERRIDE_s, baseFunc->GetDeclaration()); + WriteError(msg, decl->script, decl->node); + } - if( baseFunc->IsFinal() ) + // Move the function from the methods array to the virtualFunctionTable + decl->objType->methods.RemoveIndex(d); + decl->objType->virtualFunctionTable.PushLast(derivedFunc); + found = true; + break; + } + } + else + { + if( derivedFunc->name == baseFunc->name && + derivedFunc->IsSignatureExceptNameAndReturnTypeEqual(baseFunc) ) { - asCString msg; - msg.Format(TXT_METHOD_CANNOT_OVERRIDE_s, baseFunc->GetDeclaration()); - WriteError(msg, decl->script, decl->node); - } + if( baseFunc->returnType != derivedFunc->returnType ) + { + asCString msg; + msg.Format(TXT_DERIVED_METHOD_MUST_HAVE_SAME_RETTYPE_s, baseFunc->GetDeclaration()); + WriteError(msg, decl->script, decl->node); + } - // Move the function from the methods array to the virtualFunctionTable - decl->objType->methods.RemoveIndex(d); - decl->objType->virtualFunctionTable.PushLast(derivedFunc); - found = true; - break; + if( baseFunc->IsFinal() ) + { + asCString msg; + msg.Format(TXT_METHOD_CANNOT_OVERRIDE_s, baseFunc->GetDeclaration()); + WriteError(msg, decl->script, decl->node); + } + + // Move the function from the methods array to the virtualFunctionTable + decl->objType->methods.RemoveIndex(d); + decl->objType->virtualFunctionTable.PushLast(derivedFunc); + found = true; + break; + } } } @@ -2574,11 +2791,13 @@ void asCBuilder::CompileClasses() { // Push the base class function on the virtual function table decl->objType->virtualFunctionTable.PushLast(baseType->virtualFunctionTable[m]); - baseType->virtualFunctionTable[m]->AddRef(); + baseType->virtualFunctionTable[m]->AddRefInternal(); + + CheckForConflictsDueToDefaultArgs(decl->script, decl->node, baseType->virtualFunctionTable[m], decl->objType); } decl->objType->methods.PushLast(baseType->methods[m]); - engine->scriptFunctions[baseType->methods[m]]->AddRef(); + engine->scriptFunctions[baseType->methods[m]]->AddRefInternal(); } } @@ -2646,17 +2865,13 @@ void asCBuilder::CompileClasses() // implemented and we error out later in the checks. decl->objType->virtualFunctionTable.PushLast(realFunc); if( realFunc ) - realFunc->AddRef(); + realFunc->AddRefInternal(); } } } // Enumerate each of the declared properties asCScriptNode *node = decl->node->firstChild->next; - if( decl->objType->IsShared() ) - node = node->next; - if( decl->objType->flags & asOBJ_NOINHERIT ) - node = node->next; // Skip list of classes and interfaces while( node && node->nodeType == snIdentifier ) @@ -2668,13 +2883,18 @@ void asCBuilder::CompileClasses() { asCScriptNode *n = node->firstChild; - // Is the property declared as private? - bool isPrivate = false; + // Is the property declared as private or protected? + bool isPrivate = false, isProtected = false; if( n && n->tokenType == ttPrivate ) { isPrivate = true; n = n->next; } + else if( n && n->tokenType == ttProtected ) + { + isProtected = true; + n = n->next; + } // Determine the type of the property asCScriptCode *file = decl->script; @@ -2698,7 +2918,7 @@ void asCBuilder::CompileClasses() if( !decl->isExistingShared ) { CheckNameConflictMember(decl->objType, name.AddressOf(), n, file, true); - AddPropertyToClass(decl, name, dt, isPrivate, file, n); + AddPropertyToClass(decl, name, dt, isPrivate, isProtected, false, file, n); } else { @@ -2708,6 +2928,7 @@ void asCBuilder::CompileClasses() { asCObjectProperty *prop = decl->objType->properties[p]; if( prop->isPrivate == isPrivate && + prop->isProtected == isProtected && prop->name == name && prop->type.IsEqualExceptRef(dt) ) { @@ -2741,6 +2962,8 @@ void asCBuilder::CompileClasses() if( !decl->isExistingShared ) toValidate.PushLast(decl); + + asASSERT( decl->objType->interfaces.GetLength() == decl->objType->interfaceVFTOffsets.GetLength() ); } // TODO: Warn if a method overrides a base method without marking it as 'override'. @@ -2814,14 +3037,21 @@ void asCBuilder::CompileClasses() asCObjectProperty *prop = decl->objType->properties[n]; asCDataType dt = prop->type; + // TODO: Add this check again, once solving the issues commented below + /* if( dt.IsTemplate() ) { + // TODO: This must verify all sub types, not just the first one + // TODO: Just because the subtype is not a handle doesn't mean the template will actually instance the object + // this it shouldn't automatically raise an error for this, e.g. weakref should be legal as member + // of the Object class asCDataType sub = dt; while( sub.IsTemplate() && !sub.IsObjectHandle() ) sub = sub.GetSubType(); dt = sub; } + */ if( dt.IsObject() && !dt.IsObjectHandle() ) { @@ -2882,6 +3112,8 @@ void asCBuilder::CompileClasses() if( numErrors > 0 ) return; // Verify which script classes can really form circular references, and mark only those as garbage collected. + // This must be done in the correct order, so that a class that contains another class isn't needlessly marked + // as garbage collected, just because the contained class was evaluated afterwards. // TODO: runtime optimize: This algorithm can be further improved by checking the types that inherits from // a base class. If the base class is not shared all the classes that derive from it @@ -2892,92 +3124,170 @@ void asCBuilder::CompileClasses() // existing module. However, the applications that want to use that should use a special // build flag to not finalize the module. + asCArray typesToValidate; for( n = 0; n < classDeclarations.GetLength(); n++ ) { + // Existing shared classes won't need evaluating, nor interfaces sClassDeclaration *decl = classDeclarations[n]; - - // Existing shared classes won't be re-evaluated if( decl->isExistingShared ) continue; + if( decl->objType->IsInterface() ) continue; - asCObjectType *ot = decl->objType; + typesToValidate.PushLast(decl->objType); + } + + asUINT numReevaluations = 0; + while( typesToValidate.GetLength() ) + { + if( numReevaluations > typesToValidate.GetLength() ) + { + // No types could be completely evaluated in the last iteration so + // we consider the remaining types in the array as garbage collected + break; + } + + asCObjectType *type = typesToValidate[0]; + typesToValidate.RemoveIndex(0); + + // If the type inherits from another type that is yet to be validated, then reinsert it at the end + if( type->derivedFrom && typesToValidate.Exists(type->derivedFrom) ) + { + typesToValidate.PushLast(type); + numReevaluations++; + continue; + } + + // If the type inherits from a known garbage collected type, then this type must also be garbage collected + if( type->derivedFrom && (type->derivedFrom->flags & asOBJ_GC) ) + { + type->flags |= asOBJ_GC; + continue; + } + + // Evaluate template instances (silently) before verifying each of the classes, since it is possible that + // a class will be marked as non-garbage collected, which in turn will mark the template instance that uses + // it as non-garbage collected, which in turn means the class that contains the array also do not have to be + // garbage collected + EvaluateTemplateInstances(numTempl, true); // Is there some path in which this structure is involved in circular references? + // If the type contains a member of a type that is yet to be validated, then reinsert it at the end + bool mustReevaluate = false; bool gc = false; - for( asUINT p = 0; p < ot->properties.GetLength(); p++ ) + for( asUINT p = 0; p < type->properties.GetLength(); p++ ) { - asCDataType dt = ot->properties[p]->type; + asCDataType dt = type->properties[p]->type; if( !dt.IsObject() ) continue; - if( dt.IsObjectHandle() ) + if( typesToValidate.Exists(dt.GetObjectType()) ) + mustReevaluate = true; + else { - // If it is known that the handle can't be involved in a circular reference - // then this object doesn't need to be marked as garbage collected. - asCObjectType *prop = dt.GetObjectType(); - - if( prop->flags & asOBJ_SCRIPT_OBJECT ) + if( dt.IsTemplate() ) { - // For script objects, treat non-final classes as if they can contain references - // as it is not known what derived classes might do. For final types, check all - // properties to determine if any of those can cause a circular reference. - if( prop->flags & asOBJ_NOINHERIT ) + // Check if any of the subtypes are yet to be evaluated + bool skip = false; + for( asUINT s = 0; s < dt.GetObjectType()->GetSubTypeCount(); s++ ) { - for( asUINT sp = 0; sp < prop->properties.GetLength(); sp++ ) + asCObjectType *t = reinterpret_cast(dt.GetObjectType()->GetSubType(s)); + if( typesToValidate.Exists(t) ) { - asCDataType sdt = prop->properties[sp]->type; + mustReevaluate = true; + skip = true; + break; + } + } + if( skip ) + continue; + } - if( sdt.IsObject() ) + if( dt.IsObjectHandle() ) + { + // If it is known that the handle can't be involved in a circular reference + // then this object doesn't need to be marked as garbage collected. + asCObjectType *prop = dt.GetObjectType(); + + if( prop->flags & asOBJ_SCRIPT_OBJECT ) + { + // For script objects, treat non-final classes as if they can contain references + // as it is not known what derived classes might do. For final types, check all + // properties to determine if any of those can cause a circular reference with this + // class. + if( prop->flags & asOBJ_NOINHERIT ) + { + for( asUINT sp = 0; sp < prop->properties.GetLength(); sp++ ) { - if( sdt.IsObjectHandle() ) + asCDataType sdt = prop->properties[sp]->type; + + if( sdt.IsObject() ) { - // TODO: runtime optimize: If the handle is again to a final class, then we can recursively check if the circular reference can occur - if( sdt.GetObjectType()->flags & (asOBJ_SCRIPT_OBJECT | asOBJ_GC) ) + if( sdt.IsObjectHandle() ) { + // TODO: runtime optimize: If the handle is again to a final class, then we can recursively check if the circular reference can occur + if( sdt.GetObjectType()->flags & (asOBJ_SCRIPT_OBJECT | asOBJ_GC) ) + { + gc = true; + break; + } + } + else if( sdt.GetObjectType()->flags & asOBJ_GC ) + { + // TODO: runtime optimize: Just because the member type is a potential circle doesn't mean that this one is. + // Only if the object is of a type that can reference this type, either directly or indirectly gc = true; break; } } - else if( sdt.GetObjectType()->flags & asOBJ_GC ) - { - // TODO: runtime optimize: Just because the member type is a potential circle doesn't mean that this one is. - // Only if the object is of a type that can reference this type, either directly or indirectly - gc = true; - break; - } } - } - if( gc ) + if( gc ) + break; + } + else + { + // Assume it is garbage collected as it is not known at compile time what might inherit from this type + gc = true; break; + } } - else + else if( prop->flags & asOBJ_GC ) { - // Assume it is garbage collected as it is not known at compile time what might inherit from this type + // If a type is not a script object, adopt its GC flag + // TODO: runtime optimize: Just because an application registered class is garbage collected, doesn't mean it + // can form a circular reference with this script class. Perhaps need a flag to tell + // if the script classes that contains the type should be garbage collected or not. gc = true; break; } } - else if( prop->flags & asOBJ_GC ) + else if( dt.GetObjectType()->flags & asOBJ_GC ) { - // If a type is not a script object, adopt its GC flag + // TODO: runtime optimize: Just because the member type is a potential circle doesn't mean that this one is. + // Only if the object is of a type that can reference this type, either directly or indirectly gc = true; break; } } - else if( dt.GetObjectType()->flags & asOBJ_GC ) - { - // TODO: runtime optimize: Just because the member type is a potential circle doesn't mean that this one is. - // Only if the object is of a type that can reference this type, either directly or indirectly - gc = true; - break; - } + } + + // If the class wasn't found to require garbage collection, but it + // contains another type that has yet to be evaluated then it must be + // re-evaluated. + if( !gc && mustReevaluate ) + { + typesToValidate.PushLast(type); + numReevaluations++; + continue; } // Update the flag in the object type if( gc ) - ot->flags |= asOBJ_GC; + type->flags |= asOBJ_GC; else - ot->flags &= ~asOBJ_GC; + type->flags &= ~asOBJ_GC; + + // Reset the counter + numReevaluations = 0; } } @@ -2985,10 +3295,9 @@ void asCBuilder::IncludeMethodsFromMixins(sClassDeclaration *decl) { asCScriptNode *node = decl->node->firstChild; - // Skip the 'final' and 'shared' keywords - if( decl->objType->IsShared() ) - node = node->next; - if( decl->objType->flags & asOBJ_NOINHERIT ) + // Skip the class attributes + while( node->nodeType == snIdentifier && + !decl->script->TokenEquals(node->tokenPos, node->tokenLength, decl->name.AddressOf()) ) node = node->next; // Skip the name of the class @@ -3005,7 +3314,20 @@ void asCBuilder::IncludeMethodsFromMixins(sClassDeclaration *decl) continue; } - sMixinClass *mixin = GetMixinClass(name.AddressOf(), ns); + sMixinClass *mixin = 0; + while( ns ) + { + // Need to make sure the name is not an object type + asCObjectType *objType = GetObjectType(name.AddressOf(), ns); + if( objType == 0 ) + mixin = GetMixinClass(name.AddressOf(), ns); + + if( objType || mixin ) + break; + + ns = engine->GetParentNameSpace(ns); + } + if( mixin ) { // Find methods from mixin declaration @@ -3026,7 +3348,7 @@ void asCBuilder::IncludeMethodsFromMixins(sClassDeclaration *decl) asCScriptNode *copy = n->CreateCopy(engine); // Register the method, but only if it doesn't already exist in the class - RegisterScriptFunctionFromNode(copy, mixin->script, decl->objType, false, false, 0, false, true); + RegisterScriptFunctionFromNode(copy, mixin->script, decl->objType, false, false, mixin->ns, false, true); } else if( n->nodeType == snVirtualProperty ) { @@ -3047,10 +3369,9 @@ void asCBuilder::IncludePropertiesFromMixins(sClassDeclaration *decl) { asCScriptNode *node = decl->node->firstChild; - // Skip the 'final' and 'shared' keywords - if( decl->objType->IsShared() ) - node = node->next; - if( decl->objType->flags & asOBJ_NOINHERIT ) + // Skip the class attributes + while( node->nodeType == snIdentifier && + !decl->script->TokenEquals(node->tokenPos, node->tokenLength, decl->name.AddressOf()) ) node = node->next; // Skip the name of the class @@ -3067,7 +3388,20 @@ void asCBuilder::IncludePropertiesFromMixins(sClassDeclaration *decl) continue; } - sMixinClass *mixin = GetMixinClass(name.AddressOf(), ns); + sMixinClass *mixin = 0; + while( ns ) + { + // Need to make sure the name is not an object type + asCObjectType *objType = GetObjectType(name.AddressOf(), ns); + if( objType == 0 ) + mixin = GetMixinClass(name.AddressOf(), ns); + + if( objType || mixin ) + break; + + ns = engine->GetParentNameSpace(ns); + } + if( mixin ) { // Find properties from mixin declaration @@ -3084,12 +3418,17 @@ void asCBuilder::IncludePropertiesFromMixins(sClassDeclaration *decl) if( n->nodeType == snDeclaration ) { asCScriptNode *n2 = n->firstChild; - bool isPrivate = false; + bool isPrivate = false, isProtected = false; if( n2 && n2->tokenType == ttPrivate ) { isPrivate = true; n2 = n2->next; } + else if( n2 && n2->tokenType == ttProtected ) + { + isProtected = true; + n2 = n2->next; + } asCScriptCode *file = mixin->script; asCDataType dt = CreateDataTypeFromNode(n2, file, mixin->ns); @@ -3128,7 +3467,7 @@ void asCBuilder::IncludePropertiesFromMixins(sClassDeclaration *decl) if( r < 0 ) WriteInfo(TXT_WHILE_INCLUDING_MIXIN, decl->script, node); - AddPropertyToClass(decl, name, dt, isPrivate, file, n2); + AddPropertyToClass(decl, name, dt, isPrivate, isProtected, false, file, n2); } else { @@ -3138,6 +3477,7 @@ void asCBuilder::IncludePropertiesFromMixins(sClassDeclaration *decl) { asCObjectProperty *prop = decl->objType->properties[p]; if( prop->isPrivate == isPrivate && + prop->isProtected == isProtected && prop->name == name && prop->type == dt ) { @@ -3184,8 +3524,10 @@ int asCBuilder::CreateVirtualFunction(asCScriptFunction *func, int idx) vf->id = engine->GetNextScriptFunctionId(); vf->isReadOnly = func->isReadOnly; vf->objectType = func->objectType; + vf->objectType->AddRefInternal(); vf->signatureId = func->signatureId; vf->isPrivate = func->isPrivate; + vf->isProtected = func->isProtected; vf->isFinal = func->isFinal; vf->isOverride = func->isOverride; vf->vfTableIdx = idx; @@ -3200,19 +3542,25 @@ int asCBuilder::CreateVirtualFunction(asCScriptFunction *func, int idx) return vf->id; } -asCObjectProperty *asCBuilder::AddPropertyToClass(sClassDeclaration *decl, const asCString &name, const asCDataType &dt, bool isPrivate, asCScriptCode *file, asCScriptNode *node) +asCObjectProperty *asCBuilder::AddPropertyToClass(sClassDeclaration *decl, const asCString &name, const asCDataType &dt, bool isPrivate, bool isProtected, bool isInherited, asCScriptCode *file, asCScriptNode *node) { - // If the declaration node is not given, then - // this property is inherited from a base class if( node ) { + asASSERT(!isInherited); + // Check if the property is allowed - if( !dt.CanBeInstanciated() ) + if( !dt.CanBeInstantiated() ) { if( file && node ) { asCString str; - str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf()); + if( dt.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, dt.Format(decl->objType->nameSpace).AddressOf()); + else if( dt.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, dt.Format(decl->objType->nameSpace).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(decl->objType->nameSpace).AddressOf()); WriteError(str, file, node); } return 0; @@ -3230,9 +3578,15 @@ asCObjectProperty *asCBuilder::AddPropertyToClass(sClassDeclaration *decl, const sPropertyInitializer p(name, declNode, initNode, file); decl->propInits.PushLast(p); } + else + { + // If the declaration node is not given, then + // this property is inherited from a base class + asASSERT(isInherited); + } // Add the property to the object type - return decl->objType->AddPropertyToClass(name, dt, isPrivate); + return decl->objType->AddPropertyToClass(name, dt, isPrivate, isProtected, isInherited); } bool asCBuilder::DoesMethodExist(asCObjectType *objType, int methodId, asUINT *methodIndex) @@ -3266,17 +3620,18 @@ void asCBuilder::AddDefaultConstructor(asCObjectType *objType, asCScriptCode *fi asCArray parameterTypes; asCArray inOutFlags; asCArray defaultArgs; + asCArray parameterNames; // Add the script function // TODO: declaredAt should be set to where the class has been declared - module->AddScriptFunction(file->idx, 0, funcId, objType->name, returnType, parameterTypes, inOutFlags, defaultArgs, false, objType); + module->AddScriptFunction(file->idx, 0, funcId, objType->name, returnType, parameterTypes, parameterNames, inOutFlags, defaultArgs, false, objType); // Set it as default constructor if( objType->beh.construct ) - engine->scriptFunctions[objType->beh.construct]->Release(); + engine->scriptFunctions[objType->beh.construct]->ReleaseInternal(); objType->beh.construct = funcId; objType->beh.constructors[0] = funcId; - engine->scriptFunctions[funcId]->AddRef(); + engine->scriptFunctions[funcId]->AddRefInternal(); // The bytecode for the default constructor will be generated // only after the potential inheritance has been established @@ -3299,16 +3654,16 @@ void asCBuilder::AddDefaultConstructor(asCObjectType *objType, asCScriptCode *fi // Add a default factory as well funcId = engine->GetNextScriptFunctionId(); if( objType->beh.factory ) - engine->scriptFunctions[objType->beh.factory]->Release(); + engine->scriptFunctions[objType->beh.factory]->ReleaseInternal(); objType->beh.factory = funcId; objType->beh.factories[0] = funcId; returnType = asCDataType::CreateObjectHandle(objType, false); // TODO: should be the same as the constructor - module->AddScriptFunction(file->idx, 0, funcId, objType->name, returnType, parameterTypes, inOutFlags, defaultArgs, false); + module->AddScriptFunction(file->idx, 0, funcId, objType->name, returnType, parameterTypes, parameterNames, inOutFlags, defaultArgs, false); functions.PushLast(0); asCCompiler compiler(engine); compiler.CompileFactory(this, file, engine->scriptFunctions[funcId]); - engine->scriptFunctions[funcId]->AddRef(); + engine->scriptFunctions[funcId]->AddRefInternal(); // If the object is shared, then the factory must also be marked as shared if( objType->flags & asOBJ_SHARED ) @@ -3336,9 +3691,9 @@ int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSp if( isShared ) { // Look for a pre-existing shared enum with the same signature - for( asUINT n = 0; n < engine->classTypes.GetLength(); n++ ) + for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ ) { - asCObjectType *o = engine->classTypes[n]; + asCObjectType *o = engine->sharedScriptTypes[n]; if( o && o->IsShared() && (o->flags & asOBJ_ENUM) && @@ -3358,7 +3713,10 @@ int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSp asCObjectType *st; if( existingSharedType ) + { st = existingSharedType; + st->AddRefInternal(); + } else { st = asNEW(asCObjectType)(engine); @@ -3371,14 +3729,15 @@ int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSp st->size = 4; st->name = name; st->nameSpace = ns; + st->module = module; } module->enumTypes.PushLast(st); - st->AddRef(); - // TODO: cleanup: Should the enum type really be stored in the engine->classTypes? - // http://www.gamedev.net/topic/616912-c-header-file-shared-with-scripts/page__gopid__4895940 - if( !existingSharedType ) - engine->classTypes.PushLast(st); + if( !existingSharedType && isShared ) + { + engine->sharedScriptTypes.PushLast(st); + st->AddRefInternal(); + } // Store the location of this declaration for reference in name collisions sClassDeclaration *decl = asNEW(sClassDeclaration); @@ -3406,7 +3765,7 @@ int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSp // If this is a pre-existent shared enum, then just double check // that the value is already defined in the original declaration bool found = false; - for( size_t n = 0; n < st->enumValues.GetLength(); n++ ) + for( asUINT n = 0; n < st->enumValues.GetLength(); n++ ) if( st->enumValues[n]->name == name ) { found = true; @@ -3460,6 +3819,7 @@ int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSp gvar->initializationNode = asnNode; gvar->name = name; gvar->datatype = type; + gvar->ns = ns; // No need to allocate space on the global memory stack since the values are stored in the asCObjectType // Set the index to a negative to allow compiler to diferentiate from ordinary global var when compiling the initialization gvar->index = -1; @@ -3523,11 +3883,9 @@ int asCBuilder::RegisterTypedef(asCScriptNode *node, asCScriptCode *file, asSNam st->name = name; st->nameSpace = ns; st->templateSubTypes.PushLast(dataType); - - st->AddRef(); + st->module = module; module->typeDefs.PushLast(st); - engine->classTypes.PushLast(st); // Store the location of this declaration for reference in name collisions sClassDeclaration *decl = asNEW(sClassDeclaration); @@ -3547,17 +3905,22 @@ int asCBuilder::RegisterTypedef(asCScriptNode *node, asCScriptCode *file, asSNam return r; } -void asCBuilder::GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool &isConstMethod, bool &isConstructor, bool &isDestructor, bool &isPrivate, bool &isOverride, bool &isFinal, bool &isShared, asSNameSpace *implicitNamespace) +void asCBuilder::GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool &isConstMethod, bool &isConstructor, bool &isDestructor, bool &isPrivate, bool &isProtected, bool &isOverride, bool &isFinal, bool &isShared, asSNameSpace *implicitNamespace) { node = node->firstChild; - // Is the function a private class method? - isPrivate = false; + // Is the function a private or protected class method? + isPrivate = false, isProtected = false; if( node->tokenType == ttPrivate ) { isPrivate = true; node = node->next; } + else if( node->tokenType == ttProtected ) + { + isProtected = true; + node = node->next; + } // Is the function shared? isShared = false; @@ -3714,7 +4077,7 @@ asCString asCBuilder::GetCleanExpressionString(asCScriptNode *node, asCScriptCod asCString cleanStr; for( asUINT n = 0; n < str.GetLength(); ) { - int len; + asUINT len = 0; asETokenClass tok = engine->ParseToken(str.AddressOf() + n, str.GetLength() - n, &len); if( tok != asTC_COMMENT && tok != asTC_WHITESPACE ) { @@ -3742,25 +4105,26 @@ int asCBuilder::RegisterScriptFunctionFromNode(asCScriptNode *node, asCScriptCod bool isConstructor; bool isDestructor; bool isPrivate; + bool isProtected; bool isShared; - asASSERT( (objType && ns == 0) || isGlobalFunction ); + asASSERT( (objType && ns == 0) || isGlobalFunction || isMixin ); // Set the default namespace if( ns == 0 ) - { + { if( objType ) ns = objType->nameSpace; else ns = engine->nameSpaces[0]; - } + } - GetParsedFunctionDetails(node, file, objType, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isOverride, isFinal, isShared, ns); + GetParsedFunctionDetails(node, file, objType, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared, ns); - return RegisterScriptFunction(node, file, objType, isInterface, isGlobalFunction, ns, isExistingShared, isMixin, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isOverride, isFinal, isShared); + return RegisterScriptFunction(node, file, objType, isInterface, isGlobalFunction, ns, isExistingShared, isMixin, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared); } -int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool isConstMethod, bool isConstructor, bool isDestructor, bool isPrivate, bool isOverride, bool isFinal, bool isShared) +int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool isConstMethod, bool isConstructor, bool isDestructor, bool isPrivate, bool isProtected, bool isOverride, bool isFinal, bool isShared) { // Determine default namespace if not specified if( ns == 0 ) @@ -3790,6 +4154,9 @@ int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, if( func->name == name && func->IsSignatureExceptNameEqual(returnType, parameterTypes, inOutFlags, objType, isConstMethod) ) { + // Add the shared function in this module too + module->AddScriptFunction(func); + found = true; break; } @@ -3843,7 +4210,14 @@ int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, // Verify that the name of the constructor/destructor is the same as the class if( name != objType->name ) - WriteError(TXT_CONSTRUCTOR_NAME_ERROR, file, node); + { + asCString str; + if( isDestructor ) + str.Format(TXT_DESTRUCTOR_s_s_NAME_ERROR, objType->name.AddressOf(), name.AddressOf()); + else + str.Format(TXT_METHOD_s_s_HAS_NO_RETURN_TYPE, objType->name.AddressOf(), name.AddressOf()); + WriteError(str, file, node); + } if( isDestructor ) name = "~" + name; @@ -3928,29 +4302,62 @@ int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, GetObjectMethodDescriptions(name.AddressOf(), objType, funcs, false); else GetFunctionDescriptions(name.AddressOf(), funcs, ns); - for( asUINT n = 0; n < funcs.GetLength(); ++n ) + if( objType && (name == "opConv" || name == "opImplConv" || name == "opCast" || name == "opImplCast") && parameterTypes.GetLength() == 0 ) { - asCScriptFunction *func = GetFunctionDescription(funcs[n]); - if( func->IsSignatureExceptNameAndReturnTypeEqual(parameterTypes, inOutFlags, objType, isConstMethod) ) + // opConv and opCast are special methods used for type casts + for( asUINT n = 0; n < funcs.GetLength(); ++n ) { - if( isMixin ) + asCScriptFunction *func = GetFunctionDescription(funcs[n]); + if( func->IsSignatureExceptNameEqual(returnType, parameterTypes, inOutFlags, objType, isConstMethod) ) { - // Clean up the memory, as the function will not be registered - if( node ) - node->Destroy(engine); - sFunctionDescription *func = functions.PopLast(); - asDELETE(func, sFunctionDescription); + // TODO: clean up: Reuse the same error handling for both opConv and normal methods + if( isMixin ) + { + // Clean up the memory, as the function will not be registered + if( node ) + node->Destroy(engine); + sFunctionDescription *func = functions.PopLast(); + asDELETE(func, sFunctionDescription); - // Free the default args - for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) - if( defaultArgs[n] ) - asDELETE(defaultArgs[n], asCString); + // Free the default args + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); - return 0; + return 0; + } + + WriteError(TXT_FUNCTION_ALREADY_EXIST, file, node); + break; } + } + } + else + { + for( asUINT n = 0; n < funcs.GetLength(); ++n ) + { + asCScriptFunction *func = GetFunctionDescription(funcs[n]); + if( func->IsSignatureExceptNameAndReturnTypeEqual(parameterTypes, inOutFlags, objType, isConstMethod) ) + { + if( isMixin ) + { + // Clean up the memory, as the function will not be registered + if( node ) + node->Destroy(engine); + sFunctionDescription *func = functions.PopLast(); + asDELETE(func, sFunctionDescription); - WriteError(TXT_FUNCTION_ALREADY_EXIST, file, node); - break; + // Free the default args + for( asUINT n = 0; n < defaultArgs.GetLength(); n++ ) + if( defaultArgs[n] ) + asDELETE(defaultArgs[n], asCString); + + return 0; + } + + WriteError(TXT_FUNCTION_ALREADY_EXIST, file, node); + break; + } } } @@ -3967,36 +4374,36 @@ int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, // TODO: clean up: This should be done by AddScriptFunction() itself module->globalFunctions.Put(f); - f->AddRef(); } else { int row = 0, col = 0; if( node ) file->ConvertPosToRowCol(node->tokenPos, &row, &col); - module->AddScriptFunction(file->idx, (row&0xFFFFF)|((col&0xFFF)<<20), funcId, name, returnType, parameterTypes, inOutFlags, defaultArgs, isInterface, objType, isConstMethod, isGlobalFunction, isPrivate, isFinal, isOverride, isShared, ns); + module->AddScriptFunction(file->idx, (row&0xFFFFF)|((col&0xFFF)<<20), funcId, name, returnType, parameterTypes, parameterNames, inOutFlags, defaultArgs, isInterface, objType, isConstMethod, isGlobalFunction, isPrivate, isProtected, isFinal, isOverride, isShared, ns); } // Make sure the default args are declared correctly ValidateDefaultArgs(file, node, engine->scriptFunctions[funcId]); + CheckForConflictsDueToDefaultArgs(file, node, engine->scriptFunctions[funcId], objType); if( objType ) { asASSERT( !isExistingShared ); - engine->scriptFunctions[funcId]->AddRef(); + engine->scriptFunctions[funcId]->AddRefInternal(); if( isConstructor ) { int factoryId = engine->GetNextScriptFunctionId(); if( parameterTypes.GetLength() == 0 ) { // Overload the default constructor - engine->scriptFunctions[objType->beh.construct]->Release(); + engine->scriptFunctions[objType->beh.construct]->ReleaseInternal(); objType->beh.construct = funcId; objType->beh.constructors[0] = funcId; // Register the default factory as well - engine->scriptFunctions[objType->beh.factory]->Release(); + engine->scriptFunctions[objType->beh.factory]->ReleaseInternal(); objType->beh.factory = factoryId; objType->beh.factories[0] = factoryId; } @@ -4014,7 +4421,7 @@ int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, defaultArgs[n] = asNEW(asCString)(*defaultArgs[n]); asCDataType dt = asCDataType::CreateObjectHandle(objType, false); - module->AddScriptFunction(file->idx, engine->scriptFunctions[funcId]->scriptData->declaredAt, factoryId, name, dt, parameterTypes, inOutFlags, defaultArgs, false); + module->AddScriptFunction(file->idx, engine->scriptFunctions[funcId]->scriptData->declaredAt, factoryId, name, dt, parameterTypes, parameterNames, inOutFlags, defaultArgs, false); // If the object is shared, then the factory must also be marked as shared if( objType->flags & asOBJ_SHARED ) @@ -4026,7 +4433,7 @@ int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, // Compile the factory immediately asCCompiler compiler(engine); compiler.CompileFactory(this, file, engine->scriptFunctions[factoryId]); - engine->scriptFunctions[factoryId]->AddRef(); + engine->scriptFunctions[factoryId]->AddRefInternal(); } else if( isDestructor ) objType->beh.destruct = funcId; @@ -4038,9 +4445,9 @@ int asCBuilder::RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, f->parameterTypes[0].GetObjectType() == f->objectType && (f->inOutFlags[0] & asTM_INREF) ) { - engine->scriptFunctions[objType->beh.copy]->Release(); + engine->scriptFunctions[objType->beh.copy]->ReleaseInternal(); objType->beh.copy = funcId; - f->AddRef(); + f->AddRefInternal(); } objType->methods.PushLast(funcId); @@ -4066,14 +4473,14 @@ int asCBuilder::RegisterVirtualProperty(asCScriptNode *node, asCScriptCode *file asASSERT( (objType && ns == 0) || isGlobalFunction ); if( ns == 0 ) - { + { if( objType ) ns = objType->nameSpace; else ns = engine->nameSpaces[0]; - } + } - bool isPrivate = false; + bool isPrivate = false, isProtected = false; asCString emulatedName; asCDataType emulatedType; @@ -4085,6 +4492,11 @@ int asCBuilder::RegisterVirtualProperty(asCScriptNode *node, asCScriptCode *file isPrivate = true; node = node->next; } + else if( !isGlobalFunction && node->tokenType == ttProtected ) + { + isProtected = true; + node = node->next; + } emulatedType = CreateDataTypeFromNode(node, file, ns); emulatedType = ModifyDataTypeFromNode(emulatedType, node->next, file, 0, 0); @@ -4190,7 +4602,7 @@ int asCBuilder::RegisterVirtualProperty(asCScriptNode *node, asCScriptCode *file if( success ) { if( !isExistingShared ) - RegisterScriptFunction(funcNode, file, objType, isInterface, isGlobalFunction, ns, false, false, name, returnType, paramNames, paramTypes, paramModifiers, defaultArgs, isConst, false, false, isPrivate, isOverride, isFinal, false); + RegisterScriptFunction(funcNode, file, objType, isInterface, isGlobalFunction, ns, false, false, name, returnType, paramNames, paramTypes, paramModifiers, defaultArgs, isConst, false, false, isPrivate, isProtected, isOverride, isFinal, false); else { // Free the funcNode as it won't be used @@ -4234,12 +4646,12 @@ int asCBuilder::RegisterImportedFunction(int importID, asCScriptNode *node, asCS asCArray parameterTypes; asCArray inOutFlags; asCArray defaultArgs; - bool isConstMethod, isOverride, isFinal, isConstructor, isDestructor, isPrivate, isShared; + bool isConstMethod, isOverride, isFinal, isConstructor, isDestructor, isPrivate, isProtected, isShared; if( ns == 0 ) ns = engine->nameSpaces[0]; - GetParsedFunctionDetails(node->firstChild, file, 0, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isOverride, isFinal, isShared, ns); + GetParsedFunctionDetails(node->firstChild, file, 0, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared, ns); CheckNameConflict(name.AddressOf(), node, file, ns); // Check that the same function hasn't been registered already in the namespace @@ -4296,7 +4708,8 @@ void asCBuilder::GetFunctionDescriptions(const char *name, asCArray &funcs, // TODO: optimize: Linear search: This is probably not that critial. Also bindInformation will probably be removed in near future for( n = 0; n < module->bindInformations.GetLength(); n++ ) { - if( module->bindInformations[n]->importedFunctionSignature->name == name ) + if( module->bindInformations[n]->importedFunctionSignature->name == name && + module->bindInformations[n]->importedFunctionSignature->nameSpace == ns ) funcs.PushLast(module->bindInformations[n]->importedFunctionSignature->id); } @@ -4314,12 +4727,21 @@ void asCBuilder::GetFunctionDescriptions(const char *name, asCArray &funcs, } } -void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray &methods, bool objIsConst, const asCString &scope) +void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray &methods, bool objIsConst, const asCString &scope, asCScriptNode *errNode, asCScriptCode *script) { if( scope != "" ) { + // If searching with a scope informed, then the node and script must also be informed for potential error reporting + asASSERT( errNode && script ); + + // If the scope contains ::identifier, then use the last identifier as the class name and the rest of is as the namespace + int n = scope.FindLast("::"); + asCString className = n >= 0 ? scope.SubString(n+2) : scope; + asCString nsName = n >= 0 ? scope.SubString(0, n) : ""; + asSNameSpace *ns = GetNameSpaceByString(nsName, objectType->nameSpace, errNode, script); + // Find the base class with the specified scope - while( objectType && objectType->name != scope ) + while( objectType && (objectType->name != className || objectType->nameSpace != ns) ) objectType = objectType->derivedFrom; // If the scope is not any of the base classes, then return no methods @@ -4333,8 +4755,10 @@ void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *ob // Only add const methods to the list for( asUINT n = 0; n < objectType->methods.GetLength(); n++ ) { - if( engine->scriptFunctions[objectType->methods[n]]->name == name && - engine->scriptFunctions[objectType->methods[n]]->isReadOnly ) + asCScriptFunction *func = engine->scriptFunctions[objectType->methods[n]]; + if( func->name == name && + func->isReadOnly && + (func->accessMask & module->accessMask) ) { // When the scope is defined the returned methods should be the true methods, not the virtual method stubs if( scope == "" ) @@ -4353,7 +4777,9 @@ void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *ob // TODO: Prefer non-const over const for( asUINT n = 0; n < objectType->methods.GetLength(); n++ ) { - if( engine->scriptFunctions[objectType->methods[n]]->name == name ) + asCScriptFunction *func = engine->scriptFunctions[objectType->methods[n]]; + if( func->name == name && + (func->accessMask & module->accessMask) ) { // When the scope is defined the returned methods should be the true methods, not the virtual method stubs if( scope == "" ) @@ -4375,15 +4801,15 @@ void asCBuilder::WriteInfo(const asCString &scriptname, const asCString &message // Need to store the pre message in a structure if( pre ) { - preMessage.isSet = true; - preMessage.c = c; - preMessage.r = r; - preMessage.message = message; - preMessage.scriptname = scriptname; + engine->preMessage.isSet = true; + engine->preMessage.c = c; + engine->preMessage.r = r; + engine->preMessage.message = message; + engine->preMessage.scriptname = scriptname; } else { - preMessage.isSet = false; + engine->preMessage.isSet = false; if( !silent ) engine->WriteMessage(scriptname.AddressOf(), r, c, asMSGTYPE_INFORMATION, message.AddressOf()); @@ -4412,10 +4838,6 @@ void asCBuilder::WriteError(const asCString &scriptname, const asCString &messag { numErrors++; - // Need to pass the preMessage first - if( preMessage.isSet ) - WriteInfo(preMessage.scriptname, preMessage.message, preMessage.r, preMessage.c, false); - if( !silent ) engine->WriteMessage(scriptname.AddressOf(), r, c, asMSGTYPE_ERROR, message.AddressOf()); } @@ -4426,15 +4848,20 @@ void asCBuilder::WriteWarning(const asCString &scriptname, const asCString &mess { numWarnings++; - // Need to pass the preMessage first - if( preMessage.isSet ) - WriteInfo(preMessage.scriptname, preMessage.message, preMessage.r, preMessage.c, false); - if( !silent ) engine->WriteMessage(scriptname.AddressOf(), r, c, asMSGTYPE_WARNING, message.AddressOf()); } } +void asCBuilder::WriteWarning(const asCString &message, asCScriptCode *file, asCScriptNode *node) +{ + int r = 0, c = 0; + if( node && file ) + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteWarning(file ? file->name : asCString(""), message, r, c); +} + asCString asCBuilder::GetScopeFromNode(asCScriptNode *node, asCScriptCode *script, asCScriptNode **next) { asCString scope; @@ -4464,39 +4891,28 @@ asCString asCBuilder::GetScopeFromNode(asCScriptNode *node, asCScriptCode *scrip asSNameSpace *asCBuilder::GetNameSpaceFromNode(asCScriptNode *node, asCScriptCode *script, asSNameSpace *implicitNs, asCScriptNode **next) { asCString scope = GetScopeFromNode(node, script, next); + return GetNameSpaceByString(scope, implicitNs, node, script); +} + +asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script) +{ asSNameSpace *ns = implicitNs; - if( scope == "::" ) + if( nsName == "::" ) ns = engine->nameSpaces[0]; - else if( scope != "" ) + else if( nsName != "" ) { - ns = engine->FindNameSpace(scope.AddressOf()); + ns = engine->FindNameSpace(nsName.AddressOf()); if( ns == 0 ) { asCString msg; - msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, scope.AddressOf()); - WriteError(msg, script, node); + msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, nsName.AddressOf()); + WriteError(msg, script, errNode); } } return ns; } -asSNameSpace *asCBuilder::GetParentNameSpace(asSNameSpace *ns) -{ - if( ns == 0 ) return 0; - if( ns == engine->nameSpaces[0] ) return 0; - - asCString scope = ns->name; - int pos = scope.FindLast("::"); - if( pos >= 0 ) - { - scope = scope.SubString(0, pos); - return engine->FindNameSpace(scope.AddressOf()); - } - - return engine->nameSpaces[0]; -} - asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCode *file, asSNameSpace *implicitNamespace, bool acceptHandleForScope, asCObjectType *currentType) { asASSERT(node->nodeType == snDataType); @@ -4531,13 +4947,14 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod str.Assign(&file->code[n->tokenPos], n->tokenLength); // Recursively search parent namespaces for matching type + asSNameSpace *origNs = ns; while( ns && !found ) { asCObjectType *ot = 0; // If this is for a template type, then we must first determine if the // identifier matches any of the template subtypes - if( currentType && (currentType->flags & asOBJ_TEMPLATE)) + if( currentType && (currentType->flags & asOBJ_TEMPLATE) ) { for( asUINT subtypeIndex = 0; subtypeIndex < currentType->templateSubTypes.GetLength(); subtypeIndex++) { @@ -4565,7 +4982,7 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod // Make sure the module has access to the object type if( !module || (module->accessMask & ot->accessMask) ) { - if(asOBJ_TYPEDEF == (ot->flags & asOBJ_TYPEDEF)) + if( asOBJ_TYPEDEF == (ot->flags & asOBJ_TYPEDEF) ) { // TODO: typedef: A typedef should be considered different from the original type (though with implicit conversions between the two) // Create primitive data type based on object flags @@ -4587,7 +5004,10 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod { n = n->next; - asCDataType subType = CreateDataTypeFromNode(n, file, implicitNamespace, false, module ? 0 : ot); + // When parsing function definitions for template registrations (currentType != 0) it is necessary + // to pass in the current template type to the recursive call since it is this ones sub-template types + // that should be allowed. + asCDataType subType = CreateDataTypeFromNode(n, file, implicitNamespace, false, module ? 0 : (currentType ? currentType : ot)); subTypes.PushLast(subType); if( subType.IsReadOnly() ) @@ -4611,8 +5031,6 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod return asCDataType::CreatePrimitive(ttInt, false); } - asASSERT( subTypes.GetLength() == ot->templateSubTypes.GetLength() ); - // Check if any of the given subtypes are different from the template's declared subtypes bool isDifferent = false; for( subtypeIndex = 0; subtypeIndex < subTypes.GetLength(); subtypeIndex++ ) @@ -4628,14 +5046,28 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod { // This is a template instance // Need to find the correct object type - asCObjectType *otInstance = engine->GetTemplateInstanceType(ot, subTypes); + asCObjectType *otInstance = engine->GetTemplateInstanceType(ot, subTypes, module); + + if( otInstance && otInstance->scriptSectionIdx < 0 ) + { + // If this is the first time the template instance is used, store where it was declared from + otInstance->scriptSectionIdx = engine->GetScriptSectionNameIndex(file->name.AddressOf()); + int row, column; + file->ConvertPosToRowCol(n->tokenPos, &row, &column); + otInstance->declaredAt = (row&0xFFFFF)|(column<<20); + } if( !otInstance ) { - asCString msg; - // TODO: Should name all subtypes - msg.Format(TXT_CANNOT_INSTANCIATE_TEMPLATE_s_WITH_s, ot->name.AddressOf(), subTypes[0].Format().AddressOf()); - WriteError(msg, file, n); + asCString sub = subTypes[0].Format(ot->nameSpace); + for( asUINT s = 1; s < subTypes.GetLength(); s++ ) + { + sub += ","; + sub += subTypes[s].Format(ot->nameSpace); + } + asCString str; + str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, ot->name.AddressOf(), sub.AddressOf()); + WriteError(str, file, n); } ot = otInstance; @@ -4673,20 +5105,27 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod if( !found ) { // Try to find it in the parent namespace - ns = GetParentNameSpace(ns); + ns = engine->GetParentNameSpace(ns); } } if( !found ) { asCString msg; - msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, (const char *)str.AddressOf()); + if( origNs->name == "" ) + msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_GLOBAL_NS, str.AddressOf()); + else + msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_NS_s, str.AddressOf(), origNs->name.AddressOf()); WriteError(msg, file, n); dt = asCDataType::CreatePrimitive(ttInt, isConst); return dt; } } + else if( n->tokenType == ttAuto ) + { + dt = asCDataType::CreateAuto(isConst); + } else { // Create primitive data type @@ -4699,17 +5138,23 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod { if( n->tokenType == ttOpenBracket ) { - // Make sure the sub type can be instanciated - if( !dt.CanBeInstanciated() ) + // Make sure the sub type can be instantiated + if( !dt.CanBeInstantiated() ) { asCString str; - // TODO: Change to "Array sub type cannot be 'type'" - str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf()); + if( dt.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, dt.Format(ns).AddressOf()); + else if( dt.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, dt.Format(ns).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(ns).AddressOf()); + WriteError(str, file, n); } // Make the type an array (or multidimensional array) - if( dt.MakeArray(engine) < 0 ) + if( dt.MakeArray(engine, module) < 0 ) { WriteError(TXT_NO_DEFAULT_ARRAY_TYPE, file, n); break; @@ -4718,7 +5163,12 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod else { // Make the type a handle - if( dt.MakeHandle(true, acceptHandleForScope) < 0 ) + if( dt.IsObjectHandle() ) + { + WriteError(TXT_HANDLE_OF_HANDLE_IS_NOT_ALLOWED, file, n); + break; + } + else if( dt.MakeHandle(true, acceptHandleForScope) < 0 ) { WriteError(TXT_OBJECT_HANDLE_NOT_SUPPORTED, file, n); break; diff --git a/lib/angelscript/source/as_builder.h b/lib/angelscript/source/as_builder.h index 5b894857a..3a9851fec 100644 --- a/lib/angelscript/source/as_builder.h +++ b/lib/angelscript/source/as_builder.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -51,6 +51,11 @@ BEGIN_AS_NAMESPACE +#ifdef AS_NO_COMPILER +// Forward declare the structure, as it is part of some function signatures used even without the compiler +struct sGlobalVariableDescription; +#endif + #ifndef AS_NO_COMPILER struct sFunctionDescription @@ -72,6 +77,7 @@ struct sGlobalVariableDescription asCString name; asCGlobalProperty *property; asCDataType datatype; + asSNameSpace *ns; int index; bool isCompiled; bool isPureConstant; @@ -160,14 +166,16 @@ protected: void WriteError(const asCString &scriptname, const asCString &msg, int r, int c); void WriteError(const asCString &msg, asCScriptCode *file, asCScriptNode *node); void WriteWarning(const asCString &scriptname, const asCString &msg, int r, int c); + void WriteWarning(const asCString &msg, asCScriptCode *file, asCScriptNode *node); + bool DoesGlobalPropertyExist(const char *prop, asSNameSpace *ns, asCGlobalProperty **outProp = 0, sGlobalVariableDescription **outDesc = 0, bool *isAppProp = 0); asCGlobalProperty *GetGlobalProperty(const char *prop, asSNameSpace *ns, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue, bool *isAppProp); int ValidateDefaultArgs(asCScriptCode *script, asCScriptNode *node, asCScriptFunction *func); asCString GetCleanExpressionString(asCScriptNode *n, asCScriptCode *file); asSNameSpace *GetNameSpaceFromNode(asCScriptNode *node, asCScriptCode *script, asSNameSpace *implicitNs, asCScriptNode **next); + asSNameSpace *GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script); asCString GetScopeFromNode(asCScriptNode *n, asCScriptCode *script, asCScriptNode **next = 0); - asSNameSpace *GetParentNameSpace(asSNameSpace *ns); asCObjectType *GetObjectType(const char *type, asSNameSpace *ns); asCScriptFunction *GetFuncDef(const char *type); @@ -175,15 +183,6 @@ protected: asCDataType CreateDataTypeFromNode(asCScriptNode *node, asCScriptCode *file, asSNameSpace *implicitNamespace, bool acceptHandleForScope = false, asCObjectType *currentType = 0); asCDataType ModifyDataTypeFromNode(const asCDataType &type, asCScriptNode *node, asCScriptCode *file, asETypeModifiers *inOutFlag, bool *autoHandle); - struct preMessage_t - { - bool isSet; - asCString message; - asCString scriptname; - int r; - int c; - } preMessage; - int numErrors; int numWarnings; bool silent; @@ -195,6 +194,7 @@ protected: protected: friend class asCCompiler; + int CheckForConflictsDueToDefaultArgs(asCScriptCode *script, asCScriptNode *node, asCScriptFunction *func, asCObjectType *objType); int GetNamespaceAndNameFromNode(asCScriptNode *n, asCScriptCode *script, asSNameSpace *implicitNs, asSNameSpace *&outNs, asCString &outName); int RegisterMixinClass(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); sMixinClass *GetMixinClass(const char *name, asSNameSpace *ns); @@ -204,7 +204,7 @@ protected: void AddInterfaceFromMixinToClass(sClassDeclaration *decl, asCScriptNode *errNode, sMixinClass *mixin); int RegisterScriptFunctionFromNode(asCScriptNode *node, asCScriptCode *file, asCObjectType *object = 0, bool isInterface = false, bool isGlobalFunction = false, asSNameSpace *ns = 0, bool isExistingShared = false, bool isMixin = false); - int RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool isConstMethod, bool isConstructor, bool isDestructor, bool isPrivate, bool isOverride, bool isFinal, bool isShared); + int RegisterScriptFunction(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction, asSNameSpace *ns, bool isExistingShared, bool isMixin, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool isConstMethod, bool isConstructor, bool isDestructor, bool isPrivate, bool isProtected, bool isOverride, bool isFinal, bool isShared); int RegisterVirtualProperty(asCScriptNode *node, asCScriptCode *file, asCObjectType *object = 0, bool isInterface = false, bool isGlobalFunction = false, asSNameSpace *ns = 0, bool isExistingShared = false); int RegisterImportedFunction(int funcID, asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); int RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); @@ -215,11 +215,11 @@ protected: int RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); void CompleteFuncDef(sFuncDef *funcDef); void CompileInterfaces(); - void CompileClasses(); - void GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool &isConstMethod, bool &isConstructor, bool &isDestructor, bool &isPrivate, bool &isOverride, bool &isFinal, bool &isShared, asSNameSpace *implicitNamespace); + void CompileClasses(asUINT originalNumTempl); + void GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray ¶meterNames, asCArray ¶meterTypes, asCArray &inOutFlags, asCArray &defaultArgs, bool &isConstMethod, bool &isConstructor, bool &isDestructor, bool &isPrivate, bool &isProtected, bool &isOverride, bool &isFinal, bool &isShared, asSNameSpace *implicitNamespace); bool DoesMethodExist(asCObjectType *objType, int methodId, asUINT *methodIndex = 0); void AddDefaultConstructor(asCObjectType *objType, asCScriptCode *file); - asCObjectProperty *AddPropertyToClass(sClassDeclaration *c, const asCString &name, const asCDataType &type, bool isPrivate, asCScriptCode *file = 0, asCScriptNode *node = 0); + asCObjectProperty *AddPropertyToClass(sClassDeclaration *c, const asCString &name, const asCDataType &type, bool isPrivate, bool isProtected, bool isInherited, asCScriptCode *file = 0, asCScriptNode *node = 0); int CreateVirtualFunction(asCScriptFunction *func, int idx); void ParseScripts(); void RegisterTypesFromScript(asCScriptNode *node, asCScriptCode *script, asSNameSpace *ns); @@ -232,7 +232,8 @@ protected: asCObjectProperty *GetObjectProperty(asCDataType &obj, const char *prop); asCScriptFunction *GetFunctionDescription(int funcId); void GetFunctionDescriptions(const char *name, asCArray &funcs, asSNameSpace *ns); - void GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray &methods, bool objIsConst, const asCString &scope = ""); + void GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray &methods, bool objIsConst, const asCString &scope = "", asCScriptNode *errNode = 0, asCScriptCode *script = 0); + void EvaluateTemplateInstances(asUINT startIdx, bool keepSilent); asCArray scripts; asCArray functions; diff --git a/lib/angelscript/source/as_bytecode.cpp b/lib/angelscript/source/as_bytecode.cpp index e0531c20a..32564f2a0 100644 --- a/lib/angelscript/source/as_bytecode.cpp +++ b/lib/angelscript/source/as_bytecode.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -864,7 +864,6 @@ void asCByteCode::OptimizeLocally(const asCArray &tempVariableOffsets) instr = GoForward(curr); } - } else if( currOp == asBC_RDSPtr ) { @@ -1553,6 +1552,7 @@ int asCByteCode::GetSize() void asCByteCode::AddCode(asCByteCode *bc) { + if( bc == this ) return; if( bc->first ) { if( first == 0 ) @@ -2037,7 +2037,14 @@ void asCByteCode::PostProcess() DeleteInstruction(curr); } else + { +#ifndef AS_DEBUG + // If the stackSize is negative, then there is a problem with the bytecode. + // If AS_DEBUG is turned on, this same check is done in DebugOutput. + asASSERT( instr->stackSize >= 0 || asBCInfo[instr->op].type == asBCTYPE_INFO ); +#endif instr = instr->next; + } } } @@ -2079,7 +2086,9 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri fprintf(file, "Variables: \n"); for( n = 0; n < func->scriptData->variables.GetLength(); n++ ) { - fprintf(file, " %.3d: %s %s\n", func->scriptData->variables[n]->stackOffset, func->scriptData->variables[n]->type.Format().AddressOf(), func->scriptData->variables[n]->name.AddressOf()); + int idx = func->scriptData->objVariablePos.IndexOf(func->scriptData->variables[n]->stackOffset); + bool isOnHeap = asUINT(idx) < func->scriptData->objVariablesOnHeap ? true : false; + fprintf(file, " %.3d: %s%s %s\n", func->scriptData->variables[n]->stackOffset, isOnHeap ? "(heap) " : "", func->scriptData->variables[n]->type.Format(func->nameSpace).AddressOf(), func->scriptData->variables[n]->name.AddressOf()); } asUINT offset = 0; if( func->objectType ) @@ -2099,7 +2108,11 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri } } if( !found ) - fprintf(file, " %.3d: %s {noname param}\n", offset, func->parameterTypes[n].Format().AddressOf()); + { + int idx = func->scriptData->objVariablePos.IndexOf(offset); + bool isOnHeap = asUINT(idx) < func->scriptData->objVariablesOnHeap ? true : false; + fprintf(file, " %.3d: %s%s {noname param}\n", offset, isOnHeap ? "(heap) " : "", func->parameterTypes[n].Format(func->nameSpace).AddressOf()); + } offset -= func->parameterTypes[n].GetSizeOnStackDWords(); } @@ -2115,10 +2128,20 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri } } if( !found ) - fprintf(file, " %.3d: %s {noname}\n", func->scriptData->objVariablePos[n], func->scriptData->objVariableTypes[n]->name.AddressOf()); + { + if( func->scriptData->objVariableTypes[n] ) + { + int idx = func->scriptData->objVariablePos.IndexOf(func->scriptData->objVariablePos[n]); + bool isOnHeap = asUINT(idx) < func->scriptData->objVariablesOnHeap ? true : false; + fprintf(file, " %.3d: %s%s {noname}\n", func->scriptData->objVariablePos[n], isOnHeap ? "(heap) " : "", func->scriptData->objVariableTypes[n]->name.AddressOf()); + } + else + fprintf(file, " %.3d: null handle {noname}\n", func->scriptData->objVariablePos[n]); + } } fprintf(file, "\n\n"); + bool invalidStackSize = false; int pos = 0; asUINT lineIndex = 0; asCByteInstruction *instr = first; @@ -2131,10 +2154,19 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri lineIndex += 2; } - fprintf(file, "%5d ", pos); - pos += instr->GetSize(); + if( instr->GetSize() > 0 ) + { + fprintf(file, "%5d ", pos); + pos += instr->GetSize(); - fprintf(file, "%3d %c ", int(instr->stackSize + func->scriptData->variableSpace), instr->marked ? '*' : ' '); + fprintf(file, "%3d %c ", int(instr->stackSize + func->scriptData->variableSpace), instr->marked ? '*' : ' '); + if( instr->stackSize < 0 ) + invalidStackSize = true; + } + else + { + fprintf(file, " "); + } switch( asBCInfo[instr->op].type ) { @@ -2182,7 +2214,10 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri switch( instr->op ) { case asBC_OBJTYPE: - fprintf(file, " %-8s 0x%x\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg)); + { + asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg); + fprintf(file, " %-8s 0x%x (type:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), ot->GetName()); + } break; case asBC_PshC4: @@ -2229,6 +2264,16 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri break; case asBCTYPE_QW_ARG: + switch( instr->op ) + { + case asBC_OBJTYPE: + { + asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg); + fprintf(file, " %-8s 0x%x (type:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), ot->GetName()); + } + break; + + default: #ifdef __GNUC__ #ifdef _LP64 fprintf(file, " %-8s 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); @@ -2238,6 +2283,7 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri #else fprintf(file, " %-8s 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); #endif + } break; case asBCTYPE_wW_QW_ARG: @@ -2257,7 +2303,8 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri if( instr->op == asBC_ALLOC ) { asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg); - fprintf(file, " %-8s 0x%x, %d (type:%s)\n", asBCInfo[instr->op].name, *(int*)ARG_DW(instr->arg), *(int*)(ARG_DW(instr->arg)+1), ot->GetName()); + asCScriptFunction *func = engine->scriptFunctions[instr->wArg[0]]; + fprintf(file, " %-8s 0x%x, %d (type:%s, %s)\n", asBCInfo[instr->op].name, *(int*)ARG_DW(instr->arg), *(int*)(ARG_DW(instr->arg)+1), ot->GetName(), func ? func->GetDeclaration() : "{no func}"); } else fprintf(file, " %-8s %u, %d\n", asBCInfo[instr->op].name, *(int*)ARG_DW(instr->arg), *(int*)(ARG_DW(instr->arg)+1)); @@ -2271,14 +2318,15 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri if( instr->op == asBC_ALLOC ) { asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); + asCScriptFunction *func = engine->scriptFunctions[instr->wArg[0]]; #ifdef __GNUC__ #ifdef AS_64BIT_PTR - fprintf(file, " %-8s 0x%lx, %d (type:%s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName()); + fprintf(file, " %-8s 0x%lx, %d (type:%s, %s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName(), func ? func->GetDeclaration() : "{no func}"); #else - fprintf(file, " %-8s 0x%llx, %d (type:%s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName()); + fprintf(file, " %-8s 0x%llx, %d (type:%s, %s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName(), func ? func->GetDeclaration() : "{no func}"); #endif #else - fprintf(file, " %-8s 0x%I64x, %d (type:%s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName()); + fprintf(file, " %-8s 0x%I64x, %d (type:%s, %s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName(), func ? func->GetDeclaration() : "{no func}"); #endif } else @@ -2333,6 +2381,11 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri } fclose(file); + + // If the stackSize is negative then there is something wrong with the + // bytecode, i.e. there is a bug in the compiler or in the optimizer. We + // only check this here to have the bytecode available on file for verification + asASSERT( !invalidStackSize ); } #endif diff --git a/lib/angelscript/source/as_callfunc.cpp b/lib/angelscript/source/as_callfunc.cpp index 597f5ec96..723ba1c28 100644 --- a/lib/angelscript/source/as_callfunc.cpp +++ b/lib/angelscript/source/as_callfunc.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -45,6 +45,14 @@ BEGIN_AS_NAMESPACE +// ref: Member Function Pointers and the Fastest Possible C++ Delegates +// describes the structure of class method pointers for most compilers +// http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible + +// ref: The code comments for ItaniumCXXABI::EmitLoadOfMemberFunctionPointer in the LLVM compiler +// describes the structure for class method pointers on Itanium and arm64 ABI +// http://clang.llvm.org/doxygen/CodeGen_2ItaniumCXXABI_8cpp_source.html#l00937 + int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, void *objForThiscall, asSSystemFunctionInterface *internal) { memset(internal, 0, sizeof(asSSystemFunctionInterface)); @@ -57,9 +65,9 @@ int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, { if( ptr.flag == 1 && callConv != asCALL_GENERIC ) return asWRONG_CALLING_CONV; - else if( ptr.flag == 2 && (callConv == asCALL_GENERIC || callConv == asCALL_THISCALL || callConv == asCALL_THISCALL_ASGLOBAL) ) + else if( ptr.flag == 2 && (callConv == asCALL_GENERIC || callConv == asCALL_THISCALL || callConv == asCALL_THISCALL_ASGLOBAL || callConv == asCALL_THISCALL_OBJFIRST || callConv == asCALL_THISCALL_OBJLAST) ) return asWRONG_CALLING_CONV; - else if( ptr.flag == 3 && !(callConv == asCALL_THISCALL || callConv == asCALL_THISCALL_ASGLOBAL) ) + else if( ptr.flag == 3 && !(callConv == asCALL_THISCALL || callConv == asCALL_THISCALL_ASGLOBAL || callConv == asCALL_THISCALL_OBJFIRST || callConv == asCALL_THISCALL_OBJLAST) ) return asWRONG_CALLING_CONV; } @@ -90,20 +98,44 @@ int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, if( isMethod ) { #ifndef AS_NO_CLASS_METHODS - if( base == asCALL_THISCALL ) + if( base == asCALL_THISCALL || base == asCALL_THISCALL_OBJFIRST || base == asCALL_THISCALL_OBJLAST ) { - internal->callConv = ICC_THISCALL; + internalCallConv thisCallConv; + if( base == asCALL_THISCALL ) + { + if( callConv != asCALL_THISCALL_ASGLOBAL && objForThiscall ) + return asINVALID_ARG; + + thisCallConv = ICC_THISCALL; + } + else + { +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + return asNOT_SUPPORTED; +#else + if( objForThiscall == 0 ) + return asINVALID_ARG; + + internal->objForThiscall = objForThiscall; + if( base == asCALL_THISCALL_OBJFIRST ) + thisCallConv = ICC_THISCALL_OBJFIRST; + else //if( base == asCALL_THISCALL_OBJLAST ) + thisCallConv = ICC_THISCALL_OBJLAST; +#endif + } + + internal->callConv = thisCallConv; #ifdef GNU_STYLE_VIRTUAL_METHOD if( (size_t(ptr.ptr.f.func) & 1) ) - internal->callConv = ICC_VIRTUAL_THISCALL; + internal->callConv = (internalCallConv)(thisCallConv + 2); #endif internal->baseOffset = ( int )MULTI_BASE_OFFSET(ptr); -#if defined(AS_ARM) && defined(__GNUC__) +#if defined(AS_ARM) && (defined(__GNUC__) || defined(AS_PSVITA)) // As the least significant bit in func is used to switch to THUMB mode // on ARM processors, the LSB in the __delta variable is used instead of // the one in __pfn on ARM processors. if( (size_t(internal->baseOffset) & 1) ) - internal->callConv = ICC_VIRTUAL_THISCALL; + internal->callConv = (internalCallConv)(thisCallConv + 2); #endif #ifdef HAVE_VIRTUAL_BASE_OFFSET @@ -135,6 +167,49 @@ int PrepareSystemFunctionGeneric(asCScriptFunction *func, asSSystemFunctionInter // Calculate the size needed for the parameters internal->paramSize = func->GetSpaceNeededForArguments(); + // Prepare the clean up instructions for the function arguments + internal->cleanArgs.SetLength(0); + int offset = 0; + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + asCDataType &dt = func->parameterTypes[n]; + + if( dt.IsObject() && !dt.IsReference() ) + { + asSTypeBehaviour *beh = &dt.GetObjectType()->beh; + if( dt.GetObjectType()->flags & asOBJ_REF ) + { + asASSERT( (dt.GetObjectType()->flags & asOBJ_NOCOUNT) || beh->release ); + if( beh->release ) + { + asSSystemFunctionInterface::SClean clean; + clean.op = 0; // call release + clean.ot = dt.GetObjectType(); + clean.off = short(offset); + internal->cleanArgs.PushLast(clean); + } + } + else + { + asSSystemFunctionInterface::SClean clean; + clean.op = 1; // call free + clean.ot = dt.GetObjectType(); + clean.off = short(offset); + + // Call the destructor then free the memory + if( beh->destruct ) + clean.op = 2; // call destruct, then free + + internal->cleanArgs.PushLast(clean); + } + } + + if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() ) + offset += AS_PTR_SIZE; + else + offset += dt.GetSizeOnStackDWords(); + } + return 0; } @@ -142,11 +217,14 @@ int PrepareSystemFunctionGeneric(asCScriptFunction *func, asSSystemFunctionInter int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine) { #ifdef AS_MAX_PORTABILITY + UNUSED_VAR(func); + UNUSED_VAR(internal); + UNUSED_VAR(engine); + // This should never happen, as when AS_MAX_PORTABILITY is on, all functions // are asCALL_GENERIC, which are prepared by PrepareSystemFunctionGeneric asASSERT(false); -#endif - +#else // References are always returned as primitive data if( func->returnType.IsReference() || func->returnType.IsObjectHandle() ) { @@ -162,7 +240,7 @@ int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *i // Only value types can be returned by value asASSERT( objType & asOBJ_VALUE ); - if( !(objType & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT)) ) + if( !(objType & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_ARRAY)) ) { // If the return is by value then we need to know the true type engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); @@ -172,6 +250,13 @@ int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *i engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0); } + else if( objType & asOBJ_APP_ARRAY ) + { + // Array types are always returned in memory + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + internal->hostReturnFloat = false; + } else if( objType & asOBJ_APP_CLASS ) { internal->hostReturnFloat = false; @@ -203,7 +288,13 @@ int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *i #ifdef THISCALL_RETURN_SIMPLE_IN_MEMORY if((internal->callConv == ICC_THISCALL || +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD internal->callConv == ICC_VIRTUAL_THISCALL) && +#else + internal->callConv == ICC_VIRTUAL_THISCALL || + internal->callConv == ICC_THISCALL_OBJFIRST || + internal->callConv == ICC_THISCALL_OBJLAST) && +#endif func->returnType.GetSizeInMemoryDWords() >= THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE) { internal->hostReturnInMemory = true; @@ -242,7 +333,7 @@ int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *i engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); asCString str; - str.Format(TXT_DONT_SUPPORT_RET_TYPE_s_BY_VAL, func->returnType.Format().AddressOf()); + str.Format(TXT_DONT_SUPPORT_RET_TYPE_s_BY_VAL, func->returnType.Format(func->nameSpace).AddressOf()); engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0); } @@ -313,7 +404,7 @@ int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *i internal->takesObjByVal = true; // Can't pass objects by value unless the application type is informed - if( !(func->parameterTypes[n].GetObjectType()->flags & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT)) ) + if( !(func->parameterTypes[n].GetObjectType()->flags & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_ARRAY)) ) { engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); @@ -350,29 +441,70 @@ int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *i } } - // Verify if the function has any registered autohandles - internal->hasAutoHandles = false; - for( n = 0; n < internal->paramAutoHandles.GetLength(); n++ ) + // Prepare the clean up instructions for the function arguments + internal->cleanArgs.SetLength(0); + int offset = 0; + for( n = 0; n < func->parameterTypes.GetLength(); n++ ) { - if( internal->paramAutoHandles[n] ) - { - internal->hasAutoHandles = true; - break; - } - } + asCDataType &dt = func->parameterTypes[n]; +#if defined(COMPLEX_OBJS_PASSED_BY_REF) || defined(AS_LARGE_OBJS_PASSED_BY_REF) + bool needFree = false; +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( dt.GetObjectType() && dt.GetObjectType()->flags & COMPLEX_MASK ) needFree = true; +#endif +#ifdef AS_LARGE_OBJS_PASSED_BY_REF + if( dt.GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE ) needFree = true; +#endif + if( needFree && + dt.IsObject() && + !dt.IsObjectHandle() && + !dt.IsReference() ) + { + asSSystemFunctionInterface::SClean clean; + clean.op = 1; // call free + clean.ot = dt.GetObjectType(); + clean.off = short(offset); + +#ifndef AS_CALLEE_DESTROY_OBJ_BY_VAL + // If the called function doesn't destroy objects passed by value we must do so here + asSTypeBehaviour *beh = &dt.GetObjectType()->beh; + if( beh->destruct ) + clean.op = 2; // call destruct, then free +#endif + + internal->cleanArgs.PushLast(clean); + } +#endif + + if( n < internal->paramAutoHandles.GetLength() && internal->paramAutoHandles[n] ) + { + asSSystemFunctionInterface::SClean clean; + clean.op = 0; // call release + clean.ot = dt.GetObjectType(); + clean.off = short(offset); + internal->cleanArgs.PushLast(clean); + } + + if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() ) + offset += AS_PTR_SIZE; + else + offset += dt.GetSizeOnStackDWords(); + } +#endif // !defined(AS_MAX_PORTABILITY) return 0; } #ifdef AS_MAX_PORTABILITY -int CallSystemFunction(int id, asCContext *context, void *objectPointer) +int CallSystemFunction(int id, asCContext *context) { asCScriptEngine *engine = context->m_engine; - asSSystemFunctionInterface *sysFunc = engine->scriptFunctions[id]->sysFuncIntf; + asCScriptFunction *func = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = func->sysFuncIntf; int callConv = sysFunc->callConv; if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) - return context->CallGeneric(id, objectPointer); + return context->CallGeneric(func); context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); @@ -396,15 +528,15 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) // args - This is the function arguments, which are packed as in AngelScript // retPointer - This points to a the memory buffer where the return object is to be placed, if the function returns the value in memory rather than in registers // retQW2 - This output parameter should be used if the function returns a value larger than 64bits in registers +// secondObj - This is the object pointer that the proxy method should invoke its method on when the call convention is THISCALL_OBJFIRST/LAST // // Return value: // // The function should return the value that is returned in registers. -// -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2); +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObj); -int CallSystemFunction(int id, asCContext *context, void *objectPointer) +int CallSystemFunction(int id, asCContext *context) { asCScriptEngine *engine = context->m_engine; asCScriptFunction *descr = engine->scriptFunctions[id]; @@ -412,26 +544,24 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) int callConv = sysFunc->callConv; if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) - return context->CallGeneric(id, objectPointer); + return context->CallGeneric(descr); asQWORD retQW = 0; asQWORD retQW2 = 0; asDWORD *args = context->m_regs.stackPointer; void *retPointer = 0; - void *obj = 0; int popSize = sysFunc->paramSize; +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + void *obj = 0; + void *secondObj = 0; + if( callConv >= ICC_THISCALL ) { if( sysFunc->objForThiscall ) { // This class method is being called as if it is a global function obj = sysFunc->objForThiscall; - asASSERT( objectPointer == 0 ); - } - else if( objectPointer ) - { - obj = objectPointer; } else { @@ -447,7 +577,7 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) } // Add the base offset for multiple inheritance -#if defined(__GNUC__) && defined(AS_ARM) +#if (defined(__GNUC__) && defined(AS_ARM)) || defined(AS_PSVITA) // On GNUC + ARM the lsb of the offset is used to indicate a virtual function // and the whole offset is thus shifted one bit left to keep the original // offset resolution @@ -460,7 +590,73 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) args += AS_PTR_SIZE; } } - +#else + // TODO: clean-up: CallSystemFunctionNative should have two arguments for object pointers + // objForThiscall is the object pointer that should be used for the thiscall + // objForArg is the object pointer that should be passed as argument when using OBJFIRST or OBJLAST + + // Used to save two object pointers with THISCALL_OBJLAST or THISCALL_OBJFIRST + void *obj = 0; + void *secondObj = 0; + + if( callConv >= ICC_THISCALL ) + { + bool continueCheck = true; // True if need check objectPointer or context stack for object + int continueCheckIndex = 0; // Index into objectsPtrs to save the object if continueCheck + + if( callConv >= ICC_THISCALL_OBJLAST ) + { + asASSERT( sysFunc->objForThiscall != 0 ); + // This class method is being called as object method (sysFunc->objForThiscall must be set). + obj = sysFunc->objForThiscall; + continueCheckIndex = 1; + } + else if( sysFunc->objForThiscall ) + { + // This class method is being called as if it is a global function + obj = sysFunc->objForThiscall; + continueCheck = false; + } + + if( continueCheck ) + { + void *tempPtr = 0; + + // The object pointer should be popped from the context stack + popSize += AS_PTR_SIZE; + + // Check for null pointer + tempPtr = (void*)*(asPWORD*)(args); + if( tempPtr == 0 ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + return 0; + } + + // Add the base offset for multiple inheritance +#if (defined(__GNUC__) && defined(AS_ARM)) || defined(AS_PSVITA) + // On GNUC + ARM the lsb of the offset is used to indicate a virtual function + // and the whole offset is thus shifted one bit left to keep the original + // offset resolution + tempPtr = (void*)(asPWORD(tempPtr) + (sysFunc->baseOffset>>1)); +#else + tempPtr = (void*)(asPWORD(tempPtr) + sysFunc->baseOffset); +#endif + + // Skip the object pointer + args += AS_PTR_SIZE; + + if( continueCheckIndex ) + secondObj = tempPtr; + else + { + asASSERT( obj == 0 ); + obj = tempPtr; + } + } + } +#endif // AS_NO_THISCALL_FUNCTOR_METHOD + if( descr->DoesReturnOnStack() ) { // Get the address of the location for the return value from the stack @@ -479,8 +675,9 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) } context->m_callingSystemFunction = descr; + bool cppException = false; #ifdef AS_NO_EXCEPTIONS - retQW = CallSystemFunctionNative(context, descr, obj, args, sysFunc->hostReturnInMemory ? retPointer : 0, retQW2); + retQW = CallSystemFunctionNative(context, descr, obj, args, sysFunc->hostReturnInMemory ? retPointer : 0, retQW2, secondObj); #else // This try/catch block is to catch potential exception that may // be thrown by the registered function. The implementation of the @@ -489,10 +686,12 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) // executed in case of an exception. try { - retQW = CallSystemFunctionNative(context, descr, obj, args, sysFunc->hostReturnInMemory ? retPointer : 0, retQW2); + retQW = CallSystemFunctionNative(context, descr, obj, args, sysFunc->hostReturnInMemory ? retPointer : 0, retQW2, secondObj); } catch(...) { + cppException = true; + // Convert the exception to a script exception so the VM can // properly report the error to the application and then clean up context->SetException(TXT_EXCEPTION_CAUGHT); @@ -500,51 +699,6 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) #endif context->m_callingSystemFunction = 0; -#if defined(COMPLEX_OBJS_PASSED_BY_REF) || defined(AS_LARGE_OBJS_PASSED_BY_REF) - if( sysFunc->takesObjByVal ) - { - // Need to free the complex objects passed by value, but that the - // calling convention implicitly passes by reference behind the scene as the - // calling function is the owner of that memory. - - // args is pointing to the first real argument as used in CallSystemFunctionNative, - // i.e. hidden arguments such as the object pointer and return address have already - // been skipped. - - int spos = 0; - for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) - { - bool needFree = false; - asCDataType &dt = descr->parameterTypes[n]; -#ifdef COMPLEX_OBJS_PASSED_BY_REF - if( dt.GetObjectType() && dt.GetObjectType()->flags & COMPLEX_MASK ) needFree = true; -#endif -#ifdef AS_LARGE_OBJS_PASSED_BY_REF - if( dt.GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE ) needFree = true; -#endif - if( needFree && - dt.IsObject() && - !dt.IsObjectHandle() && - !dt.IsReference() ) - { - void *obj = (void*)*(asPWORD*)&args[spos]; - spos += AS_PTR_SIZE; - -#ifndef AS_CALLEE_DESTROY_OBJ_BY_VAL - // If the called function doesn't destroy objects passed by value we must do so here - asSTypeBehaviour *beh = &dt.GetObjectType()->beh; - if( beh->destruct ) - engine->CallObjectMethod(obj, beh->destruct); -#endif - - engine->CallFree(obj); - } - else - spos += dt.GetSizeOnStackDWords(); - } - } -#endif - // Store the returned value in our stack if( descr->returnType.IsObject() && !descr->returnType.IsReference() ) { @@ -595,7 +749,7 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) } } - if( context->m_status == asEXECUTION_EXCEPTION ) + if( context->m_status == asEXECUTION_EXCEPTION && !cppException ) { // If the function raised a script exception it really shouldn't have // initialized the object. However, as it is a soft exception there is @@ -663,27 +817,36 @@ int CallSystemFunction(int id, asCContext *context, void *objectPointer) context->m_regs.valueRegister = retQW; } - // Release autohandles in the arguments - if( sysFunc->hasAutoHandles ) + // Clean up arguments + const asUINT cleanCount = sysFunc->cleanArgs.GetLength(); + if( cleanCount ) { args = context->m_regs.stackPointer; - if( callConv >= ICC_THISCALL && !objectPointer ) + if( callConv >= ICC_THISCALL ) args += AS_PTR_SIZE; - int spos = 0; - for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf(); + for( asUINT n = 0; n < cleanCount; n++, clean++ ) { - if( sysFunc->paramAutoHandles[n] && *(asPWORD*)&args[spos] != 0 ) + void **addr = (void**)&args[clean->off]; + if( clean->op == 0 ) { - // Call the release method on the type - engine->CallObjectMethod((void*)*(asPWORD*)&args[spos], descr->parameterTypes[n].GetObjectType()->beh.release); - *(asPWORD*)&args[spos] = 0; + if( *addr != 0 ) + { + engine->CallObjectMethod(*addr, clean->ot->beh.release); + *addr = 0; + } } + else + { + asASSERT( clean->op == 1 || clean->op == 2 ); + asASSERT( *addr ); - if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) - spos += AS_PTR_SIZE; - else - spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + if( clean->op == 2 ) + engine->CallObjectMethod(*addr, clean->ot->beh.destruct); + + engine->CallFree(*addr); + } } } diff --git a/lib/angelscript/source/as_callfunc.h b/lib/angelscript/source/as_callfunc.h index f15ecdbdc..dde7f72d7 100644 --- a/lib/angelscript/source/as_callfunc.h +++ b/lib/angelscript/source/as_callfunc.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -46,6 +46,7 @@ BEGIN_AS_NAMESPACE class asCContext; class asCScriptEngine; class asCScriptFunction; +class asCObjectType; struct asSSystemFunctionInterface; int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, void *objForThiscall, asSSystemFunctionInterface *internal); @@ -54,7 +55,7 @@ int PrepareSystemFunctionGeneric(asCScriptFunction *func, asSSystemFunctionInter int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine); -int CallSystemFunction(int id, asCContext *context, void *objectPointer); +int CallSystemFunction(int id, asCContext *context); inline asPWORD FuncPtrToUInt(asFUNCTION_t func) { @@ -83,7 +84,15 @@ enum internalCallConv ICC_CDECL_OBJFIRST, ICC_CDECL_OBJFIRST_RETURNINMEM, ICC_GENERIC_METHOD, - ICC_GENERIC_METHOD_RETURNINMEM // never used + ICC_GENERIC_METHOD_RETURNINMEM, // never used + ICC_THISCALL_OBJLAST, + ICC_THISCALL_OBJLAST_RETURNINMEM, + ICC_VIRTUAL_THISCALL_OBJLAST, + ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM, + ICC_THISCALL_OBJFIRST, + ICC_THISCALL_OBJFIRST_RETURNINMEM, + ICC_VIRTUAL_THISCALL_OBJFIRST, + ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM }; struct asSSystemFunctionInterface @@ -97,11 +106,18 @@ struct asSSystemFunctionInterface int hostReturnSize; int paramSize; bool takesObjByVal; - asCArray paramAutoHandles; + asCArray paramAutoHandles; // TODO: Should be able to remove this array. Perhaps the flags can be stored together with the inOutFlags in asCScriptFunction? bool returnAutoHandle; - bool hasAutoHandles; void *objForThiscall; + struct SClean + { + asCObjectType *ot; // argument type for clean up + short op; // clean up operation: 0 = release, 1 = free, 2 = destruct then free + short off; // argument offset on the stack + }; + asCArray cleanArgs; + asSSystemFunctionInterface() {} asSSystemFunctionInterface(const asSSystemFunctionInterface &in) @@ -122,8 +138,8 @@ struct asSSystemFunctionInterface takesObjByVal = in.takesObjByVal; paramAutoHandles = in.paramAutoHandles; returnAutoHandle = in.returnAutoHandle; - hasAutoHandles = in.hasAutoHandles; objForThiscall = in.objForThiscall; + cleanArgs = in.cleanArgs; return *this; } }; diff --git a/lib/angelscript/source/as_callfunc_arm.cpp b/lib/angelscript/source/as_callfunc_arm.cpp index 51e550c33..a5d20355e 100644 --- a/lib/angelscript/source/as_callfunc_arm.cpp +++ b/lib/angelscript/source/as_callfunc_arm.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -37,6 +37,8 @@ // Written by Fredrik Ehnbom in June 2009, based on as_callfunc_x86.cpp // // The code was complemented to support Linux with ARM by Carlos Luna in December, 2012. +// +// Added support for functor methods by Jordi Oliveras Rovira in April, 2014. // This code has to conform to both AAPCS and the modified ABI for iOS @@ -71,7 +73,7 @@ extern "C" asQWORD armFuncR0R1 (const asDWORD *, int, asFUNCTION_t, asDWORD extern "C" asQWORD armFuncObjLast (const asDWORD *, int, asFUNCTION_t, asDWORD obj); extern "C" asQWORD armFuncR0ObjLast (const asDWORD *, int, asFUNCTION_t, asDWORD r0, asDWORD obj); -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) { asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; @@ -87,33 +89,45 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // The return is made in memory callConv++; } + bool isThisCallMethod = callConv >= ICC_THISCALL_OBJLAST; + asDWORD paramBuffer[64+2]; // Android & Linux needs to align 64bit types on even registers, but this isn't done on iOS or Windows Phone // TODO: optimize runtime: There should be a check for this in PrepareSystemFunction() so this // doesn't have to be done for functions that don't have any 64bit types #if !defined(AS_ANDROID) && !defined(AS_LINUX) - if( sysFunc->takesObjByVal ) + // In cases of thiscall methods, the callstack is configured as a standard thiscall + // adding the secondObject as first or last element in callstack + if( sysFunc->takesObjByVal || isThisCallMethod ) #endif { #if defined(AS_ANDROID) || defined(AS_LINUX) // mask is used as a toggler to skip uneven registers. int mask = 1; - // Check for object pointer as first argument - switch( callConv ) + if( isThisCallMethod ) { - case ICC_THISCALL: - case ICC_CDECL_OBJFIRST: - case ICC_VIRTUAL_THISCALL: - case ICC_THISCALL_RETURNINMEM: - case ICC_CDECL_OBJFIRST_RETURNINMEM: - case ICC_VIRTUAL_THISCALL_RETURNINMEM: - mask = 0; - break; - default: - break; + mask = 0; } + else + { + // Check for object pointer as first argument + switch( callConv ) + { + case ICC_THISCALL: + case ICC_CDECL_OBJFIRST: + case ICC_VIRTUAL_THISCALL: + case ICC_THISCALL_RETURNINMEM: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + mask = 0; + break; + default: + break; + } + } + // Check for hidden address in case of return by value if( sysFunc->hostReturnInMemory ) mask = !mask; @@ -122,6 +136,14 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, int spos = 0; int dpos = 2; + if( isThisCallMethod && (callConv >= ICC_THISCALL_OBJFIRST && + callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) + { + // Add the object pointer as the first parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) { // TODO: runtime optimize: Declare a reference to descr->parameterTypes[n] so the array doesn't have to be access all the time @@ -177,6 +199,15 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); } } + + if( isThisCallMethod && (callConv >= ICC_THISCALL_OBJLAST && + callConv <= ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) ) + { + // Add the object pointer as the last parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + // Keep a free location at the beginning args = ¶mBuffer[2]; } @@ -193,9 +224,13 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, break; case ICC_THISCALL: // fall through case ICC_CDECL_OBJFIRST: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJLAST: retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)obj); break; case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST_RETURNINMEM: #ifdef __GNUC__ // On GNUC the address where the return value will be placed should be put in R0 retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); @@ -208,11 +243,15 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); break; case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJLAST: // Get virtual function table from the object pointer vftable = *(asFUNCTION_t**)obj; retQW = armFuncR0(args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2], (asDWORD)obj); break; case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: // Get virtual function table from the object pointer vftable = *(asFUNCTION_t**)obj; #ifdef __GNUC__ @@ -255,7 +294,7 @@ extern "C" asQWORD armFuncR0R1 (const asDWORD *, int, asFUNCTION_t, asDWORD extern "C" asQWORD armFuncObjLast (const asDWORD *, int, asFUNCTION_t, asDWORD obj); extern "C" asQWORD armFuncR0ObjLast (const asDWORD *, int, asFUNCTION_t, asDWORD r0, asDWORD obj); -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) { asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; @@ -307,6 +346,8 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, callConv++; } + bool isThisCallMethod = callConv >= ICC_THISCALL_OBJLAST; + // Linux needs to align 64bit types on even registers, but this isn't done on iOS or Windows Phone // TODO: optimize runtime: There should be a check for this in PrepareSystemFunction() so this // doesn't have to be done for functions that don't have any 64bit types @@ -314,19 +355,26 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // mask is used as a toggler to skip uneven registers. int mask = 1; - // Check for object pointer as first argument - switch( callConv ) + if( isThisCallMethod ) { - case ICC_THISCALL: - case ICC_CDECL_OBJFIRST: - case ICC_VIRTUAL_THISCALL: - case ICC_THISCALL_RETURNINMEM: - case ICC_CDECL_OBJFIRST_RETURNINMEM: - case ICC_VIRTUAL_THISCALL_RETURNINMEM: - mask = 0; - break; - default: - break; + mask = 0; + } + else + { + // Check for object pointer as first argument + switch( callConv ) + { + case ICC_THISCALL: + case ICC_CDECL_OBJFIRST: + case ICC_VIRTUAL_THISCALL: + case ICC_THISCALL_RETURNINMEM: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + mask = 0; + break; + default: + break; + } } // Check for hidden address in case of return by value if( sysFunc->hostReturnInMemory ) @@ -336,10 +384,19 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, int spos = 0; int dpos = 2; + if( isThisCallMethod && (callConv >= ICC_THISCALL_OBJFIRST && + callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) + { + // Add the object pointer as the first parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) { // TODO: runtime optimize: Declare a reference to descr->parameterTypes[n] so the array doesn't have to be access all the time - if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() && + !(descr->parameterTypes[n].GetObjectType()->flags & asOBJ_APP_ARRAY) ) { #ifdef COMPLEX_OBJS_PASSED_BY_REF if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) @@ -501,6 +558,21 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, }// else... }// Loop + if( isThisCallMethod && (callConv >= ICC_THISCALL_OBJLAST && + callConv <= ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) ) + { + if (paramSize < 4) + { + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + else + { + paramBuffer[stackPos++] = (asDWORD)secondObject; + stackSize++; + } + } + // Keep a free location at the beginning args = ¶mBuffer[2]; } @@ -519,9 +591,13 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, break; case ICC_THISCALL: // fall through case ICC_CDECL_OBJFIRST: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJLAST: retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)obj); break; case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST_RETURNINMEM: // On GNUC the address where the return value will be placed should be put in R0 retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); break; @@ -529,11 +605,15 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, retQW = armFuncR0R1(args, paramSize<<2, func, (asDWORD)retPointer, (asDWORD)obj); break; case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJLAST: // Get virtual function table from the object pointer vftable = *(asFUNCTION_t**)obj; retQW = armFuncR0(args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2], (asDWORD)obj); break; case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: // Get virtual function table from the object pointer vftable = *(asFUNCTION_t**)obj; // On GNUC the address where the return value will be placed should be put in R0 diff --git a/lib/angelscript/source/as_callfunc_arm_gcc.S b/lib/angelscript/source/as_callfunc_arm_gcc.S index 126e0b480..38299d453 100644 --- a/lib/angelscript/source/as_callfunc_arm_gcc.S +++ b/lib/angelscript/source/as_callfunc_arm_gcc.S @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -488,6 +488,7 @@ armFuncR0ObjLast: strge r7, [r6, #8] str r5, [r6, #12] /* Put objlast in r6 + 12 */ + mov r5, #0 movge r5, #4 /* Set r5 with an offset of #4 if there´s one last arg that couldn´t be placed in r registers */ add r5, r5, #4 /* Set r5 with an offset of + #4, so objlast can be loaded into the stack */ @@ -687,6 +688,12 @@ nomoreargsarmFuncR0R1: pop {r4-r8, r10, r11, pc} +#endif /* hard float abi */ + +#if defined(__linux__) && defined(__ELF__) +/* ref: http://hardened.gentoo.org/gnu-stack.xml */ +.section .note.GNU-stack,"",%progbits #endif -#endif +#endif /* arm */ + diff --git a/lib/angelscript/source/as_callfunc_arm_msvc.asm b/lib/angelscript/source/as_callfunc_arm_msvc.asm index 05bcddfbb..7ddf99758 100644 --- a/lib/angelscript/source/as_callfunc_arm_msvc.asm +++ b/lib/angelscript/source/as_callfunc_arm_msvc.asm @@ -1,6 +1,6 @@ ; ; AngelCode Scripting Library -; Copyright (c) 2003-2013 Andreas Jonsson +; Copyright (c) 2003-2014 Andreas Jonsson ; ; This software is provided 'as-is', without any express or implied ; warranty. In no event will the authors be held liable for any @@ -29,7 +29,7 @@ ; -; Assembly routines for the ARM call convention +; Assembly routines for the ARM call convention used for Windows CE ; Written by Fredrik Ehnbom in June 2009 ; MSVC currently doesn't support inline assembly for the ARM platform diff --git a/lib/angelscript/source/as_callfunc_arm_vita.S b/lib/angelscript/source/as_callfunc_arm_vita.S new file mode 100644 index 000000000..3281f46f5 --- /dev/null +++ b/lib/angelscript/source/as_callfunc_arm_vita.S @@ -0,0 +1,480 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2013 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +/* + Assembly routines for the Playstation Vita SNC call convention. + + This code was adapted from as_callfunc_arm_gcc (ARM, Linux hard float) by Brandon Bare on October 2014. +*/ + +#ifdef __psp2__ + +.syntax unified +.cpu cortex-a9 +.fpu neon + +.section .text.armCallFunc +.balign 2 +.thumb +.thumb_func + +.align 2 + +.global armFunc +.global armFuncR0 +.global armFuncR0R1 +.global armFuncObjLast +.global armFuncR0ObjLast + +.type armFunc, %function +.type armFuncR0, %function +.type armFuncR0R1, %function +.type armFuncObjLast, %function +.type armFuncR0ObjLast, %function + +/* --------------------------------------------------------------------------------------------*/ +armFunc: + .fnstart + + push {r4-r8, r10, r11, lr} /* sp must be 8-byte alignment for ABI compliance, so the pushed registers must be even */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + /* Load float and double args into d0-d7 and s0-s15 */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargs + + /* Load the first 4 arguments into r0-r3 */ + cmp r7, #4 + + it ge + ldrge r0, [r6] + cmp r7, #8 + + it ge + ldrge r1, [r6, #4] + cmp r7, #12 + + it ge + ldrge r2, [r6, #8] + cmp r7, #16 + + it ge + ldrge r3, [r6, #12] + +stackargs: + ldr r5, [r6, #268] /* Load stack size into r5 */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargs + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargsloop: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargsloop + mov sp, r12 + +nomoreargs: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d7 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + + .fnend + +/* --------------------------------------------------------------------------------------------*/ +armFuncObjLast: + .fnstart + + push {r4-r8, r10, r11, lr} /* We´re storing r11 just to keep the stack aligned to an 8 byte boundary */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + mov r0, r3 /* objlast. might get overwritten */ + mov r5, #0 /* This will hold an offset of #4 only if objlast couldn´t be placed into an "r" register */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsFuncObjLast + + mov r5, r3 /* store objlast in r5 temporarily */ + + /* Load the first 4 arguments into r0-r3 */ + cmp r7, #4 + + it ge + ldrge r0, [r6] + cmp r7, #8 + + it ge + ldrge r1, [r6,#4] + + it lt + movlt r1, r5 + cmp r7, #12 + + it ge + ldrge r2, [r6,#8] + + it lt + movlt r2, r5 + cmp r7, #16 + + it ge + ldrge r3, [r6,#12] + + ittt lt + movlt r3, r5 + movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */ + blt stackargsFuncObjLast /* If objlast got placed into a register, go to stackargsFuncObjLast */ + + str r5, [r6, #12] /* Put objlast in r6 + 12 */ + mov r5, #4 /* Set r5 with an offset of #4, so objlast can be loaded into the stack */ + +stackargsFuncObjLast: + ldr r7, [r6, #268] /* Load stack size into r7 */ + add r7, r7, r5 /* Add the offset placed in r5 (could be #0 or #4) */ + cmp r7, #0 /* Check for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncObjLast + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r5 /* r6 = r6 - r5 (r5 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncObjLast: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncObjLast + mov sp, r12 + +nomoreargsarmFuncObjLast: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10,r11, pc} + + .fnend + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0ObjLast: + .fnstart + + push {r4-r8, r10, r11, lr} + + ldr r5, [sp,#32] /* objlast to temp reg */ + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + + mov r0, r3 /* r0 explicitly set */ + mov r1, r5 /* objlast. might get overwritten */ + mov r5, #0 /* This will hold an offset of #4 or #8 if objlast or one arg couldn´t be placed into an "r" register */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsFuncR0ObjLast + + mov r5, r1 /* store objlast in r5 temporarily */ + + /* Load the first 3 arguments into r1-r3 */ + cmp r7, #4 + + it ge + ldrge r1, [r6] + cmp r7, #8 + + it ge + ldrge r2, [r6,#4] + + it lt + movlt r2, r5 + cmp r7, #12 + + it ge + ldrge r3, [r6,#8] + + ittt lt + movlt r3, r5 + movlt r5, #0 /* If objlast got placed into a register, r5 = 0 */ + blt stackargsFuncR0ObjLast /* If objlast got placed into a register, go to stackargsFuncR0ObjLast */ + + cmp r7, #16 /* Else if we have one last arg set the offset accordingly and store the arg in the array */ + + itt ge + ldrge r7, [r6, #12] + strge r7, [r6, #8] + + str r5, [r6, #12] /* Put objlast in r6 + 12 */ + mov r5, #0 + + it ge + movge r5, #4 /* Set r5 with an offset of #4 if there´s one last arg that couldn´t be placed in r registers */ + add r5, r5, #4 /* Set r5 with an offset of + #4, so objlast can be loaded into the stack */ + +stackargsFuncR0ObjLast: + ldr r7, [r6, #268] /* Load stack size into r7 */ + add r7, r7, r5 /* Add the offset placed in r5 (could be #0 or #4) */ + cmp r7, #0 /* Check for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0ObjLast + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r5 /* r6 = r6 - r5 (r5 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0ObjLast: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0ObjLast + mov sp, r12 + +nomoreargsarmFuncR0ObjLast: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + + .fnend + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0: + .fnstart + + push {r4-r8, r10, r11, lr} + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r11, #0 /* This will hold an offset of #4 only if the last arg that should have been placed into an "r" reg needs to go to the stack */ + mov r0, r3 /* r0 explicitly set */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r0-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsarmFuncR0 + + /* Load the first 3 arguments into r1-r3 */ + cmp r7, #4 + + it ge + ldrge r1, [r6] + cmp r7, #8 + + it ge + ldrge r2, [r6, #4] + cmp r7, #12 + + it ge + ldrge r3, [r6, #8] + cmp r7, #16 + + it ge + movge r11, #4 /* If there is still one arg to be placed, set the offset in r11 to #4 */ + +stackargsarmFuncR0: + ldr r5, [r6, #268] /* Load stack size into r5 */ + add r5, r11 /* Add the offset placed in r11 (could be #0 or #4) */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0 + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r11 /* r6 = r6 - r11 (r11 can be #0 or #4) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0 + mov sp, r12 + +nomoreargsarmFuncR0: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + + .fnend + +/* --------------------------------------------------------------------------------------------*/ +armFuncR0R1: + .fnstart + + push {r4-r8, r10, r11, lr} + + mov r6, r0 /* arg table */ + movs r7, r1 /* arg size (also set the condition code flags so that we detect if there are no arguments) */ + mov r4, r2 /* function address */ + mov r11, #0 /* This will hold an offset of #4 or #8 only if the last arg (or last 2 args) that should have been placed into "r" regs need to go to the stack */ + + mov r0, r3 /* r0 explicitly set */ + ldr r1, [sp, #32] /* r1 explicitly set too */ + + /* Load float and double args into d0-d7 and s0-s15 (r10 holds pointer to first float value) */ + add r10, r6, #272 /* r10 (r6 + 272) points to the first value for the VFP registers */ + mov r8, #0 + vldmia.64 r10, {d0-d7} /* Load contents starting at r10 into registers d0-d7 */ + + /* If there are no arguments to set into r2-r3 */ + /* go check if there are arguments for the stack */ + beq stackargsarmFuncR0R1 + + /* Load the first 2 arguments into r2-r3 */ + cmp r7, #4 + + it ge + ldrge r2, [r6] + cmp r7, #8 + + it ge + ldrge r3, [r6, #4] + cmp r7, #12 + + it ge + movge r11, #4 /* If there is a third arg to be placed, set the offset in r11 to #4 */ + + cmp r7, #16 + + it ge + movge r11, #8 /* If there is a fourth arg to be placed, set the offset in r11 to #8 */ + + itt lt + ldrlt r7, [r6, #8] /* Else copy the third arg to the correct place in the array */ + strlt r7, [r6, #12] + +stackargsarmFuncR0R1: + ldr r5, [r6, #268] /* Load stack size into r5 */ + add r5, r11 /* Add the offset placed in r11 (could be #0 or #4 or #8) */ + movs r7, r5 /* Load stack size into r7, checking for 0 args */ + + /* If there are no args for the stack, branch */ + beq nomoreargsarmFuncR0R1 + + /* Load the rest of the arguments onto the stack */ + /* Ensure 8-byte stack alignment */ + mov r8, sp + sub sp, sp, r7 + add r6, r6, #16 /* Set r6 to point to the first arg to be placed on the stack */ + + sub r12, sp, #8 + sub r6, r6, r11 /* r6 = r6 - r11 (r11 can be #0 or #4 or #8) */ + bic r12, r12, #7 /* thumb mode couldn't support "bic sp, sp, #7" instruction */ + sub r8, r8, r12 + mov sp, r12 /* copy size != frame size, so store frame start sp, r12(ip) is not callee saved register */ + +stackargslooparmFuncR0R1: + ldr r5, [r6], #4 + subs r7, r7, #4 + str r5, [sp], #4 + bne stackargslooparmFuncR0R1 + mov sp, r12 + +nomoreargsarmFuncR0R1: + blx r4 + add sp, sp, r8 + vstmia.64 r10, {d0-d7} /* Copy contents of registers d0-d10 to the address stored in r10 */ + + pop {r4-r8, r10, r11, pc} + + .fnend + +#endif \ No newline at end of file diff --git a/lib/angelscript/source/as_callfunc_mips.cpp b/lib/angelscript/source/as_callfunc_mips.cpp index 8020847f0..2dc8e0078 100644 --- a/lib/angelscript/source/as_callfunc_mips.cpp +++ b/lib/angelscript/source/as_callfunc_mips.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -221,12 +221,14 @@ asQWORD GetReturnedDouble() return d; } -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) { asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; int callConv = sysFunc->callConv; + // TODO: Mips does not yet support THISCALL_OBJFIRST/LAST + asQWORD retQW = 0; void *func = (void*)sysFunc->func; diff --git a/lib/angelscript/source/as_callfunc_ppc.cpp b/lib/angelscript/source/as_callfunc_ppc.cpp index fff759c16..32a73f8f8 100644 --- a/lib/angelscript/source/as_callfunc_ppc.cpp +++ b/lib/angelscript/source/as_callfunc_ppc.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -482,8 +482,10 @@ static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArg return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); } -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) { + // TODO: PPC does not yet support THISCALL_OBJFIRST/LAST + // use a working array of types, we'll configure the final one in stackArgs asBYTE argsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1]; memset( argsType, 0, sizeof(argsType)); diff --git a/lib/angelscript/source/as_callfunc_ppc_64.cpp b/lib/angelscript/source/as_callfunc_ppc_64.cpp index 141a6d495..a2c62f2b9 100644 --- a/lib/angelscript/source/as_callfunc_ppc_64.cpp +++ b/lib/angelscript/source/as_callfunc_ppc_64.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -547,8 +547,10 @@ inline bool IsVariableArgument( asCDataType type ) return (type.GetTokenType() == ttQuestion) ? true : false; } -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) { + // TODO: PPC 64 does not yet support THISCALL_OBJFIRST/LAST + // use a working array of types, we'll configure the final one in stackArgs asBYTE argsType[AS_PPC_MAX_ARGS + 1 + 1 + 1]; memset( argsType, 0, sizeof(argsType)); @@ -663,7 +665,8 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) { - if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() && + !(descr->parameterTypes[n].GetObjectType()->flags & asOBJ_APP_ARRAY) ) { #ifdef COMPLEX_OBJS_PASSED_BY_REF if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) diff --git a/lib/angelscript/source/as_callfunc_sh4.cpp b/lib/angelscript/source/as_callfunc_sh4.cpp index e22a3dd54..2fb4ff68f 100644 --- a/lib/angelscript/source/as_callfunc_sh4.cpp +++ b/lib/angelscript/source/as_callfunc_sh4.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -270,8 +270,10 @@ asQWORD GetReturnedDouble() return d; } -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) { + // TODO: SH4 does not yet support THISCALL_OBJFIRST/LAST + asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; int callConv = sysFunc->callConv; diff --git a/lib/angelscript/source/as_callfunc_x64_gcc.cpp b/lib/angelscript/source/as_callfunc_x64_gcc.cpp index 056ac1f2e..5e497dec8 100644 --- a/lib/angelscript/source/as_callfunc_x64_gcc.cpp +++ b/lib/angelscript/source/as_callfunc_x64_gcc.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -34,8 +34,14 @@ * Author: Ionut "gargltk" Leonte * * Initial author: niteice + * + * Added support for functor methods by Jordi Oliveras Rovira in April, 2014. */ +// Useful references for the System V AMD64 ABI: +// http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/ +// http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_amd64.pdf + #include "as_config.h" #ifndef AS_MAX_PORTABILITY @@ -73,6 +79,8 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i // Backup stack pointer in R15 that is guaranteed to maintain its value over function calls " movq %%rsp, %%r15 \n" + // Make sure the stack unwind logic knows we've backed up the stack pointer in register r15 + " .cfi_def_cfa_register r15 \n" // Skip the first 128 bytes on the stack frame, called "red zone", // that might be used by the compiler to store temporary values @@ -124,6 +132,8 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i // Restore stack pointer " mov %%r15, %%rsp \n" + // Inform the stack unwind logic that the stack pointer has been restored + " .cfi_def_cfa_register rsp \n" // Put return value in retQW1 and retQW2, using either RAX:RDX or XMM0:XMM1 depending on type of return value " movl %5, %%ecx \n" @@ -152,7 +162,7 @@ static inline bool IsVariableArgument( asCDataType type ) return ( type.GetTokenType() == ttQuestion ) ? true : false; } -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObject) { asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; @@ -172,8 +182,17 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, callConv++; } +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD // Determine the real function pointer in case of virtual method if ( obj && ( callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) ) +#else + if ( obj && ( callConv == ICC_VIRTUAL_THISCALL || + callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) ) +#endif { vftable = *((funcptr_t**)obj); func = vftable[FuncPtrToUInt(asFUNCTION_t(func)) >> 3]; @@ -195,6 +214,11 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, break; } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + case ICC_THISCALL_OBJLAST: + case ICC_VIRTUAL_THISCALL_OBJLAST: + param_post = 2; +#endif case ICC_THISCALL: case ICC_VIRTUAL_THISCALL: case ICC_CDECL_OBJFIRST: @@ -206,6 +230,11 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, break; } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + case ICC_THISCALL_OBJLAST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: + param_post = 2; +#endif case ICC_THISCALL_RETURNINMEM: case ICC_VIRTUAL_THISCALL_RETURNINMEM: case ICC_CDECL_OBJFIRST_RETURNINMEM: @@ -219,7 +248,33 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, break; } - case ICC_CDECL_OBJLAST: +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + case ICC_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + { + paramBuffer[0] = (asPWORD)obj; + paramBuffer[1] = (asPWORD)secondObject; + argsType[0] = x64INTARG; + argsType[1] = x64INTARG; + + argIndex = 2; + break; + } + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + { + paramBuffer[0] = (asPWORD)retPointer; + paramBuffer[1] = (asPWORD)obj; + paramBuffer[2] = (asPWORD)secondObject; + argsType[0] = x64INTARG; + argsType[1] = x64INTARG; + argsType[2] = x64INTARG; + + argIndex = 3; + break; + } +#endif + case ICC_CDECL_OBJLAST: param_post = 1; break; case ICC_CDECL_OBJLAST_RETURNINMEM: @@ -337,7 +392,11 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // For the CDECL_OBJ_LAST calling convention we need to add the object pointer as the last argument if( param_post ) { +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD paramBuffer[argIndex] = (asPWORD)obj; +#else + paramBuffer[argIndex] = (asPWORD)(param_post > 1 ? secondObject : obj); +#endif argsType[argIndex] = x64INTARG; argIndex++; } diff --git a/lib/angelscript/source/as_callfunc_x64_mingw.cpp b/lib/angelscript/source/as_callfunc_x64_mingw.cpp index 4b5cb54d3..35e775fe9 100644 --- a/lib/angelscript/source/as_callfunc_x64_mingw.cpp +++ b/lib/angelscript/source/as_callfunc_x64_mingw.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -31,6 +31,8 @@ // // This code was adapted from as_callfunc_x64_msvc by _Vicious_ on August 20th, 2011. // +// Added support for functor methods by Jordi Oliveras Rovira in April, 2014. +// #include @@ -165,7 +167,7 @@ static asQWORD GetReturnedDouble() return ret; } -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) { asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; @@ -189,10 +191,16 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, allArgBuffer[paramSize++] = (asQWORD)retPointer; } +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD if( callConv == ICC_THISCALL || callConv == ICC_THISCALL_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) +#else + // Optimization to avoid check 12 values (all ICC_ that contains THISCALL) + if( (callConv >= ICC_THISCALL && callConv <= ICC_VIRTUAL_THISCALL_RETURNINMEM) || + (callConv >= ICC_THISCALL_OBJLAST && callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) +#endif { // Add the object pointer as the first parameter allArgBuffer[paramSize++] = (asQWORD)obj; @@ -204,8 +212,28 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Add the object pointer as the first parameter allArgBuffer[paramSize++] = (asQWORD)obj; } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + else if( callConv == ICC_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ) + { + // Add the object pointer as the first parameter + allArgBuffer[paramSize++] = (asQWORD)secondObject; + } +#endif + +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD if( callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) +#else + if( callConv == ICC_VIRTUAL_THISCALL || + callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) +#endif { // Get the true function pointer from the virtual function table vftable = *(void***)obj; @@ -287,6 +315,16 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Add the object pointer as the last parameter allArgBuffer[paramSize++] = (asQWORD)obj; } +#ifndef AS_NO_THISCALL_FUNCTOR_METHOD + else if( callConv == ICC_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last parameter + allArgBuffer[paramSize++] = (asQWORD)secondObject; + } +#endif retQW = CallX64(allArgBuffer, floatArgBuffer, paramSize*8, (asPWORD)func); diff --git a/lib/angelscript/source/as_callfunc_x64_msvc.cpp b/lib/angelscript/source/as_callfunc_x64_msvc.cpp index bddd3f7c8..84b96c2dd 100644 --- a/lib/angelscript/source/as_callfunc_x64_msvc.cpp +++ b/lib/angelscript/source/as_callfunc_x64_msvc.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -28,6 +28,9 @@ andreas@angelcode.com */ +// +// Added support for thiscall methods by Jordi Oliveras Rovira in April, 2014. +// #include @@ -48,7 +51,7 @@ extern "C" asQWORD CallX64(const asQWORD *args, const asQWORD *floatArgs, int pa extern "C" asDWORD GetReturnedFloat(); extern "C" asQWORD GetReturnedDouble(); -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) { asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; @@ -62,10 +65,10 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, asQWORD floatArgBuffer[4]; int callConv = sysFunc->callConv; - if( callConv == ICC_THISCALL || - callConv == ICC_THISCALL_RETURNINMEM || - callConv == ICC_VIRTUAL_THISCALL || - callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) + + // Optimization to avoid check 12 values (all ICC_ that contains THISCALL) + if( (callConv >= ICC_THISCALL && callConv <= ICC_VIRTUAL_THISCALL_RETURNINMEM) || + (callConv >= ICC_THISCALL_OBJLAST && callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) { // Add the object pointer as the first parameter allArgBuffer[paramSize++] = (asQWORD)obj; @@ -86,9 +89,21 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Add the object pointer as the first parameter allArgBuffer[paramSize++] = (asQWORD)obj; } + else if( callConv == ICC_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ) + { + // Add the object pointer as the first parameter + allArgBuffer[paramSize++] = (asQWORD)secondObject; + } if( callConv == ICC_VIRTUAL_THISCALL || - callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) + callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) { // Get the true function pointer from the virtual function table vftable = *(void***)obj; @@ -100,10 +115,11 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, asUINT spos = 0; for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) { - if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + asCDataType &dt = descr->parameterTypes[n]; + if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() ) { - if( descr->parameterTypes[n].GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE || - (descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) ) + if( dt.GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE || + (dt.GetObjectType()->flags & COMPLEX_MASK) ) { allArgBuffer[dpos++] = *(asQWORD*)&args[spos]; spos += AS_PTR_SIZE; @@ -112,18 +128,18 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, else { // Copy the object's memory to the buffer - memcpy(&allArgBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + memcpy(&allArgBuffer[dpos], *(void**)(args+spos), dt.GetSizeInMemoryBytes()); // Delete the original memory engine->CallFree(*(char**)(args+spos)); spos += AS_PTR_SIZE; - asUINT dwords = descr->parameterTypes[n].GetSizeInMemoryDWords(); + asUINT dwords = dt.GetSizeInMemoryDWords(); asUINT qwords = (dwords >> 1) + (dwords & 1); dpos += qwords; paramSize += qwords; } } - else if( descr->parameterTypes[n].GetTokenType() == ttQuestion ) + else if( dt.GetTokenType() == ttQuestion ) { // Copy the reference and the type id allArgBuffer[dpos++] = *(asQWORD*)&args[spos]; @@ -134,14 +150,14 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, else { // Copy the value directly - asUINT dwords = descr->parameterTypes[n].GetSizeOnStackDWords(); + asUINT dwords = dt.GetSizeOnStackDWords(); if( dwords > 1 ) { allArgBuffer[dpos] = *(asQWORD*)&args[spos]; // Double arguments are moved to a separate buffer in order to be placed in the XMM registers, // though this is only done for first 4 arguments, the rest are placed on the stack - if( paramSize < 4 && descr->parameterTypes[n].IsDoubleType() ) + if( paramSize < 4 && dt.IsDoubleType() ) floatArgBuffer[dpos] = *(asQWORD*)&args[spos]; dpos++; @@ -153,7 +169,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Float arguments are moved to a separate buffer in order to be placed in the XMM registers, // though this is only done for first 4 arguments, the rest are placed on the stack - if( paramSize < 4 && descr->parameterTypes[n].IsFloatType() ) + if( paramSize < 4 && dt.IsFloatType() ) floatArgBuffer[dpos] = args[spos]; dpos++; @@ -170,6 +186,14 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Add the object pointer as the last parameter allArgBuffer[paramSize++] = (asQWORD)obj; } + else if( callConv == ICC_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last parameter + allArgBuffer[paramSize++] = (asQWORD)secondObject; + } retQW = CallX64(allArgBuffer, floatArgBuffer, paramSize*8, (asPWORD)func); diff --git a/lib/angelscript/source/as_callfunc_x86.cpp b/lib/angelscript/source/as_callfunc_x86.cpp index 3c551cfcf..21c5f40df 100644 --- a/lib/angelscript/source/as_callfunc_x86.cpp +++ b/lib/angelscript/source/as_callfunc_x86.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -34,6 +34,8 @@ // // These functions handle the actual calling of system functions // +// Added support for functor methods by Jordi Oliveras Rovira in April, 2014. +// @@ -87,7 +89,7 @@ asQWORD CallThisCallFunctionRetByRef(const void *, const asDWORD *, int, asFUNCT asDWORD GetReturnedFloat(); asQWORD GetReturnedDouble(); -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject) { asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; @@ -95,13 +97,30 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, asQWORD retQW = 0; // Prepare the parameters - int paramSize = sysFunc->paramSize; asDWORD paramBuffer[64]; - if( sysFunc->takesObjByVal ) + int callConv = sysFunc->callConv; + + // Changed because need check for ICC_THISCALL_OBJFIRST or + // ICC_THISCALL_OBJLAST if sysFunc->takesObjByVal (avoid copy code) + // Check if is THISCALL_OBJ* calling convention (in this case needs to add secondObject pointer into stack). + bool isThisCallMethod = callConv >= ICC_THISCALL_OBJLAST; + int paramSize = isThisCallMethod || sysFunc->takesObjByVal ? 0 : sysFunc->paramSize; + + int dpos = 1; + + if( isThisCallMethod && + (callConv >= ICC_THISCALL_OBJFIRST && + callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) ) + { + // Add the object pointer as the first parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + + if( sysFunc->takesObjByVal || isThisCallMethod ) { - paramSize = 0; int spos = 0; - int dpos = 1; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) { if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) @@ -146,9 +165,17 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, args = ¶mBuffer[1]; } + if( isThisCallMethod && + (callConv >= ICC_THISCALL_OBJLAST && + callConv <= ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) ) + { + // Add the object pointer as the last parameter + paramBuffer[dpos++] = (asDWORD)secondObject; + paramSize++; + } + // Make the actual call asFUNCTION_t func = sysFunc->func; - int callConv = sysFunc->callConv; if( sysFunc->hostReturnInMemory ) callConv++; @@ -176,14 +203,20 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, break; case ICC_THISCALL: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJLAST: retQW = CallThisCallFunction(obj, args, paramSize<<2, func); break; case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST_RETURNINMEM: retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, func, retPointer); break; case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJLAST: { // Get virtual function table from the object pointer asFUNCTION_t *vftable = *(asFUNCTION_t**)obj; @@ -192,6 +225,8 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, break; case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: { // Get virtual function table from the object pointer asFUNCTION_t *vftable = *(asFUNCTION_t**)obj; diff --git a/lib/angelscript/source/as_callfunc_xenon.cpp b/lib/angelscript/source/as_callfunc_xenon.cpp index c4f0394f3..34228216c 100644 --- a/lib/angelscript/source/as_callfunc_xenon.cpp +++ b/lib/angelscript/source/as_callfunc_xenon.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -498,8 +498,10 @@ inline bool IsVariableArgument( asCDataType type ) return (type.GetTokenType() == ttQuestion) ? true : false; } -asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/) +asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/) { + // TODO: Xenon does not yet support THISCALL_OBJFIRST/LAST + asCScriptEngine *engine = context->m_engine; asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; int callConv = sysFunc->callConv; diff --git a/lib/angelscript/source/as_compiler.cpp b/lib/angelscript/source/as_compiler.cpp index 8df4c83c3..62b67315d 100644 --- a/lib/angelscript/source/as_compiler.cpp +++ b/lib/angelscript/source/as_compiler.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -194,7 +194,7 @@ int asCCompiler::CompileFactory(asCBuilder *builder, asCScriptCode *script, asCS } } - // Allocate the class and instanciate it with the constructor + // Allocate the class and instantiate it with the constructor int varOffset = AllocateVariable(dt, true); outFunc->scriptData->variableSpace = AS_PTR_SIZE; @@ -340,12 +340,12 @@ int asCCompiler::SetupParametersAndReturnVariable(asCArray ¶meter } // Is the return type allowed? - if( (!returnType.CanBeInstanciated() && returnType != asCDataType::CreatePrimitive(ttVoid, false)) || - (returnType.IsReference() && !returnType.CanBeInstanciated()) ) + if( returnType != asCDataType::CreatePrimitive(ttVoid, false) && + !returnType.CanBeInstantiated() ) { // TODO: Hasn't this been validated by the builder already? asCString str; - str.Format(TXT_RETURN_CANT_BE_s, returnType.Format().AddressOf()); + str.Format(TXT_RETURN_CANT_BE_s, returnType.Format(outFunc->nameSpace).AddressOf()); Error(str, func); } @@ -367,10 +367,10 @@ int asCCompiler::SetupParametersAndReturnVariable(asCArray ¶meter // Is the data type allowed? // TODO: Hasn't this been validated by the builder already? - if( (type.IsReference() && inoutFlag != asTM_INOUTREF && !type.CanBeInstanciated()) || - (!type.IsReference() && !type.CanBeInstanciated()) ) + if( (type.IsReference() && inoutFlag != asTM_INOUTREF && !type.CanBeInstantiated()) || + (!type.IsReference() && !type.CanBeInstantiated()) ) { - asCString parm = type.Format(); + asCString parm = type.Format(outFunc->nameSpace); if( inoutFlag == asTM_INREF ) parm += "in"; else if( inoutFlag == asTM_OUTREF ) @@ -502,6 +502,7 @@ void asCCompiler::CompileMemberInitialization(asCByteCode *byteCode, bool onlyDe asQWORD constantValue; asCByteCode bc(engine); CompileInitialization(initNode, &bc, prop->type, declNode, prop->byteOffset, &constantValue, 2); + bc.OptimizeLocally(tempVariableOffsets); byteCode->AddCode(&bc); script = origScript; @@ -608,22 +609,6 @@ int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asC CompileMemberInitialization(&byteCode, false); } } - - // Increase the reference for the object pointer, so that it is guaranteed to live during the entire call - if( !m_isConstructor && !outFunc->returnType.IsReference() ) - { - // TODO: runtime optimize: If the function is trivial, i.e. doesn't access any outside functions, - // then this is not necessary. If I implement this, then the function needs - // to set a flag so the exception handler doesn't try to release the handle. - // It is not necessary to do this for constructors, as they have no outside references that can be released anyway - // It is not necessary to do this for methods that return references, as the caller is guaranteed to hold a reference to the object - asCByteCode tmpBC(engine); - tmpBC.InstrSHORT(asBC_PSF, 0); - tmpBC.Instr(asBC_RDSPtr); - tmpBC.Call(asBC_CALLSYS, outFunc->objectType->beh.addref, AS_PTR_SIZE); - tmpBC.OptimizeLocally(tempVariableOffsets); - byteCode.AddCode(&tmpBC); - } } // Add the code for the statement block @@ -666,10 +651,6 @@ int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asC // Do not deallocate parameters } - // Release the object pointer again - if( outFunc->objectType && !m_isConstructor && !outFunc->returnType.IsReference() ) - byteCode.InstrW_PTR(asBC_FREE, 0, outFunc->objectType); - // Check if the number of labels in the functions isn't too many to be handled if( nextLabel >= (1<<15) ) Error(TXT_TOO_MANY_JUMP_LABELS, func); @@ -808,7 +789,7 @@ int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, bool isObjec return -1; } -int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem, bool derefDest) +int asCCompiler::CallDefaultConstructor(const asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem, bool derefDest) { if( !type.IsObject() || type.IsObjectHandle() ) return 0; @@ -820,7 +801,7 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb int func = 0; asSTypeBehaviour *beh = type.GetBehaviour(); - if( beh ) + if( beh ) { func = beh->factory; @@ -847,7 +828,7 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb if( f->parameterTypes.GetLength() ) { // Add the default values for arguments not explicitly supplied - CompileDefaultArgs(node, args, f); + CompileDefaultAndNamedArgs(node, args, func, type.GetObjectType()); PrepareFunctionCall(func, &ctx.bc, args); @@ -927,7 +908,7 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb asSTypeBehaviour *beh = type.GetBehaviour(); int func = 0; - if( beh ) + if( beh ) { func = beh->construct; @@ -955,7 +936,7 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb if( f && f->parameterTypes.GetLength() ) { // Add the default values for arguments not explicitly supplied - CompileDefaultArgs(node, args, f); + CompileDefaultAndNamedArgs(node, args, func, type.GetObjectType()); PrepareFunctionCall(func, &ctx.bc, args); @@ -1001,7 +982,7 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb bc->AddCode(&ctx.bc); } } - else + else { asASSERT( false ); } @@ -1019,7 +1000,31 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb bc->InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); } - bc->Alloc(asBC_ALLOC, type.GetObjectType(), func, AS_PTR_SIZE); + if( (type.GetObjectType()->flags & asOBJ_TEMPLATE) ) + { + asCScriptFunction *descr = engine->scriptFunctions[func]; + asASSERT( descr->funcType == asFUNC_SCRIPT ); + + // Find the id of the real constructor and not the generated stub + asUINT id = 0; + asDWORD *funcBc = descr->scriptData->byteCode.AddressOf(); + while( funcBc ) + { + if( (*(asBYTE*)funcBc) == asBC_CALLSYS ) + { + id = asBC_INTARG(funcBc); + break; + } + funcBc += asBCTypeSize[asBCInfo[*(asBYTE*)funcBc].type]; + } + + asASSERT( id ); + + bc->InstrPTR(asBC_OBJTYPE, type.GetObjectType()); + bc->Alloc(asBC_ALLOC, type.GetObjectType(), id, AS_PTR_SIZE + AS_PTR_SIZE); + } + else + bc->Alloc(asBC_ALLOC, type.GetObjectType(), func, AS_PTR_SIZE); } // Cleanup @@ -1052,6 +1057,10 @@ void asCCompiler::CallDestructor(asCDataType &type, int offset, bool isObjectOnH // Call destructor for the data type if( type.IsObject() ) { + // The null pointer doesn't need to be destroyed + if( type.IsNullHandle() ) + return; + // Nothing is done for list pattern types, as this is taken care of by the CompileInitList method if( type.GetObjectType()->flags & asOBJ_LIST_PATTERN ) return; @@ -1106,6 +1115,13 @@ void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableSc asCScriptNode *node = block->firstChild; while( node ) { +#ifdef AS_DEBUG + // Keep the current line in a variable so it will be easier + // to determine where in a script an assert is occurring. + int currentLine = 0; + script->ConvertPosToRowCol(node->tokenPos, ¤tLine, 0); +#endif + if( !hasUnreachableCode && (*hasReturn || isFinished) ) { // Empty statements don't count @@ -1188,10 +1204,20 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip node = parser.GetScriptNode(); } + asSExprContext compiledCtx(engine); + bool preCompiled = false; + if( gvar->datatype.IsAuto() ) + preCompiled = CompileAutoType(gvar->datatype, compiledCtx, node, gvar->declaredAtNode); + if( gvar->property == 0 ) + { + gvar->property = builder->module->AllocateGlobalProperty(gvar->name.AddressOf(), gvar->datatype, gvar->ns); + gvar->index = gvar->property->id; + } + // Compile the expression asSExprContext ctx(engine); - asQWORD constantValue; - if( CompileInitialization(node, &ctx.bc, gvar->datatype, gvar->declaredAtNode, gvar->index, &constantValue, 1) ) + asQWORD constantValue = 0; + if( CompileInitialization(node, &ctx.bc, gvar->datatype, gvar->declaredAtNode, gvar->index, &constantValue, 1, preCompiled ? &compiledCtx : 0) ) { // Should the variable be marked as pure constant? if( gvar->datatype.IsPrimitive() && gvar->datatype.IsReadOnly() ) @@ -1263,8 +1289,8 @@ void asCCompiler::DetermineSingleFunc(asSExprContext *ctx, asCScriptNode *node) if( pos >= 0 ) { asCString nsName = ctx->methodName.SubString(0, pos+2); - - // Cut off the :: + + // Cut off the :: if( nsName.GetLength() > 2 ) nsName.SetLength(nsName.GetLength()-2); @@ -1300,7 +1326,7 @@ void asCCompiler::DetermineSingleFunc(asSExprContext *ctx, asCScriptNode *node) asCString msg; msg.Format(TXT_SHARED_CANNOT_CALL_NON_SHARED_FUNC_s, builder->GetFunctionDescription(funcs[0])->GetDeclaration()); Error(msg, node); - + // Fall through so the compiler can continue anyway } @@ -1312,7 +1338,79 @@ void asCCompiler::DetermineSingleFunc(asSExprContext *ctx, asCScriptNode *node) ctx->methodName = ""; } -void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction, int refType, bool isMakingCopy) +void asCCompiler::CompileInitAsCopy(asCDataType &dt, int offset, asCByteCode *bc, asSExprContext *arg, asCScriptNode *node, bool derefDestination) +{ + asASSERT( dt.GetObjectType() ); + + bool isObjectOnHeap = derefDestination ? false : IsVariableOnHeap(offset); + + // Use copy constructor if available. + if( dt.GetObjectType()->beh.copyconstruct ) + { + PrepareForAssignment(&dt, arg, node, true); + int r = CallCopyConstructor(dt, offset, isObjectOnHeap, bc, arg, node, 0, derefDestination); + if( r < 0 && tempVariables.Exists(offset) ) + Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node); + } + else + { + // TODO: 2.28.1: Need to reserve variables, as the default constructor may need + // to allocate temporary variables to compute default args + + // Allocate and construct the temporary object before whatever is already in the bytecode + asCByteCode tmpBC(engine); + int r = CallDefaultConstructor(dt, offset, isObjectOnHeap, &tmpBC, node, 0, derefDestination); + if( r < 0 ) + { + if( tempVariables.Exists(offset) ) + Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node); + return; + } + + tmpBC.AddCode(bc); + bc->AddCode(&tmpBC); + + // Assign the evaluated expression to the temporary variable + PrepareForAssignment(&dt, arg, node, true); + bc->AddCode(&arg->bc); + + // Call the opAssign method to assign the value to the temporary object + dt.MakeReference(isObjectOnHeap); + asCTypeInfo type; + type.Set(dt); + type.isTemporary = true; + type.stackOffset = (short)offset; + + if( dt.IsObjectHandle() ) + type.isExplicitHandle = true; + + bc->InstrSHORT(asBC_PSF, (short)offset); + if( derefDestination ) + bc->Instr(asBC_RDSPtr); + + r = PerformAssignment(&type, &arg->type, bc, node); + if( r < 0 ) + { + if( tempVariables.Exists(offset) ) + Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node); + return; + } + + // Pop the reference that was pushed on the stack if the result is an object + if( type.dataType.IsObject() ) + bc->Instr(asBC_PopPtr); + + // If the assignment operator returned an object by value it will + // be in a temporary variable which we need to destroy now + if( type.isTemporary && type.stackOffset != (short)offset ) + ReleaseTemporaryVariable(type.stackOffset, bc); + + // Release the original value too in case it is a temporary + ReleaseTemporaryVariable(arg->type, bc); + } +} + +int asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction, int refType, bool isMakingCopy) { asCDataType param = *paramType; if( paramType->GetTokenType() == ttQuestion ) @@ -1324,9 +1422,13 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a param = ctx->type.dataType; param.MakeHandle(ctx->type.isExplicitHandle || ctx->type.IsNullConstant()); + // Treat the void expression like a null handle when working with var types + if( ctx->type.IsVoidExpression() ) + param = asCDataType::CreateNullHandle(); + // If value assign is disabled for reference types, then make // sure to always pass the handle to ? parameters - if( builder->engine->ep.disallowValueAssignForRefType && + if( builder->engine->ep.disallowValueAssignForRefType && ctx->type.dataType.GetObjectType() && (ctx->type.dataType.GetObjectType()->flags & asOBJ_REF) && !(ctx->type.dataType.GetObjectType()->flags & asOBJ_SCOPED) ) { param.MakeHandle(true); @@ -1365,10 +1467,10 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a ctx->bc.AddCode(&tmpBC); } - // If the reference is const, then it is not necessary to make a copy if the value already is a variable - // Even if the same variable is passed in another argument as non-const then there is no problem - if( dt.IsPrimitive() || dt.IsNullHandle() ) + if( dt.IsPrimitive() ) { + // If the reference is const, then it is not necessary to make a copy if the value already is a variable + // Even if the same variable is passed in another argument as non-const then there is no problem IsVariableInitialized(&ctx->type, node); if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); @@ -1380,82 +1482,99 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a PushVariableOnStack(ctx, true); ctx->type.dataType.MakeReadOnly(param.IsReadOnly()); } + else if( ctx->type.dataType.IsNullHandle() ) + { + // Need to initialize a local temporary variable to + // represent the null handle when passed as reference + asASSERT( ctx->bc.GetLastInstr() == asBC_PshNull ); + ctx->bc.Instr(asBC_PopPtr); + + dt.MakeHandle(true); + offset = AllocateVariableNotIn(dt, true, false, ctx); + + // Push the reference to the variable on the stack + ctx->bc.InstrWORD(asBC_PSF, (short)offset); + + ctx->type.SetVariable(dt, offset, true); + } else { IsVariableInitialized(&ctx->type, node); if( !isMakingCopy ) { - ImplicitConversion(ctx, param, node, asIC_IMPLICIT_CONV, true); - - if( !ctx->type.dataType.IsEqualExceptRef(param) ) + // Even though the parameter expects a reference, it is only meant to be + // used as input value and doesn't have to refer to the actual object, so it + // is OK to do an implicit conversion. + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true); + if( !ctx->type.dataType.IsEqualExceptRefAndConst(param) ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), param.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), param.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.Set(param); + return -1; + } + + // The compiler must guarantee that the object stays alive during the execution + // of the function, and it must also guarantee that the value isn't modified by + // the function. + + // If the argument is a temporary local variable then it is safe to be passed to + // the function as it is, since the local variable will stay alive, and since it + // is temporary there is no side effect if the function modifies it. + + // If the parameter is read-only and therefor guaranteed not to be modified by the + // function, then it is enough that the variable is local to guarantee the lifetime. + if( !ctx->type.isTemporary && !(param.IsReadOnly() && ctx->type.isVariable) ) + { + if( (ctx->type.dataType.GetObjectType()->flags & asOBJ_REF) && param.IsReadOnly() && !(ctx->type.dataType.GetObjectType()->flags & asOBJ_SCOPED) ) + { + // If the object is a reference type (except scoped reference types), and the + // parameter is a const reference, then it is not necessary to make a copy of the + // object. The compiler just needs to hold a handle to guarantee the lifetime. + + // Allocate a handle variable + dt.MakeHandle(true); + offset = AllocateVariableNotIn(dt, true, false, ctx); + + // Copy the handle + Dereference(ctx, true); + ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset); + ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType()); + ctx->bc.Instr(asBC_PopPtr); + ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset); + + // The type should be set to the param type instead of dt to guarantee + // that the expression keeps the correct type for variable ? args. Otherwise + // MoveArgsToStack will use the wrong bytecode to move the arg to the stack + ctx->type.SetVariable(param, offset, true); + } + else + { + // Allocate and initialize a temporary local object + offset = AllocateVariableNotIn(dt, true, false, ctx); + CompileInitAsCopy(dt, offset, &ctx->bc, ctx, node, false); + + // Push the object pointer on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if( dt.IsObject() && !dt.IsObjectHandle() ) + ctx->bc.Instr(asBC_RDSPtr); + + // Set the resulting type + ctx->type.Set(dt); + ctx->type.isTemporary = true; + ctx->type.stackOffset = short(offset); + if( dt.IsObjectHandle() ) + ctx->type.isExplicitHandle = true; + ctx->type.dataType.MakeReference(false); + if( paramType->IsReadOnly() ) + ctx->type.dataType.MakeReadOnly(true); + } } } - - // If the argument already is a temporary - // variable we don't need to allocate another - - // If the parameter is read-only and the object already is a local - // variable then it is not necessary to make a copy either - if( !ctx->type.isTemporary && !(param.IsReadOnly() && ctx->type.isVariable) && !isMakingCopy ) - { - // TODO: 2.28.1: Need to reserve variables, as the default constructor may need - // to allocate temporary variables to compute default args - // Make sure the variable is not used in the expression - offset = AllocateVariableNotIn(dt, true, false, ctx); - - // TODO: copy: Use copy constructor if available. See PrepareTemporaryObject() - - // Allocate and construct the temporary object - asCByteCode tmpBC(engine); - - CallDefaultConstructor(dt, offset, IsVariableOnHeap(offset), &tmpBC, node); - - // Insert the code before the expression code - tmpBC.AddCode(&ctx->bc); - ctx->bc.AddCode(&tmpBC); - - // Assign the evaluated expression to the temporary variable - PrepareForAssignment(&dt, ctx, node, true); - - dt.MakeReference(IsVariableOnHeap(offset)); - asCTypeInfo type; - type.Set(dt); - type.isTemporary = true; - type.stackOffset = (short)offset; - - if( dt.IsObjectHandle() ) - type.isExplicitHandle = true; - - ctx->bc.InstrSHORT(asBC_PSF, (short)offset); - - PerformAssignment(&type, &ctx->type, &ctx->bc, node); - - ctx->bc.Instr(asBC_PopPtr); - - ReleaseTemporaryVariable(ctx->type, &ctx->bc); - - ctx->type.Set(dt); - ctx->type.isTemporary = true; - ctx->type.stackOffset = offset; - if( dt.IsObjectHandle() ) - ctx->type.isExplicitHandle = true; - ctx->type.dataType.MakeReference(false); - - ctx->bc.InstrSHORT(asBC_PSF, (short)offset); - if( dt.IsObject() && !dt.IsObjectHandle() ) - ctx->bc.Instr(asBC_RDSPtr); - - if( paramType->IsReadOnly() ) - ctx->type.dataType.MakeReadOnly(true); - } - else if( isMakingCopy ) + else { // We must guarantee that the address to the value is on the stack if( ctx->type.dataType.IsObject() && @@ -1490,7 +1609,7 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a } else { - // TODO: 2.28.1: Need to reserve variables, as the default constructor may need + // TODO: 2.28.1: Need to reserve variables, as the default constructor may need // to allocate temporary variables to compute default args // Allocate and construct the temporary object @@ -1501,7 +1620,7 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a tmpBC.AddCode(&ctx->bc); ctx->bc.AddCode(&tmpBC); - dt.MakeReference((!dt.IsObject() || dt.IsObjectHandle())); + dt.MakeReference(!dt.IsObject() || dt.IsObjectHandle()); asCTypeInfo type; type.Set(dt); type.isTemporary = true; @@ -1537,9 +1656,24 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a // Literal constants cannot be passed to inout ref arguments if( !ctx->type.isVariable && ctx->type.isConstant ) { - Error(TXT_NOT_VALID_REFERENCE, node); + // Unless unsafe references are turned on and the reference is const + if( param.IsReadOnly() && engine->ep.allowUnsafeReferences ) + { + // Since the parameter is a const & make a copy. + ConvertToTempVariable(ctx); + ctx->type.dataType.MakeReadOnly(true); + } + else + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } } + // Perform implicit ref cast if necessary, but don't allow the implicit conversion to create new objects + if( ctx->type.dataType.IsObject() && ctx->type.dataType.GetObjectType() != dt.GetObjectType() ) + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, false); + // Only objects that support object handles // can be guaranteed to be safe. Local variables are // already safe, so there is no need to add an extra @@ -1573,9 +1707,7 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a // Release previous temporary variable stored in the context (if any) if( ctx->type.isTemporary ) - { ReleaseTemporaryVariable(ctx->type.stackOffset, &ctx->bc); - } ctx->type.SetVariable(dt, offset, true); } @@ -1627,16 +1759,17 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a if( !ctx->type.dataType.IsEqualExceptRef(dt) ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), dt.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), dt.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.Set(dt); + return -1; } if( dt.IsObjectHandle() ) ctx->type.isExplicitHandle = true; - if( dt.IsObject() ) + if( dt.IsObject() && !dt.IsNullHandle() ) { if( !dt.IsReference() ) { @@ -1667,7 +1800,7 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a } // Don't put any pointer on the stack yet - if( param.IsReference() || param.IsObject() ) + if( param.IsReference() || (param.IsObject() && !param.IsNullHandle()) ) { // &inout parameter may leave the reference on the stack already if( refType != 3 ) @@ -1683,6 +1816,8 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a } } } + + return 0; } void asCCompiler::PrepareFunctionCall(int funcId, asCByteCode *bc, asCArray &args) @@ -1692,6 +1827,7 @@ void asCCompiler::PrepareFunctionCall(int funcId, asCByteCode *bc, asCArrayparameterTypes.GetLength() == args.GetLength() ); bool makingCopy = false; if( descr->parameterTypes.GetLength() == 1 && descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) && @@ -1802,7 +1938,7 @@ void asCCompiler::MoveArgsToStack(int funcId, asCByteCode *bc, asCArray &args) +int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArray &args, asCArray &namedArgs) { asASSERT(node->nodeType == snArgList); @@ -1811,7 +1947,8 @@ int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArraynodeType != snNamedArgument ) + argCount++; arg = arg->next; } @@ -1824,44 +1961,126 @@ int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArraylastChild; while( arg ) { + asCScriptNode *asgNode = arg, *namedNode = 0; + if( asgNode->nodeType == snNamedArgument ) + { + if( inPositionalArguments ) + { + Error(TXT_POS_ARG_AFTER_NAMED_ARG, node); + return -1; + } + + asgNode = arg->firstChild->next; + namedNode = arg->firstChild; + + asASSERT( namedNode->nodeType == snIdentifier ); + } + else + inPositionalArguments = true; + asSExprContext expr(engine); - int r = CompileAssignment(arg, &expr); + int r = CompileAssignment(asgNode, &expr); if( r < 0 ) anyErrors = true; - args[n] = asNEW(asSExprContext)(engine); - if( args[n] == 0 ) + asSExprContext *ctx = asNEW(asSExprContext)(engine); + if( ctx == 0 ) { // Out of memory return -1; } - MergeExprBytecodeAndType(args[n], &expr); + MergeExprBytecodeAndType(ctx, &expr); + + if( inPositionalArguments ) + { + args[n] = ctx; + n--; + } + else + { + asSNamedArgument namedArg; + namedArg.name = asCString(&script->code[namedNode->tokenPos], namedNode->tokenLength); + namedArg.ctx = ctx; + + // Error out when multiple arguments with the same name are passed + for( asUINT n = 0; n < namedArgs.GetLength(); ++n ) + { + if( namedArgs[n].name == namedArg.name ) + { + Error(TXT_DUPLICATE_NAMED_ARG, asgNode); + anyErrors = true; + break; + } + } + + namedArgs.PushLast(namedArg); + } - n--; arg = arg->prev; } return anyErrors ? -1 : 0; } -int asCCompiler::CompileDefaultArgs(asCScriptNode *node, asCArray &args, asCScriptFunction *func) +int asCCompiler::CompileDefaultAndNamedArgs(asCScriptNode *node, asCArray &args, int funcId, asCObjectType *objectType, asCArray *namedArgs) { + asCScriptFunction *func = builder->GetFunctionDescription(funcId); + if( func == 0 || args.GetLength() >= (asUINT)func->GetParamCount() ) + return 0; + + // Make sure to use the real function for virtual functions + if( func->funcType == asFUNC_VIRTUAL ) + { + asASSERT( objectType ); + func = objectType->virtualFunctionTable[func->vfTableIdx]; + } + + // Make sure none of the variables used in the previous arguments are reused in the default arguments bool anyErrors = false; - asCArray varsUsed; + int prevReservedVars = reservedVariables.GetLength(); + int explicitArgs = (int)args.GetLength(); for( int p = 0; p < explicitArgs; p++ ) - args[p]->bc.GetVarsUsed(varsUsed); + args[p]->bc.GetVarsUsed(reservedVariables); - // Compile the arguments in reverse order (as they will be pushed on the stack) + // Make space for all the new arguments args.SetLength(func->parameterTypes.GetLength()); for( asUINT c = explicitArgs; c < args.GetLength(); c++ ) args[c] = 0; + + // Add the named arguments to the argument list in the right position + if( namedArgs ) + { + for( asUINT n = 0; n < namedArgs->GetLength(); ++n ) + { + asSNamedArgument &named = (*namedArgs)[n]; + named.ctx->bc.GetVarsUsed(reservedVariables); + + // Find the right spot to put it in + asUINT index = asUINT(-1); + for( asUINT j = 0; j < func->parameterTypes.GetLength(); ++j ) + { + if( func->parameterNames[j] == (*namedArgs)[n].name ) + { + index = j; + break; + } + } + + asASSERT( index < args.GetLength() ); + args[index] = named.ctx; + named.ctx = 0; + } + } + + // Compile the arguments in reverse order (as they will be pushed on the stack) for( int n = (int)func->parameterTypes.GetLength() - 1; n >= explicitArgs; n-- ) { + if( args[n] != 0 ) continue; if( func->defaultArgs[n] == 0 ) { anyErrors = true; continue; } // Parse the default arg string @@ -1885,11 +2104,19 @@ int asCCompiler::CompileDefaultArgs(asCScriptNode *node, asCArraynameSpace; + outFunc->nameSpace = func->nameSpace; + asSExprContext expr(engine); r = CompileExpression(arg, &expr); + // Restore the namespace + outFunc->nameSpace = origNameSpace; + // Don't allow address of class method if( expr.methodName != "" ) { @@ -1928,48 +2155,18 @@ int asCCompiler::CompileDefaultArgs(asCScriptNode *node, asCArraytype.isVariable ) - { - int offset = args[n]->type.stackOffset; - if( varsUsed.Exists(offset) ) - { - // Release the current temporary variable - ReleaseTemporaryVariable(args[n]->type, 0); - - asCDataType dt = args[n]->type.dataType; - dt.MakeReference(false); - - // Reserve all variables already used in the expression so none of them will be used - asCArray used; - args[n]->bc.GetVarsUsed(used); - size_t prevReserved = reservedVariables.GetLength(); - reservedVariables.Concatenate(used); - - int newOffset = AllocateVariable(dt, true, IsVariableOnHeap(offset)); - asASSERT( IsVariableOnHeap(offset) == IsVariableOnHeap(newOffset) ); - - reservedVariables.SetLength(prevReserved); - - // Replace the variable in the expression - args[n]->bc.ExchangeVar(offset, newOffset); - args[n]->type.stackOffset = (short)newOffset; - args[n]->type.isTemporary = true; - args[n]->type.isVariable = true; - } - } } + reservedVariables.SetLength(prevReservedVars); return anyErrors ? -1 : 0; } -asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArray &args, asCScriptNode *node, const char *name, asCObjectType *objectType, bool isConstMethod, bool silent, bool allowObjectConstruct, const asCString &scope) +asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArray &args, asCScriptNode *node, const char *name, asCArray *namedArgs, asCObjectType *objectType, bool isConstMethod, bool silent, bool allowObjectConstruct, const asCString &scope) { asCArray origFuncs = funcs; // Keep the original list for error message asUINT cost = 0; @@ -1978,14 +2175,18 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArray 0 ) { // Check the number of parameters in the found functions + asUINT totalArgs = (asUINT)args.GetLength(); + if( namedArgs != 0 ) + totalArgs += (asUINT)namedArgs->GetLength(); + for( n = 0; n < funcs.GetLength(); ++n ) { asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]); - if( desc->parameterTypes.GetLength() != args.GetLength() ) + if( desc->parameterTypes.GetLength() != totalArgs ) { bool noMatch = true; - if( args.GetLength() < desc->parameterTypes.GetLength() ) + if( totalArgs < desc->parameterTypes.GetLength() ) { // For virtual functions, the default args are defined in the real function of the object if( desc->funcType == asFUNC_VIRTUAL ) @@ -1997,7 +2198,7 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArraydefaultArgs[d] ) defaultArgs++; - if( args.GetLength() >= desc->parameterTypes.GetLength() - defaultArgs ) + if( totalArgs >= desc->parameterTypes.GetLength() - defaultArgs ) noMatch = false; } @@ -2022,6 +2223,7 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArray tempFuncs; @@ -2055,6 +2257,93 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArrayGetFunctionDescription(matchingFuncs[i].funcId); + if( desc->funcType == asFUNC_VIRTUAL ) + desc = objectType->virtualFunctionTable[desc->vfTableIdx]; + + //Match every named argument to an argument in the function + for( n = 0; n < namedArgs->GetLength(); ++n ) + (*namedArgs)[n].match = asUINT(-1); + + bool matchedAll = true; + for( asUINT j = 0; j < desc->parameterTypes.GetLength(); ++j ) + { + asUINT match = asUINT(-1); + for( n = 0; n < namedArgs->GetLength(); ++n ) + { + asSNamedArgument &namedArg = (*namedArgs)[n]; + if( desc->parameterNames[j] == namedArg.name ) + { + namedArg.match = j; + match = n; + break; + } + } + + // Check that every position is filled somehow + if( j >= args.GetLength() ) + { + if( match == asUINT(-1) && !desc->defaultArgs[j] ) + { + // No argument was found for this, and there is no + // default, so it doesn't work. + matchedAll = false; + break; + } + } + else + { + if( match != asUINT(-1) ) + { + // Can't name an argument that was already passed + matchedAll = false; + break; + } + } + } + + //Check that every named argument was matched + if( matchedAll ) + { + for( n = 0; n < namedArgs->GetLength(); ++n ) + { + asSNamedArgument &named = (*namedArgs)[n]; + + if( named.match == asUINT(-1) ) + { + matchedAll = false; + break; + } + + // Add to the cost + asUINT cost = MatchArgument(desc, named.ctx, named.match, allowObjectConstruct); + if( cost == asUINT(-1) ) + { + matchedAll = false; + break; + } + + matchingFuncs[i].cost += cost; + } + } + + if( !matchedAll ) + { + // Remove the function, we didn't match all the arguments. + if( i == matchingFuncs.GetLength()-1 ) + matchingFuncs.PopLast(); + else + matchingFuncs[i] = matchingFuncs.PopLast(); + i--; + } + } + } + // Select the overload(s) with the lowest overall cost funcs.SetLength(0); asUINT bestCost = asUINT(-1); @@ -2080,30 +2369,44 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArraymethodName != "" ) - str += args[0]->methodName; - else - str += args[0]->type.dataType.Format(); - } - for( n = 1; n < args.GetLength(); n++ ) - { - str += ", "; + if( n > 0 ) + str += ", "; if( args[n]->methodName != "" ) + { + if( args[n]->IsClassMethod() ) + { + attemptsPassingClassMethod = true; + str += args[n]->type.dataType.GetObjectType()->GetName(); + str += "::"; + } str += args[n]->methodName; + } else - str += args[n]->type.dataType.Format(); + str += args[n]->type.dataType.Format(outFunc->nameSpace); + } + if( namedArgs != 0 ) + { + for( n = 0; n < namedArgs->GetLength(); n++ ) + { + if( n > 0 || args.GetLength() ) + str += ", "; + + asSNamedArgument &named = (*namedArgs)[n]; + str += named.name; + str += "="; + if( named.ctx->methodName != "" ) + str += named.ctx->methodName; + else + str += named.ctx->type.dataType.Format(outFunc->nameSpace); + } } str += ")"; @@ -2118,28 +2421,88 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArray 0 ) + if( attemptsPassingClassMethod ) { - int r = 0, c = 0; - asASSERT( node ); - if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c); - builder->WriteInfo(script->name.AddressOf(), TXT_CANDIDATES_ARE, r, c, false); - PrintMatchingFuncs(origFuncs, node); + // Class methods must use delegate objects + Error(TXT_CANNOT_PASS_CLASS_METHOD_AS_ARG, node); + } + else + { + // Print the list of candidates + if( origFuncs.GetLength() > 0 ) + { + int r = 0, c = 0; + asASSERT( node ); + if( node ) script->ConvertPosToRowCol(node->tokenPos, &r, &c); + builder->WriteInfo(script->name.AddressOf(), TXT_CANDIDATES_ARE, r, c, false); + PrintMatchingFuncs(origFuncs, node, objectType); + } } } else { + asASSERT( attemptsPassingClassMethod == false ); + str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, str.AddressOf()); Error(str, node); - PrintMatchingFuncs(funcs, node); + PrintMatchingFuncs(funcs, node, objectType); } } return cost; } +bool asCCompiler::CompileAutoType(asCDataType &type, asSExprContext &compiledCtx, asCScriptNode *node, asCScriptNode *errNode) +{ + if( node && node->nodeType == snAssignment ) + { + int r = CompileAssignment(node, &compiledCtx); + if( r >= 0 ) + { + asCDataType newType = compiledCtx.type.dataType; + bool success = true; + + // Handle const qualifier on auto + if( type.IsReadOnly() ) + newType.MakeReadOnly(true); + else if( newType.IsPrimitive() ) + newType.MakeReadOnly(false); + + // Handle reference/value stuff + newType.MakeReference(false); + if( !newType.IsObjectHandle() ) + { + // We got a value object or an object reference. + // Turn the variable into a handle if specified + // as auto@, otherwise make it a 'value'. + if( type.IsHandleToAuto() ) + { + if( newType.MakeHandle(true) < 0 ) + { + Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, errNode); + success = false; + } + } + } + + if(success) + type = newType; + else + type = asCDataType::CreatePrimitive(ttInt, false); + return true; + } + + return false; + } + else + { + Error(TXT_CANNOT_RESOLVE_AUTO, errNode); + type = asCDataType::CreatePrimitive(ttInt, false); + return false; + } +} + void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc) { // Get the data type @@ -2149,12 +2512,23 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc) asCScriptNode *node = decl->firstChild->next; while( node ) { + // If this is an auto type, we have to compile the assignment now to figure out the type + asSExprContext compiledCtx(engine); + bool preCompiled = false; + if( type.IsAuto() ) + preCompiled = CompileAutoType(type, compiledCtx, node->next, node); + // Is the type allowed? - if( !type.CanBeInstanciated() ) + if( !type.CanBeInstantiated() ) { asCString str; - // TODO: Change to "'type' cannot be declared as variable" - str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf()); + if( type.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, type.Format(outFunc->nameSpace).AddressOf()); + else if( type.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, type.Format(outFunc->nameSpace).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format(outFunc->nameSpace).AddressOf()); Error(str, node); // Use int instead to avoid further problems @@ -2198,6 +2572,16 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc) // lead to more errors that are likely false return; } + else + { + // Warn if this variable hides another variable in a higher scope + if( variables->parent && variables->parent->GetVariable(name.AddressOf()) ) + { + asCString str; + str.Format(TXT_s_HIDES_VAR_IN_OUTER_SCOPE, name.AddressOf()); + Warning(str, node); + } + } // Add marker that the variable has been declared bc->VarDecl((int)outFunc->scriptData->variables.GetLength()); @@ -2217,7 +2601,7 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc) { // Compile the initialization expression asQWORD constantValue = 0; - if( CompileInitialization(node, bc, type, varNode, offset, &constantValue, 0) ) + if( CompileInitialization(node, bc, type, varNode, offset, &constantValue, 0, preCompiled ? &compiledCtx : 0) ) { // Check if the variable should be marked as pure constant if( type.IsPrimitive() && type.IsReadOnly() ) @@ -2234,7 +2618,8 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc) bc->OptimizeLocally(tempVariableOffsets); } -bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, asCDataType &type, asCScriptNode *errNode, int offset, asQWORD *constantValue, int isVarGlobOrMem) +// Returns true if the initialization expression is a constant expression +bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, asCDataType &type, asCScriptNode *errNode, int offset, asQWORD *constantValue, int isVarGlobOrMem, asSExprContext *preCompiled) { bool isConstantExpression = false; if( node && node->nodeType == snArgList ) @@ -2248,7 +2633,8 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as { // Compile the arguments asCArray args; - if( CompileArgumentList(node, args) >= 0 ) + asCArray namedArgs; + if( CompileArgumentList(node, args, namedArgs) >= 0 ) { // Find all constructors asCArray funcs; @@ -2261,17 +2647,13 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as funcs = beh->constructors; } - asCString str = type.Format(); - MatchFunctions(funcs, args, node, str.AddressOf()); + asCString str = type.Format(outFunc->nameSpace); + MatchFunctions(funcs, args, node, str.AddressOf(), &namedArgs); if( funcs.GetLength() == 1 ) { - int r = asSUCCESS; - // Add the default values for arguments not explicitly supplied - asCScriptFunction *func = (funcs[0] & FUNC_IMPORTED) == 0 ? engine->scriptFunctions[funcs[0]] : 0; - if( func && args.GetLength() < (asUINT)func->GetParamCount() ) - r = CompileDefaultArgs(node, args, func); + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], type.GetObjectType(), &namedArgs); if( r == asSUCCESS ) { @@ -2374,6 +2756,11 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as { asDELETE(args[n],asSExprContext); } + for( asUINT n = 0; n < namedArgs.GetLength(); n++ ) + if( namedArgs[n].ctx ) + { + asDELETE(namedArgs[n].ctx,asSExprContext); + } } } else if( node && node->nodeType == snInitList ) @@ -2395,6 +2782,21 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as // just the copy constructor. Only if no appropriate constructor is // available should the assignment operator be used. + // Compile the expression + asSExprContext newExpr(engine); + asSExprContext* expr; + int r = 0; + + if( preCompiled ) + { + expr = preCompiled; + } + else + { + expr = &newExpr; + r = CompileAssignment(node, expr); + } + // Call the default constructor here if( isVarGlobOrMem == 0 ) CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), &ctx.bc, errNode); @@ -2403,20 +2805,17 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as else if( isVarGlobOrMem == 2 ) CallDefaultConstructor(type, offset, type.IsReference(), &ctx.bc, errNode, isVarGlobOrMem); - // Compile the expression - asSExprContext expr(engine); - int r = CompileAssignment(node, &expr); if( r >= 0 ) { if( type.IsPrimitive() ) { - if( type.IsReadOnly() && expr.type.isConstant ) + if( type.IsReadOnly() && expr->type.isConstant ) { - ImplicitConversion(&expr, type, node, asIC_IMPLICIT_CONV); + ImplicitConversion(expr, type, node, asIC_IMPLICIT_CONV); // Tell caller that the expression is a constant so it can mark the variable as pure constant isConstantExpression = true; - *constantValue = expr.type.qwordValue; + *constantValue = expr->type.qwordValue; } asSExprContext lctx(engine); @@ -2447,7 +2846,7 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as lctx.type.dataType.MakeReadOnly(false); lctx.type.isLValue = true; - DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node); + DoAssignment(&ctx, &lctx, expr, node, node, ttAssignment, node); ProcessDeferredParams(&ctx); } else @@ -2503,7 +2902,8 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as // Even though an ASHANDLE can be an explicit handle the overloaded operator needs to be called if( lexpr.type.dataType.IsObject() && (!lexpr.type.isExplicitHandle || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) ) { - assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx); + bool useHndlAssign = lexpr.type.dataType.IsHandleToAsHandleType(); + assigned = CompileOverloadedDualOperator(node, &lexpr, expr, &ctx, useHndlAssign); if( assigned ) { // Pop the resulting value @@ -2520,27 +2920,27 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as if( !assigned ) { - PrepareForAssignment(&lexpr.type.dataType, &expr, node, false); + PrepareForAssignment(&lexpr.type.dataType, expr, node, false); // If the expression is constant and the variable also is constant // then mark the variable as pure constant. This will allow the compiler // to optimize expressions with this variable. - if( type.IsReadOnly() && expr.type.isConstant ) + if( type.IsReadOnly() && expr->type.isConstant ) { isConstantExpression = true; - *constantValue = expr.type.qwordValue; + *constantValue = expr->type.qwordValue; } // Add expression code to bytecode - MergeExprBytecode(&ctx, &expr); + MergeExprBytecode(&ctx, expr); // Add byte code for storing value of expression in variable ctx.bc.AddCode(&lexpr.bc); - PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, errNode); + PerformAssignment(&lexpr.type, &expr->type, &ctx.bc, errNode); // Release temporary variables used by expression - ReleaseTemporaryVariable(expr.type, &ctx.bc); + ReleaseTemporaryVariable(expr->type, &ctx.bc); ctx.bc.Instr(asBC_PopPtr); @@ -2569,8 +2969,6 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as } } - bc->OptimizeLocally(tempVariableOffsets); - return isConstantExpression; } @@ -2582,7 +2980,7 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte var->dataType.IsObjectHandle() ) { asCString str; - str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format().AddressOf()); + str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); return; } @@ -2596,7 +2994,7 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte // TODO: runtime optimize: A future optimization should be to use the stack space directly // for small buffers so that the dynamic allocation is skipped - // Create a new special object type for the lists. Both asCRestore and the + // Create a new special object type for the lists. Both asCRestore and the // context exception handler will need this to know how to parse the buffer. asCObjectType *listPatternType = engine->GetListPatternType(funcId); @@ -2608,13 +3006,14 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte asSExprContext valueExpr(engine); asCScriptNode *el = node; asSListPatternNode *patternNode = engine->scriptFunctions[listPatternType->templateSubTypes[0].GetBehaviour()->listFactory]->listPattern; - int r = CompileInitListElement(patternNode, el, engine->GetTypeIdFromDataType(asCDataType::CreateObject(listPatternType, false)), bufferVar, bufferSize, valueExpr.bc); + int elementsInSubList = -1; + int r = CompileInitListElement(patternNode, el, engine->GetTypeIdFromDataType(asCDataType::CreateObject(listPatternType, false)), short(bufferVar), bufferSize, valueExpr.bc, elementsInSubList); asASSERT( r || patternNode == 0 ); UNUSED_VAR(r); // After all values have been evaluated we know the final size of the buffer asSExprContext allocExpr(engine); - allocExpr.bc.InstrSHORT_DW(asBC_AllocMem, bufferVar, bufferSize); + allocExpr.bc.InstrSHORT_DW(asBC_AllocMem, short(bufferVar), bufferSize); // Merge the bytecode into the final sequence bc->AddCode(&allocExpr.bc); @@ -2625,7 +3024,7 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte asSExprContext arg1(engine); arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false)); arg1.type.dataType.MakeReference(true); - arg1.bc.InstrSHORT(asBC_PshVPtr, bufferVar); + arg1.bc.InstrSHORT(asBC_PshVPtr, short(bufferVar)); args.PushLast(&arg1); asSExprContext ctx(engine); @@ -2729,15 +3128,15 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte // Free the temporary buffer. The FREE instruction will make sure to destroy // each element in the buffer so there is no need to do this manually - bc->InstrW_PTR(asBC_FREE, bufferVar, listPatternType); + bc->InstrW_PTR(asBC_FREE, short(bufferVar), listPatternType); ReleaseTemporaryVariable(bufferVar, bc); } -int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScriptNode *&valueNode, int bufferTypeId, short bufferVar, asUINT &bufferSize, asCByteCode &byteCode) +int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScriptNode *&valueNode, int bufferTypeId, short bufferVar, asUINT &bufferSize, asCByteCode &byteCode, int &elementsInSubList) { if( patternNode->type == asLPT_START ) { - if( valueNode->nodeType != snInitList ) + if( valueNode == 0 || valueNode->nodeType != snInitList ) { Error(TXT_EXPECTED_LIST, valueNode); return -1; @@ -2748,14 +3147,24 @@ int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScr asCScriptNode *node = valueNode->firstChild; while( patternNode->type != asLPT_END ) { - if( node == 0 ) + // Check for missing value here, else the error reporting will not have a source position to report the error for + if( node == 0 && patternNode->type == asLPT_TYPE ) { Error(TXT_NOT_ENOUGH_VALUES_FOR_LIST, valueNode); return -1; } - int r = CompileInitListElement(patternNode, node, bufferTypeId, bufferVar, bufferSize, byteCode); + asCScriptNode *errNode = node; + int r = CompileInitListElement(patternNode, node, bufferTypeId, bufferVar, bufferSize, byteCode, elementsInSubList); if( r < 0 ) return r; + + if( r == 1 ) + { + asASSERT( engine->ep.disallowEmptyListElements ); + // Empty elements in the middle are not allowed + Error(TXT_EMPTY_LIST_ELEMENT_IS_NOT_ALLOWED, errNode); + } + asASSERT( patternNode ); } @@ -2764,13 +3173,19 @@ int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScr Error(TXT_TOO_MANY_VALUES_FOR_LIST, valueNode); return -1; } - + // Move to the next node valueNode = valueNode->next; patternNode = patternNode->next; } - else if( patternNode->type == asLPT_REPEAT ) + else if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ) { + // TODO: list: repeat_inner should make sure the list has the same size as the inner list, i.e. square area + // TODO: list: repeat_prev should make sure the list is the same size as the previous + + asEListPatternNodeType repeatType = patternNode->type; + asCScriptNode *firstValue = valueNode; + // The following values will be repeated N times patternNode = patternNode->next; @@ -2786,14 +3201,64 @@ int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScr bufferSize += 4; asUINT countElements = 0; + int elementsInSubSubList = -1; + asSExprContext ctx(engine); while( valueNode ) { patternNode = nextNode; - int r = CompileInitListElement(patternNode, valueNode, bufferTypeId, bufferVar, bufferSize, ctx.bc); + asCScriptNode *errNode = valueNode; + int r = CompileInitListElement(patternNode, valueNode, bufferTypeId, bufferVar, bufferSize, ctx.bc, elementsInSubSubList); if( r < 0 ) return r; - countElements++; + if( r == 0 ) + countElements++; + else + { + asASSERT( r == 1 && engine->ep.disallowEmptyListElements ); + if( valueNode ) + { + // Empty elements in the middle are not allowed + Error(TXT_EMPTY_LIST_ELEMENT_IS_NOT_ALLOWED, errNode); + } + } + } + + if( countElements == 0 ) + { + // Skip the sub pattern that was expected to be repeated, otherwise the caller will try to match these when we return + patternNode = nextNode; + if( patternNode->type == asLPT_TYPE ) + patternNode = patternNode->next; + else if( patternNode->type == asLPT_START ) + { + int subCount = 1; + do + { + patternNode = patternNode->next; + if( patternNode->type == asLPT_START ) + subCount++; + else if( patternNode->type == asLPT_END ) + subCount--; + } while( subCount > 0 ); + patternNode = patternNode->next; + } + } + + // For repeat_same each repeated sublist must have the same size to form a rectangular array + if( repeatType == asLPT_REPEAT_SAME && elementsInSubList != -1 && asUINT(elementsInSubList) != countElements ) + { + if( countElements < asUINT(elementsInSubList) ) + Error(TXT_NOT_ENOUGH_VALUES_FOR_LIST, firstValue); + else + Error(TXT_TOO_MANY_VALUES_FOR_LIST, firstValue); + + return -1; + } + else + { + // Return to caller the amount of elments in this sublist + elementsInSubList = countElements; } // The first dword in the buffer will hold the number of elements @@ -2804,6 +3269,8 @@ int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScr } else if( patternNode->type == asLPT_TYPE ) { + bool isEmpty = false; + // Determine the size of the element asUINT size = 0; @@ -2840,7 +3307,7 @@ int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScr if( dt.GetTokenType() == ttQuestion ) { // Can't use init lists with var type as it is not possible to determine what type should be allocated - asCString str; + asCString str; str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, "?"); Error(str.AddressOf(), valueNode); rctx.type.SetDummy(); @@ -2921,125 +3388,153 @@ int asCCompiler::CompileInitListElement(asSListPatternNode *&patternNode, asCScr } } - asSExprContext ctx(engine); - DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode); + if( lctx.type.dataType.IsNullHandle() ) + { + // Don't add any code to assign a null handle. RefCpy doesn't work without a known type. + // The buffer is already initialized to zero in asBC_AllocMem anyway. + asASSERT( rctx.bc.GetLastInstr() == asBC_PshNull ); + asASSERT( reinterpret_cast(patternNode)->dataType.GetTokenType() == ttQuestion ); + } + else + { + asSExprContext ctx(engine); + DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode); - if( !lctx.type.dataType.IsPrimitive() ) - ctx.bc.Instr(asBC_PopPtr); + if( !lctx.type.dataType.IsPrimitive() ) + ctx.bc.Instr(asBC_PopPtr); - // Release temporary variables used by expression - ReleaseTemporaryVariable(ctx.type, &ctx.bc); + // Release temporary variables used by expression + ReleaseTemporaryVariable(ctx.type, &ctx.bc); - ProcessDeferredParams(&ctx); + ProcessDeferredParams(&ctx); - byteCode.AddCode(&ctx.bc); + byteCode.AddCode(&ctx.bc); + } } else { - // There is no specific value so we need to fill it with a default value - if( dt.GetTokenType() == ttQuestion ) + if( builder->engine->ep.disallowEmptyListElements ) { - // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. - if( bufferSize & 0x3 ) - bufferSize += 4 - (bufferSize & 0x3); - - // Place the type id for a null handle in the buffer - byteCode.InstrSHORT_DW_DW(asBC_SetListType, bufferVar, bufferSize, 0); - bufferSize += 4; - - dt = asCDataType::CreateNullHandle(); - - // No need to initialize the handle as the buffer is already initialized with zeroes + // Empty elements are not allowed, except if it is the last in the list + isEmpty = true; } - else if( dt.GetObjectType() && dt.GetObjectType()->flags & asOBJ_VALUE ) + else { - // For value types with default constructor we need to call the constructor - asSTypeBehaviour *beh = dt.GetBehaviour(); - int func = 0; - if( beh ) func = beh->construct; - if( func == 0 && (dt.GetObjectType()->flags & asOBJ_POD) == 0 ) - { - asCString str; - // TODO: funcdef: asCDataType should have a GetTypeName() - if( dt.GetFuncDef() ) - str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetFuncDef()->GetName()); - else - str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetObjectType()->GetName()); - Error(str, valueNode); - } - else if( func ) + // There is no specific value so we need to fill it with a default value + if( dt.GetTokenType() == ttQuestion ) { // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. if( bufferSize & 0x3 ) bufferSize += 4 - (bufferSize & 0x3); - // Call the constructor as a normal function - byteCode.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize); + // Place the type id for a null handle in the buffer + byteCode.InstrSHORT_DW_DW(asBC_SetListType, bufferVar, bufferSize, 0); + bufferSize += 4; - asSExprContext ctx(engine); - PerformFunctionCall(func, &ctx, false, 0, dt.GetObjectType()); - byteCode.AddCode(&ctx.bc); + dt = asCDataType::CreateNullHandle(); + + // No need to initialize the handle as the buffer is already initialized with zeroes } - } - else if( !dt.IsObjectHandle() && dt.GetObjectType() && dt.GetObjectType()->flags & asOBJ_REF ) - { - // For ref types (not handles) we need to call the default factory - asSTypeBehaviour *beh = dt.GetBehaviour(); - int func = 0; - if( beh ) func = beh->factory; - if( func == 0 ) + else if( dt.GetObjectType() && dt.GetObjectType()->flags & asOBJ_VALUE ) { - asCString str; - // TODO: funcdef: asCDataType should have a GetTypeName() - if( dt.GetFuncDef() ) - str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetFuncDef()->GetName()); - else - str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetObjectType()->GetName()); - Error(str, valueNode); + // For value types with default constructor we need to call the constructor + asSTypeBehaviour *beh = dt.GetBehaviour(); + int func = 0; + if( beh ) func = beh->construct; + if( func == 0 && (dt.GetObjectType()->flags & asOBJ_POD) == 0 ) + { + asCString str; + // TODO: funcdef: asCDataType should have a GetTypeName() + if( dt.GetFuncDef() ) + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetFuncDef()->GetName()); + else + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetObjectType()->GetName()); + Error(str, valueNode); + } + else if( func ) + { + // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. + if( bufferSize & 0x3 ) + bufferSize += 4 - (bufferSize & 0x3); + + // Call the constructor as a normal function + byteCode.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize); + + asSExprContext ctx(engine); + PerformFunctionCall(func, &ctx, false, 0, dt.GetObjectType()); + byteCode.AddCode(&ctx.bc); + } } - else if( func ) + else if( !dt.IsObjectHandle() && dt.GetObjectType() && dt.GetObjectType()->flags & asOBJ_REF ) { - asSExprContext rctx(engine); - PerformFunctionCall(func, &rctx, false, 0, dt.GetObjectType()); + // For ref types (not handles) we need to call the default factory + asSTypeBehaviour *beh = dt.GetBehaviour(); + int func = 0; + if( beh ) func = beh->factory; + if( func == 0 ) + { + asCString str; + // TODO: funcdef: asCDataType should have a GetTypeName() + if( dt.GetFuncDef() ) + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetFuncDef()->GetName()); + else + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, dt.GetObjectType()->GetName()); + Error(str, valueNode); + } + else if( func ) + { + asSExprContext rctx(engine); + PerformFunctionCall(func, &rctx, false, 0, dt.GetObjectType()); - // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. - if( bufferSize & 0x3 ) - bufferSize += 4 - (bufferSize & 0x3); + // Values on the list must be aligned to 32bit boundaries, except if the type is smaller than 32bit. + if( bufferSize & 0x3 ) + bufferSize += 4 - (bufferSize & 0x3); - asSExprContext lctx(engine); - lctx.bc.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize); - lctx.type.Set(dt); - lctx.type.isLValue = true; - lctx.type.isExplicitHandle = true; - lctx.type.dataType.MakeReference(true); + asSExprContext lctx(engine); + lctx.bc.InstrSHORT_DW(asBC_PshListElmnt, bufferVar, bufferSize); + lctx.type.Set(dt); + lctx.type.isLValue = true; + lctx.type.isExplicitHandle = true; + lctx.type.dataType.MakeReference(true); - asSExprContext ctx(engine); - DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode); + asSExprContext ctx(engine); + DoAssignment(&ctx, &lctx, &rctx, valueNode, valueNode, ttAssignment, valueNode); - if( !lctx.type.dataType.IsPrimitive() ) - ctx.bc.Instr(asBC_PopPtr); + if( !lctx.type.dataType.IsPrimitive() ) + ctx.bc.Instr(asBC_PopPtr); - // Release temporary variables used by expression - ReleaseTemporaryVariable(ctx.type, &ctx.bc); + // Release temporary variables used by expression + ReleaseTemporaryVariable(ctx.type, &ctx.bc); - ProcessDeferredParams(&ctx); + ProcessDeferredParams(&ctx); - byteCode.AddCode(&ctx.bc); + byteCode.AddCode(&ctx.bc); + } } } } - // Determine size of the element - if( dt.IsPrimitive() || (!dt.IsNullHandle() && (dt.GetObjectType()->flags & asOBJ_VALUE)) ) - size = dt.GetSizeInMemoryBytes(); - else - size = AS_PTR_SIZE*4; - asASSERT( size <= 4 || (size & 0x3) == 0 ); - + if( !isEmpty ) + { + // Determine size of the element + if( dt.IsPrimitive() || (!dt.IsNullHandle() && (dt.GetObjectType()->flags & asOBJ_VALUE)) ) + size = dt.GetSizeInMemoryBytes(); + else + size = AS_PTR_SIZE*4; + asASSERT( size <= 4 || (size & 0x3) == 0 ); + + bufferSize += size; + } + // Move to the next element - bufferSize += size; patternNode = patternNode->next; valueNode = valueNode->next; + + if( isEmpty ) + { + // The caller will determine if the empty element should be ignored or not + return 1; + } } else asASSERT( false ); @@ -3415,37 +3910,43 @@ void asCCompiler::CompileIfStatement(asCScriptNode *inode, bool *hasReturn, asCB // Compile the expression asSExprContext expr(engine); - CompileAssignment(inode->firstChild, &expr); - if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + int r = CompileAssignment(inode->firstChild, &expr); + if( r == 0 ) { - Error(TXT_EXPR_MUST_BE_BOOL, inode->firstChild); - expr.type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), 1); - } + // Allow value types to be converted to bool using 'bool opImplConv()' + if( expr.type.dataType.GetObjectType() && (expr.type.dataType.GetObjectType()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), inode, asIC_IMPLICIT_CONV); - if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); - ProcessDeferredParams(&expr); + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, inode->firstChild); + else + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); - if( !expr.type.isConstant ) - { - ProcessPropertyGetAccessor(&expr, inode); + if( !expr.type.isConstant ) + { + ProcessPropertyGetAccessor(&expr, inode); - ConvertToVariable(&expr); + ConvertToVariable(&expr); - // Add a test - expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); - expr.bc.Instr(asBC_ClrHi); - expr.bc.InstrDWORD(asBC_JZ, afterLabel); - ReleaseTemporaryVariable(expr.type, &expr.bc); + // Add a test + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + expr.bc.Instr(asBC_ClrHi); + expr.bc.InstrDWORD(asBC_JZ, afterLabel); + ReleaseTemporaryVariable(expr.type, &expr.bc); - expr.bc.OptimizeLocally(tempVariableOffsets); - bc->AddCode(&expr.bc); - } - else if( expr.type.dwordValue == 0 ) - { - // Jump to the else case - bc->InstrINT(asBC_JMP, afterLabel); + expr.bc.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&expr.bc); + } + else if( expr.type.dwordValue == 0 ) + { + // Jump to the else case + bc->InstrINT(asBC_JMP, afterLabel); - // TODO: Should we warn that the expression will always go to the else? + // TODO: Should we warn that the expression will always go to the else? + } + } } // Compile the if statement @@ -3565,6 +4066,10 @@ void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc) int r = CompileAssignment(second->firstChild, &expr); if( r >= 0 ) { + // Allow value types to be converted to bool using 'bool opImplConv()' + if( expr.type.dataType.GetObjectType() && (expr.type.dataType.GetObjectType()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), second->firstChild, asIC_IMPLICIT_CONV); + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) Error(TXT_EXPR_MUST_BE_BOOL, second); else @@ -3671,27 +4176,34 @@ void asCCompiler::CompileWhileStatement(asCScriptNode *wnode, asCByteCode *bc) // Compile expression asSExprContext expr(engine); - CompileAssignment(wnode->firstChild, &expr); - if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) - Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild); - else + int r = CompileAssignment(wnode->firstChild, &expr); + if( r == 0 ) { - if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); - ProcessDeferredParams(&expr); + // Allow value types to be converted to bool using 'bool opImplConv()' + if( expr.type.dataType.GetObjectType() && (expr.type.dataType.GetObjectType()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), wnode->firstChild, asIC_IMPLICIT_CONV); - ProcessPropertyGetAccessor(&expr, wnode); + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild); + else + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); - // Add byte code for the expression - ConvertToVariable(&expr); + ProcessPropertyGetAccessor(&expr, wnode); - // Jump to end of statement if expression is false - expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); - expr.bc.Instr(asBC_ClrHi); - expr.bc.InstrDWORD(asBC_JZ, afterLabel); - ReleaseTemporaryVariable(expr.type, &expr.bc); + // Add byte code for the expression + ConvertToVariable(&expr); - expr.bc.OptimizeLocally(tempVariableOffsets); - bc->AddCode(&expr.bc); + // Jump to end of statement if expression is false + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + expr.bc.Instr(asBC_ClrHi); + expr.bc.InstrDWORD(asBC_JZ, afterLabel); + ReleaseTemporaryVariable(expr.type, &expr.bc); + + expr.bc.OptimizeLocally(tempVariableOffsets); + bc->AddCode(&expr.bc); + } } // Add a suspend bytecode inside the loop to guarantee @@ -3759,6 +4271,11 @@ void asCCompiler::CompileDoWhileStatement(asCScriptNode *wnode, asCByteCode *bc) // Compile expression asSExprContext expr(engine); CompileAssignment(wnode->lastChild, &expr); + + // Allow value types to be converted to bool using 'bool opImplConv()' + if( expr.type.dataType.GetObjectType() && (expr.type.dataType.GetObjectType()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&expr, asCDataType::CreatePrimitive(ttBool, false), wnode->lastChild, asIC_IMPLICIT_CONV); + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild); else @@ -3846,6 +4363,10 @@ void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode * asSExprContext expr(engine); CompileAssignment(enode->firstChild, &expr); + // Must not have unused ambiguous names + if( expr.IsClassMethod() || expr.IsGlobalFunc() ) + Error(TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME, enode); + // If we get here and there is still an unprocessed property // accessor, then process it as a get access. Don't call if there is // already a compile error, or we might report an error that is not valid @@ -3901,47 +4422,7 @@ void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ct lvalue.isExplicitHandle = ctx->type.isExplicitHandle; bool isExplicitHandle = ctx->type.isExplicitHandle; - if( !dt.IsObjectHandle() && - dt.GetObjectType() && (dt.GetBehaviour()->copyconstruct || dt.GetBehaviour()->copyfactory) ) - { - PrepareForAssignment(&lvalue.dataType, ctx, node, true); - - // Use the copy constructor/factory when available - CallCopyConstructor(dt, offset, IsVariableOnHeap(offset), &ctx->bc, ctx, node); - } - else - { - // Allocate and construct the temporary object - int r = CallDefaultConstructor(dt, offset, IsVariableOnHeap(offset), &ctx->bc, node); - if( r < 0 ) - { - Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node); - } - else - { - // Assign the object to the temporary variable - PrepareForAssignment(&lvalue.dataType, ctx, node, true); - - ctx->bc.InstrSHORT(asBC_PSF, (short)offset); - r = PerformAssignment(&lvalue, &ctx->type, &ctx->bc, node); - if( r < 0 ) - { - Error(TXT_FAILED_TO_CREATE_TEMP_OBJ, node); - } - - // Release any temp that may have been created as the result of opAssign - ReleaseTemporaryVariable(lvalue, &ctx->bc); - - // Pop the original reference - if( !lvalue.dataType.IsPrimitive() ) - ctx->bc.Instr(asBC_PopPtr); - } - - // If the expression was holding off on releasing a - // previously used object, we need to release it now - if( ctx->type.isTemporary ) - ReleaseTemporaryVariable(ctx->type, &ctx->bc); - } + CompileInitAsCopy(dt, offset, &ctx->bc, ctx, node, false); // Push the reference to the temporary variable on the stack ctx->bc.InstrSHORT(asBC_PSF, (short)offset); @@ -4025,7 +4506,7 @@ void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc) // Clean up the potential deferred parameters ProcessDeferredParams(&expr); asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, expr.type.dataType.Format().AddressOf(), v->type.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf()); Error(str, rnode); return; } @@ -4059,6 +4540,14 @@ void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc) } } + // Can't return the reference if could point to a local variable + if( expr.type.isRefToLocal ) + { + ProcessDeferredParams(&expr); + Error(TXT_REF_CANT_BE_TO_LOCAL_VAR, rnode); + return; + } + // All objects in the function must be cleaned up before the expression // is evaluated, otherwise there is a possibility that the cleanup will // invalidate the reference. @@ -4101,7 +4590,7 @@ void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc) if( expr.type.dataType != v->type ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, expr.type.dataType.Format().AddressOf(), v->type.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf()); Error(str, rnode); return; } @@ -4137,35 +4626,14 @@ void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc) if( !v->type.IsEqualExceptRefAndConst(expr.type.dataType) ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, expr.type.dataType.Format().AddressOf(), v->type.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, expr.type.dataType.Format(outFunc->nameSpace).AddressOf(), v->type.Format(outFunc->nameSpace).AddressOf()); Error(str, rnode->firstChild); return; } } int offset = outFunc->objectType ? -AS_PTR_SIZE : 0; - if( v->type.GetObjectType()->beh.copyconstruct ) - { - PrepareForAssignment(&v->type, &expr, rnode->firstChild, false); - CallCopyConstructor(v->type, offset, false, &expr.bc, &expr, rnode->firstChild, false, true); - } - else - { - // If the copy constructor doesn't exist, then a manual assignment needs to be done instead. - CallDefaultConstructor(v->type, offset, false, &expr.bc, rnode->firstChild, 0, true); - PrepareForAssignment(&v->type, &expr, rnode->firstChild, false); - expr.bc.InstrSHORT(asBC_PSF, (short)offset); - expr.bc.Instr(asBC_RDSPtr); - - asSExprContext lexpr(engine); - lexpr.type.Set(v->type); - lexpr.type.isLValue = true; - PerformAssignment(&lexpr.type, &expr.type, &expr.bc, rnode->firstChild); - expr.bc.Instr(asBC_PopPtr); - - // Release any temporary variable - ReleaseTemporaryVariable(expr.type, &expr.bc); - } + CompileInitAsCopy(v->type, offset, &expr.bc, &expr, rnode->firstChild, true); // Clean up the local variables and process deferred parameters DestroyVariables(&expr.bc); @@ -4286,7 +4754,7 @@ void asCCompiler::Information(const asCString &msg, asCScriptNode *node) builder->WriteInfo(script->name, msg, r, c, false); } -void asCCompiler::PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node) +void asCCompiler::PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node, asCObjectType *inType) { int r = 0, c = 0; asASSERT( node ); @@ -4294,9 +4762,11 @@ void asCCompiler::PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node) for( unsigned int n = 0; n < funcs.GetLength(); n++ ) { - asIScriptFunction *func = builder->GetFunctionDescription(funcs[n]); + asCScriptFunction *func = builder->GetFunctionDescription(funcs[n]); + if( inType && func->funcType == asFUNC_VIRTUAL ) + func = inType->virtualFunctionTable[func->vfTableIdx]; - builder->WriteInfo(script->name, func->GetDeclaration(true), r, c, false); + builder->WriteInfo(script->name, func->GetDeclaration(true, false, true), r, c, false); } } @@ -4312,6 +4782,7 @@ int asCCompiler::AllocateVariableNotIn(const asCDataType &type, bool isTemporary int asCCompiler::AllocateVariable(const asCDataType &type, bool isTemporary, bool forceOnHeap) { asCDataType t(type); + t.MakeReference(false); if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 1 ) t.SetTokenType(ttInt); @@ -4484,6 +4955,8 @@ void asCCompiler::ReleaseTemporaryVariable(asCTypeInfo &t, asCByteCode *bc) void asCCompiler::ReleaseTemporaryVariable(int offset, asCByteCode *bc) { + asASSERT( tempVariables.Exists(offset) ); + if( bc ) { // We need to call the destructor on the true variable type @@ -4596,7 +5069,7 @@ void asCCompiler::PrepareForAssignment(asCDataType *lvalue, asSExprContext *rctx if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lvalue->Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lvalue->Format(outFunc->nameSpace).AddressOf()); Error(str, node); rctx->type.SetDummy(); @@ -4632,7 +5105,7 @@ void asCCompiler::PrepareForAssignment(asCDataType *lvalue, asSExprContext *rctx if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lvalue->Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lvalue->Format(outFunc->nameSpace).AddressOf()); Error(str, node); } else @@ -4704,8 +5177,6 @@ int asCCompiler::PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asC *lvalue = ctx.type; bc->AddCode(&ctx.bc); - // TODO: Should find the opAssign method that implements the default copy behaviour. - // The beh->copy member will be removed. asSTypeBehaviour *beh = lvalue->dataType.GetBehaviour(); if( beh->copy && beh->copy != engine->scriptTypeBehaviours.beh.copy ) { @@ -4720,7 +5191,7 @@ int asCCompiler::PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asC // Call the default copy operator for script classes // This is done differently because the default copy operator // is registered as returning int&, but in reality it returns - // a reference to the object. + // a reference to the object. // TODO: Avoid this special case by implementing a copystub for // script classes that uses the default copy operator bc->Call(asBC_CALLSYS, beh->copy, 2*AS_PTR_SIZE); @@ -4772,7 +5243,177 @@ bool asCCompiler::CompileRefCast(asSExprContext *ctx, const asCDataType &to, boo asCArray ops; asUINT n; - if( ctx->type.dataType.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT ) + // A ref cast must not remove the constness + bool isConst = ctx->type.dataType.IsObjectConst(); + + // Find a suitable opCast or opImplCast method + asCObjectType *ot = ctx->type.dataType.GetObjectType(); + for( n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( (isExplicit && func->name == "opCast") || + func->name == "opImplCast" ) + { + // Is the operator for the output type? + if( func->returnType.GetObjectType() != to.GetObjectType() ) + continue; + + // Can't call a non-const function on a const object + if( isConst && !func->IsReadOnly() ) + continue; + + ops.PushLast(func->id); + } + } + + // Filter the list by constness to remove const methods if there are matching non-const methods + FilterConst(ops, !isConst); + + // It shouldn't be possible to have more than one + // TODO: Should be allowed to have different behaviours for const and non-const references + asASSERT( ops.GetLength() <= 1 ); + + // Should only have one behaviour for each output type + if( ops.GetLength() == 1 ) + { + conversionDone = true; + if( generateCode ) + { + // TODO: runtime optimize: Instead of producing bytecode for checking if the handle is + // null, we can create a special CALLSYS instruction that checks + // if the object pointer is null and if so sets the object register + // to null directly without executing the function. + // + // Alternatively I could force the ref cast behaviours be global + // functions with 1 parameter, even though they should still be + // registered with RegisterObjectBehaviour() + + if( ctx->type.dataType.GetObjectType()->flags & asOBJ_REF ) + { + // Add code to avoid calling the cast behaviour if the handle is already null, + // because that will raise a null pointer exception due to the cast behaviour + // being a class method, and the this pointer cannot be null. + + if( !ctx->type.isVariable ) + { + Dereference(ctx, true); + ConvertToVariable(ctx); + } + + // The reference on the stack will not be used + ctx->bc.Instr(asBC_PopPtr); + + // TODO: runtime optimize: should have immediate comparison for null pointer + int offset = AllocateVariable(asCDataType::CreateNullHandle(), true); + // TODO: runtime optimize: ClrVPtr is not necessary, because the VM should initialize the variable to null anyway (it is currently not done for null pointers though) + ctx->bc.InstrSHORT(asBC_ClrVPtr, (asWORD)offset); + ctx->bc.InstrW_W(asBC_CmpPtr, ctx->type.stackOffset, offset); + DeallocateVariable(offset); + + int afterLabel = nextLabel++; + ctx->bc.InstrDWORD(asBC_JZ, afterLabel); + + // Call the cast operator + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + ctx->bc.Instr(asBC_RDSPtr); + ctx->type.dataType.MakeReference(false); + + asCArray args; + MakeFunctionCall(ctx, ops[0], ctx->type.dataType.GetObjectType(), args, node); + ctx->bc.Instr(asBC_PopPtr); + + int endLabel = nextLabel++; + + ctx->bc.InstrINT(asBC_JMP, endLabel); + ctx->bc.Label((short)afterLabel); + + // Make a NULL pointer + ctx->bc.InstrSHORT(asBC_ClrVPtr, ctx->type.stackOffset); + ctx->bc.Label((short)endLabel); + + // Push the reference to the handle on the stack + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + } + else + { + // Value types cannot be null, so there is no need to check for this + + // Call the cast operator + asCArray args; + MakeFunctionCall(ctx, ops[0], ctx->type.dataType.GetObjectType(), args, node); + } + } + else + { + asCScriptFunction *func = engine->scriptFunctions[ops[0]]; + ctx->type.Set(func->returnType); + } + } + else if( ops.GetLength() == 0 && !(ctx->type.dataType.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT) ) + { + // Check for the generic ref cast method: void opCast(?&out) + for( n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( (isExplicit && func->name == "opCast") || + func->name == "opImplCast" ) + { + // Does the operator take the ?&out parameter? + if( func->returnType.GetTokenType() != ttVoid || + func->parameterTypes.GetLength() != 1 || + func->parameterTypes[0].GetTokenType() != ttQuestion || + func->inOutFlags[0] != asTM_OUTREF ) + continue; + + ops.PushLast(func->id); + } + } + + // It shouldn't be possible to have more than one + // TODO: Should be allowed to have different implementations for const and non-const references + asASSERT( ops.GetLength() <= 1 ); + + if( ops.GetLength() == 1 ) + { + conversionDone = true; + if( generateCode ) + { + asASSERT(to.IsObjectHandle()); + + // Allocate a temporary variable of the requested handle type + int stackOffset = AllocateVariableNotIn(to, true, false, ctx); + + // Pass the reference of that variable to the function as output parameter + asCDataType toRef(to); + toRef.MakeReference(true); + asCArray args; + asSExprContext arg(engine); + arg.bc.InstrSHORT(asBC_PSF, (short)stackOffset); + // Don't mark the variable as temporary, so it won't be freed too early + arg.type.SetVariable(toRef, stackOffset, false); + arg.type.isLValue = true; + arg.type.isExplicitHandle = true; + args.PushLast(&arg); + + // Call the behaviour method + MakeFunctionCall(ctx, ops[0], ctx->type.dataType.GetObjectType(), args, node); + + // Use the reference to the variable as the result of the expression + // Now we can mark the variable as temporary + ctx->type.SetVariable(toRef, stackOffset, true); + ctx->bc.InstrSHORT(asBC_PSF, (short)stackOffset); + } + else + { + // All casts are legal + ctx->type.Set(to); + } + } + } + + // If the script object didn't implement a matching opCast or opImplCast + // then check if the desired type is part of the hierarchy + if( !conversionDone && (ctx->type.dataType.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT) ) { // We need it to be a reference if( !ctx->type.dataType.IsReference() ) @@ -4819,163 +5460,10 @@ bool asCCompiler::CompileRefCast(asSExprContext *ctx, const asCDataType &to, boo ctx->type.dataType.SetObjectType(to.GetObjectType()); } } - } - else - { - // Find a suitable registered behaviour - asSTypeBehaviour *beh = &ctx->type.dataType.GetObjectType()->beh; - for( n = 0; n < beh->operators.GetLength(); n+= 2 ) - { - if( (isExplicit && asBEHAVE_REF_CAST == beh->operators[n]) || - asBEHAVE_IMPLICIT_REF_CAST == beh->operators[n] ) - { - int funcId = beh->operators[n+1]; - // Is the operator for the output type? - asCScriptFunction *func = engine->scriptFunctions[funcId]; - if( func->returnType.GetObjectType() != to.GetObjectType() ) - continue; - - ops.PushLast(funcId); - } - } - - // It shouldn't be possible to have more than one - asASSERT( ops.GetLength() <= 1 ); - - // Should only have one behaviour for each output type - if( ops.GetLength() == 1 ) - { - if( generateCode ) - { - // TODO: runtime optimize: Instead of producing bytecode for checking if the handle is - // null, we can create a special CALLSYS instruction that checks - // if the object pointer is null and if so sets the object register - // to null directly without executing the function. - // - // Alternatively I could force the ref cast behaviours be global - // functions with 1 parameter, even though they should still be - // registered with RegisterObjectBehaviour() - - // Add code to avoid calling the cast behaviour if the handle is already null, - // because that will raise a null pointer exception due to the cast behaviour - // being a class method, and the this pointer cannot be null. - - if( ctx->type.isVariable ) - ctx->bc.Instr(asBC_PopPtr); - else - { - Dereference(ctx, true); - ConvertToVariable(ctx); - } - - // TODO: runtime optimize: should have immediate comparison for null pointer - int offset = AllocateVariable(asCDataType::CreateNullHandle(), true); - // TODO: runtime optimize: ClrVPtr is not necessary, because the VM should initialize the variable to null anyway (it is currently not done for null pointers though) - ctx->bc.InstrSHORT(asBC_ClrVPtr, (asWORD)offset); - ctx->bc.InstrW_W(asBC_CmpPtr, ctx->type.stackOffset, offset); - DeallocateVariable(offset); - - int afterLabel = nextLabel++; - ctx->bc.InstrDWORD(asBC_JZ, afterLabel); - - // Call the cast operator - ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); - ctx->bc.Instr(asBC_RDSPtr); - ctx->type.dataType.MakeReference(false); - - asCTypeInfo objType = ctx->type; - asCArray args; - MakeFunctionCall(ctx, ops[0], objType.dataType.GetObjectType(), args, node); - - ctx->bc.Instr(asBC_PopPtr); - - int endLabel = nextLabel++; - - ctx->bc.InstrINT(asBC_JMP, endLabel); - ctx->bc.Label((short)afterLabel); - - // Make a NULL pointer - ctx->bc.InstrSHORT(asBC_ClrVPtr, ctx->type.stackOffset); - ctx->bc.Label((short)endLabel); - - // Since we're receiving a handle, we can release the original variable - ReleaseTemporaryVariable(objType, &ctx->bc); - - // Push the reference to the handle on the stack - ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); - } - else - { - asCScriptFunction *func = engine->scriptFunctions[ops[0]]; - ctx->type.Set(func->returnType); - } - } - else if( ops.GetLength() == 0 ) - { - // Check for the generic ref cast behaviour - for( n = 0; n < beh->operators.GetLength(); n+= 2 ) - { - if( (isExplicit && asBEHAVE_REF_CAST == beh->operators[n]) || - asBEHAVE_IMPLICIT_REF_CAST == beh->operators[n] ) - { - int funcId = beh->operators[n+1]; - - // Does the operator take the ?&out parameter? - asCScriptFunction *func = engine->scriptFunctions[funcId]; - if( func->parameterTypes.GetLength() != 1 || - func->parameterTypes[0].GetTokenType() != ttQuestion || - func->inOutFlags[0] != asTM_OUTREF ) - continue; - - ops.PushLast(funcId); - } - } - - // It shouldn't be possible to have more than one - asASSERT( ops.GetLength() <= 1 ); - - if( ops.GetLength() == 1 ) - { - if( generateCode ) - { - asASSERT(to.IsObjectHandle()); - - // Allocate a temporary variable of the requested handle type - int stackOffset = AllocateVariableNotIn(to, true, false, ctx); - - // Pass the reference of that variable to the function as output parameter - asCDataType toRef(to); - toRef.MakeReference(true); - asCArray args; - asSExprContext arg(engine); - arg.bc.InstrSHORT(asBC_PSF, (short)stackOffset); - // Don't mark the variable as temporary, so it won't be freed too early - arg.type.SetVariable(toRef, stackOffset, false); - arg.type.isLValue = true; - arg.type.isExplicitHandle = true; - args.PushLast(&arg); - - asCTypeInfo prev = ctx->type; - - // Call the behaviour method - MakeFunctionCall(ctx, ops[0], ctx->type.dataType.GetObjectType(), args, node); - - // Release previous temporary variable - ReleaseTemporaryVariable(prev, &ctx->bc); - - // Use the reference to the variable as the result of the expression - // Now we can mark the variable as temporary - ctx->type.SetVariable(toRef, stackOffset, true); - ctx->bc.InstrSHORT(asBC_PSF, (short)stackOffset); - } - else - { - // All casts are legal - ctx->type.Set(to); - } - } - } + // A ref cast must not remove the constness + if( isConst ) + ctx->type.dataType.MakeHandleToConst(true); } return conversionDone; @@ -5008,6 +5496,9 @@ asUINT asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const ctx->type.SetConstantDW(out, value); ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + // Reset the enum value since we no longer need it + ctx->enumValue = ""; + // It wasn't really a conversion. The compiler just resolved the ambiguity (or not) return asCC_NO_CONV; } @@ -5373,6 +5864,10 @@ asUINT asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &t if( ctx->type.dataType.GetTokenType() == ttVoid ) return asCC_NO_CONV; + // No conversion from class method to any type (it requires delegate) + if( ctx->IsClassMethod() ) + return asCC_NO_CONV; + // Do we want a var type? if( to.GetTokenType() == ttQuestion ) { @@ -5410,7 +5905,7 @@ asUINT asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asC if( convType != asIC_IMPLICIT_CONV && node ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); Error(str, node); } return asCC_NO_CONV; @@ -5421,80 +5916,110 @@ asUINT asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asC // Find matching value cast behaviours // Here we're only interested in those that convert the type to a primitive type asCArray funcs; - asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour(); - if( beh ) + asCObjectType *ot = ctx->type.dataType.GetObjectType(); + if( ot == 0 ) { - if( convType == asIC_EXPLICIT_VAL_CAST ) + if( convType != asIC_IMPLICIT_CONV && node ) { - for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 ) - { - // accept both implicit and explicit cast - if( (beh->operators[n] == asBEHAVE_VALUE_CAST || - beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST) && - builder->GetFunctionDescription(beh->operators[n+1])->returnType.IsPrimitive() ) - funcs.PushLast(beh->operators[n+1]); - } + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); } - else + return asCC_NO_CONV; + } + + + if( convType == asIC_EXPLICIT_VAL_CAST ) + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) { - for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 ) - { - // accept only implicit cast - if( beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST && - builder->GetFunctionDescription(beh->operators[n+1])->returnType.IsPrimitive() ) - funcs.PushLast(beh->operators[n+1]); - } + // accept both implicit and explicit cast + asCScriptFunction *mthd = engine->scriptFunctions[ot->methods[n]]; + if( (mthd->name == "opConv" || mthd->name == "opImplConv") && + mthd->parameterTypes.GetLength() == 0 && + mthd->returnType.IsPrimitive() ) + funcs.PushLast(ot->methods[n]); + } + } + else + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) + { + // accept only implicit cast + asCScriptFunction *mthd = engine->scriptFunctions[ot->methods[n]]; + if( mthd->name == "opImplConv" && + mthd->parameterTypes.GetLength() == 0 && + mthd->returnType.IsPrimitive() ) + funcs.PushLast(ot->methods[n]); } } - // This matrix describes the priorities of the types to search for, for each target type - // The first column is the target type, the priorities goes from left to right - eTokenType matchMtx[10][10] = - { - {ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}, - {ttFloat, ttDouble, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}, - {ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat}, - {ttUInt64, ttInt64, ttUInt, ttInt, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat}, - {ttInt, ttUInt, ttInt64, ttUInt64, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat}, - {ttUInt, ttInt, ttUInt64, ttInt64, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat}, - {ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttInt8, ttUInt8, ttDouble, ttFloat}, - {ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttUInt8, ttInt8, ttDouble, ttFloat}, - {ttInt8, ttUInt8, ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttDouble, ttFloat}, - {ttUInt8, ttInt8, ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttDouble, ttFloat}, - }; - - // Which row to use? - eTokenType *row = 0; - for( unsigned int type = 0; type < 10; type++ ) - { - if( to.GetTokenType() == matchMtx[type][0] ) - { - row = &matchMtx[type][0]; - break; - } - } - - // Find the best matching cast operator int funcId = 0; - if( row ) + if( to.IsMathType() ) { - asCDataType target(to); - - // Priority goes from left to right in the matrix - for( unsigned int attempt = 0; attempt < 10 && funcId == 0; attempt++ ) + // This matrix describes the priorities of the types to search for, for each target type + // The first column is the target type, the priorities goes from left to right + eTokenType matchMtx[10][10] = { - target.SetTokenType(row[attempt]); - for( unsigned int n = 0; n < funcs.GetLength(); n++ ) + {ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}, + {ttFloat, ttDouble, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}, + {ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt64, ttInt64, ttUInt, ttInt, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt, ttUInt, ttInt64, ttUInt64, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt, ttInt, ttUInt64, ttInt64, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt8, ttUInt8, ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttDouble, ttFloat}, + {ttUInt8, ttInt8, ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttDouble, ttFloat}, + }; + + // Which row to use? + eTokenType *row = 0; + for( unsigned int type = 0; type < 10; type++ ) + { + if( to.GetTokenType() == matchMtx[type][0] ) { - asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]); - if( descr->returnType.IsEqualExceptRefAndConst(target) ) + row = &matchMtx[type][0]; + break; + } + } + + // Find the best matching cast operator + if( row ) + { + asCDataType target(to); + + // Priority goes from left to right in the matrix + for( unsigned int attempt = 0; attempt < 10 && funcId == 0; attempt++ ) + { + target.SetTokenType(row[attempt]); + for( unsigned int n = 0; n < funcs.GetLength(); n++ ) { - funcId = funcs[n]; - break; + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]); + if( descr->returnType.IsEqualExceptRefAndConst(target) ) + { + funcId = funcs[n]; + break; + } } } } } + else + { + // Only accept the exact conversion for non-math types + + // Find the matching cast operator + for( unsigned int n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]); + if( descr->returnType.IsEqualExceptRefAndConst(to) ) + { + funcId = funcs[n]; + break; + } + } + } // Did we find a suitable function? if( funcId != 0 ) @@ -5502,13 +6027,8 @@ asUINT asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asC asCScriptFunction *descr = builder->GetFunctionDescription(funcId); if( generateCode ) { - asCTypeInfo objType = ctx->type; - Dereference(ctx, true); - PerformFunctionCall(funcId, ctx); - - ReleaseTemporaryVariable(objType, &ctx->bc); } else ctx->type.Set(descr->returnType); @@ -5516,16 +6036,71 @@ asUINT asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asC // Allow one more implicit conversion to another primitive type return asCC_OBJ_TO_PRIMITIVE_CONV + ImplicitConversion(ctx, to, node, convType, generateCode, false); } - else + + // TODO: clean-up: This part is similar to what is in ImplicitConvObjectValue + // If no direct conversion is found we should look for the generic form 'void opConv(?&out)' + funcs.SetLength(0); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) { - if( convType != asIC_IMPLICIT_CONV && node ) + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( ((convType == asIC_EXPLICIT_VAL_CAST) && func->name == "opConv") || + func->name == "opImplConv" ) { - asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); - Error(str, node); + // Does the operator take the ?&out parameter? + if( func->returnType != asCDataType::CreatePrimitive(ttVoid, false) || + func->parameterTypes.GetLength() != 1 || + func->parameterTypes[0].GetTokenType() != ttQuestion || + func->inOutFlags[0] != asTM_OUTREF ) + continue; + + funcs.PushLast(ot->methods[n]); } } + // TODO: If there are multiple valid value casts, then we must choose the most appropriate one + asASSERT( funcs.GetLength() <= 1 ); + + if( funcs.GetLength() == 1 ) + { + if( generateCode ) + { + // Allocate a temporary variable of the requested type + int stackOffset = AllocateVariableNotIn(to, true, false, ctx); + CallDefaultConstructor(to, stackOffset, IsVariableOnHeap(stackOffset), &ctx->bc, node); + + // Pass the reference of that variable to the function as output parameter + asCDataType toRef(to); + toRef.MakeReference(true); + toRef.MakeReadOnly(false); + asCArray args; + asSExprContext arg(engine); + // Don't mark the variable as temporary, so it won't be freed too early + arg.type.SetVariable(toRef, stackOffset, false); + arg.type.isLValue = true; + arg.exprNode = node; + args.PushLast(&arg); + + // Call the behaviour method + MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.GetObjectType(), args, node); + + // Use the reference to the variable as the result of the expression + // Now we can mark the variable as temporary + toRef.MakeReference(false); + ctx->type.SetVariable(toRef, stackOffset, true); + } + else + ctx->type.Set(to); + + return asCC_OBJ_TO_PRIMITIVE_CONV; + } + + if( convType != asIC_IMPLICIT_CONV && node ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + } + return asCC_NO_CONV; } @@ -5545,7 +6120,7 @@ asUINT asCCompiler::ImplicitConvObjectRef(asSExprContext *ctx, const asCDataType asASSERT(ctx->type.dataType.GetObjectType() || ctx->methodName != ""); - // First attempt to convert the base type without instanciating another instance + // First attempt to convert the base type without instantiating another instance if( to.GetObjectType() != ctx->type.dataType.GetObjectType() && ctx->methodName == "" ) { // If the to type is an interface and the from type implements it, then we can convert it immediately @@ -5563,17 +6138,9 @@ asUINT asCCompiler::ImplicitConvObjectRef(asSExprContext *ctx, const asCDataType // If the types are not equal yet, then we may still be able to find a reference cast else if( ctx->type.dataType.GetObjectType() != to.GetObjectType() ) { - // A ref cast must not remove the constness - bool isConst = false; - if( (ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst()) || - (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly()) ) - isConst = true; - // We may still be able to find an implicit ref cast behaviour CompileRefCast(ctx, to, convType == asIC_EXPLICIT_REF_CAST, node, generateCode); - ctx->type.dataType.MakeHandleToConst(isConst); - // Was the conversion done? if( ctx->type.dataType.GetObjectType() == to.GetObjectType() ) return asCC_REF_CONV; @@ -5607,7 +6174,7 @@ asUINT asCCompiler::ImplicitConvObjectRef(asSExprContext *ctx, const asCDataType { asCString nsName = ctx->methodName.SubString(0, pos+2); // Trim off the last :: - if( nsName.GetLength() > 2 ) + if( nsName.GetLength() > 2 ) nsName.SetLength(nsName.GetLength()-2); ns = DetermineNameSpace(nsName); name = ctx->methodName.SubString(pos+2); @@ -5651,7 +6218,7 @@ asUINT asCCompiler::ImplicitConvObjectRef(asSExprContext *ctx, const asCDataType return asCC_NO_CONV; } -asUINT asCCompiler::ImplicitConvObjectValue(asSExprContext *ctx, const asCDataType &to, asCScriptNode * /*node*/, EImplicitConv convType, bool generateCode) +asUINT asCCompiler::ImplicitConvObjectValue(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode) { asUINT cost = asCC_NO_CONV; @@ -5660,31 +6227,36 @@ asUINT asCCompiler::ImplicitConvObjectValue(asSExprContext *ctx, const asCDataTy if( to.GetObjectType() != ctx->type.dataType.GetObjectType() ) { // TODO: Implement support for implicit constructor/factory + asCObjectType *ot = ctx->type.dataType.GetObjectType(); + if( ot == 0 ) + return cost; asCArray funcs; - asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour(); - if( beh ) + if( convType == asIC_EXPLICIT_VAL_CAST ) { - if( convType == asIC_EXPLICIT_VAL_CAST ) + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) { - for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 ) - { - // accept both implicit and explicit cast - if( (beh->operators[n] == asBEHAVE_VALUE_CAST || - beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST) && - builder->GetFunctionDescription(beh->operators[n+1])->returnType.GetObjectType() == to.GetObjectType() ) - funcs.PushLast(beh->operators[n+1]); - } + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + + // accept both implicit and explicit cast + if( (func->name == "opConv" || + func->name == "opImplConv") && + func->returnType.GetObjectType() == to.GetObjectType() && + func->parameterTypes.GetLength() == 0 ) + funcs.PushLast(ot->methods[n]); } - else + } + else + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) { - for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 ) - { - // accept only implicit cast - if( beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST && - builder->GetFunctionDescription(beh->operators[n+1])->returnType.GetObjectType() == to.GetObjectType() ) - funcs.PushLast(beh->operators[n+1]); - } + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + + // accept only implicit cast + if( func->name == "opImplConv" && + func->returnType.GetObjectType() == to.GetObjectType() && + func->parameterTypes.GetLength() == 0 ) + funcs.PushLast(ot->methods[n]); } } @@ -5696,7 +6268,6 @@ asUINT asCCompiler::ImplicitConvObjectValue(asSExprContext *ctx, const asCDataTy asCScriptFunction *f = builder->GetFunctionDescription(funcs[0]); if( generateCode ) { - asCTypeInfo objType = ctx->type; Dereference(ctx, true); bool useVariable = false; @@ -5716,13 +6287,72 @@ asUINT asCCompiler::ImplicitConvObjectValue(asSExprContext *ctx, const asCDataTy } PerformFunctionCall(funcs[0], ctx, false, 0, 0, useVariable, stackOffset); - ReleaseTemporaryVariable(objType, &ctx->bc); } else ctx->type.Set(f->returnType); cost = asCC_TO_OBJECT_CONV; } + else + { + // TODO: cleanup: This part is similar to the second half of ImplicitConvObjectToPrimitive + // Look for a value cast with variable type + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( ((convType == asIC_EXPLICIT_VAL_CAST) && func->name == "opConv") || + func->name == "opImplConv" ) + { + // Does the operator take the ?&out parameter? + if( func->returnType != asCDataType::CreatePrimitive(ttVoid, false) || + func->parameterTypes.GetLength() != 1 || + func->parameterTypes[0].GetTokenType() != ttQuestion || + func->inOutFlags[0] != asTM_OUTREF ) + continue; + + funcs.PushLast(ot->methods[n]); + } + } + + // TODO: If there are multiple valid value casts, then we must choose the most appropriate one + asASSERT( funcs.GetLength() <= 1 ); + + if( funcs.GetLength() == 1 ) + { + cost = asCC_TO_OBJECT_CONV; + if( generateCode ) + { + // Allocate a temporary variable of the requested type + int stackOffset = AllocateVariableNotIn(to, true, false, ctx); + CallDefaultConstructor(to, stackOffset, IsVariableOnHeap(stackOffset), &ctx->bc, node); + + // Pass the reference of that variable to the function as output parameter + asCDataType toRef(to); + toRef.MakeReference(false); + asCArray args; + asSExprContext arg(engine); + arg.bc.InstrSHORT(asBC_PSF, (short)stackOffset); + // Don't mark the variable as temporary, so it won't be freed too early + arg.type.SetVariable(toRef, stackOffset, false); + arg.type.isLValue = true; + arg.exprNode = node; + args.PushLast(&arg); + + // Call the behaviour method + MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.GetObjectType(), args, node); + + // Use the reference to the variable as the result of the expression + // Now we can mark the variable as temporary + ctx->type.SetVariable(toRef, stackOffset, true); + ctx->bc.InstrSHORT(asBC_PSF, (short)stackOffset); + } + else + { + // All casts are legal + ctx->type.Set(to); + } + } + } } return cost; @@ -5743,14 +6373,14 @@ asUINT asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDat asCArray args; args.PushLast(ctx); - cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, node, 0, 0, false, true, false); + cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, node, 0, 0, 0, false, true, false); // Did we find a matching constructor? if( funcs.GetLength() == 1 ) { if( generateCode ) { - // If the ASHANDLE receives a variable type parameter, then we need to + // If the ASHANDLE receives a variable type parameter, then we need to // make sure the expression is treated as a handle and not as a value asCScriptFunction *func = engine->scriptFunctions[funcs[0]]; if( func->parameterTypes[0].GetTokenType() == ttQuestion ) @@ -5860,7 +6490,7 @@ asUINT asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDat { asASSERT(node); asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); Error(str, node); } } @@ -6190,7 +6820,7 @@ asUINT asCCompiler::ImplicitConvPrimitiveToObject(asSExprContext *ctx, const asC arg.exprNode = ctx->exprNode; // Use the same node for compiler messages asCArray args; args.PushLast(&arg); - asUINT cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, 0, 0, objType, false, true, false); + asUINT cost = asCC_TO_OBJECT_CONV + MatchFunctions(funcs, args, 0, 0, 0, objType, false, true, false); if( funcs.GetLength() != 1 ) return asCC_NO_CONV; @@ -6202,7 +6832,8 @@ asUINT asCCompiler::ImplicitConvPrimitiveToObject(asSExprContext *ctx, const asC // TODO: clean up: This part is similar to CompileConstructCall(). It should be put in a common function - bool onHeap = true; + // Clear the type of ctx, as the type is moved to the arg + ctx->type.SetDummy(); // Value types and script types are allocated through the constructor asCTypeInfo tempObj; @@ -6212,7 +6843,7 @@ asUINT asCCompiler::ImplicitConvPrimitiveToObject(asSExprContext *ctx, const asC tempObj.isTemporary = true; tempObj.isVariable = true; - onHeap = IsVariableOnHeap(tempObj.stackOffset); + bool onHeap = IsVariableOnHeap(tempObj.stackOffset); // Push the address of the object on the stack if( onHeap ) @@ -6474,7 +7105,7 @@ void asCCompiler::ImplicitConversionConstant(asSExprContext *from, const asCData else if( from->type.dataType.IsIntegerType() ) { // Verify that it is possible to convert to unsigned without loosing negative - if( (from->type.dataType.GetSizeInMemoryBytes() > 4 && asINT64(from->type.qwordValue) < 0) || + if( (from->type.dataType.GetSizeInMemoryBytes() > 4 && asINT64(from->type.qwordValue) < 0) || (from->type.dataType.GetSizeInMemoryBytes() <= 4 && from->type.intValue < 0) ) { if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); @@ -6761,7 +7392,7 @@ void asCCompiler::ImplicitConversionConstant(asSExprContext *from, const asCData } } -int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, int op, asCScriptNode *opNode) +int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, eTokenType op, asCScriptNode *opNode) { // Don't allow any operators on expressions that take address of class method // If methodName is set but the type is not an object, then it is a global function @@ -6787,16 +7418,8 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr { if( op != ttAssignment ) { - // TODO: getset: We may actually be able to support this, if we can - // guarantee that the object reference will stay valid - // between the calls to the get and set accessors. - - // Process the property to free the memory - ProcessPropertySetAccessor(lctx, rctx, opNode); - - // Compound assignments are not allowed for properties - Error(TXT_COMPOUND_ASGN_WITH_PROP, opNode); - return -1; + // Generate the code for the compound assignment, i.e. get the value, apply operator, then set the value + return ProcessPropertyGetSetAccessor(ctx, lctx, rctx, op, opNode); } // It is not allowed to do a handle assignment on a property @@ -6887,12 +7510,12 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr if( op != ttAssignment ) { asCString str; - str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf()); + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, lexpr); return -1; } - if( lctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE ) + if( lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE) ) { // The object is a value type but that should be treated as a handle @@ -6911,21 +7534,23 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, rexpr); return -1; } } } - if( CompileOverloadedDualOperator(opNode, lctx, rctx, ctx) ) + if( CompileOverloadedDualOperator(opNode, lctx, rctx, ctx, true) ) { // An overloaded assignment operator was found (or a compilation error occured) return 0; } // The object must implement the opAssign method - Error(TXT_NO_APPROPRIATE_OPASSIGN, opNode); + asCString msg; + msg.Format(TXT_NO_APPROPRIATE_OPHNDLASSIGN_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(msg.AddressOf(), opNode); return -1; } else @@ -6937,7 +7562,7 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, rexpr); return -1; } @@ -6959,16 +7584,6 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr } else // if( lctx->type.dataType.IsObject() ) { - // An ASHANDLE type must not allow a value assignment, as - // the opAssign operator is used for the handle assignment - if( lctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE ) - { - asCString str; - str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf()); - Error(str, lexpr); - return -1; - } - // The lvalue reference may be marked as a temporary, if for example // it was originated as a handle returned from a function. In such // cases it must be possible to assign values to it anyway. @@ -6996,7 +7611,7 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr if( op != ttAssignment ) { asCString str; - str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf()); + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, lexpr); return -1; } @@ -7023,11 +7638,13 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr asCDataType dt = lctx->type.dataType; dt.MakeReference(true); dt.MakeReadOnly(true); - PrepareArgument(&dt, rctx, rexpr, true, 1, !needConversion); + int r = PrepareArgument(&dt, rctx, rexpr, true, 1, !needConversion); + if( r < 0 ) + return -1; if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) ) { asCString str; - str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf()); + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, rexpr); return -1; } @@ -7103,6 +7720,11 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx) int r = CompileExpression(cexpr, &e); if( r < 0 ) e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + + // Allow value types to be converted to bool using 'bool opImplConv()' + if( e.type.dataType.GetObjectType() && (e.type.dataType.GetObjectType()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(&e, asCDataType::CreatePrimitive(ttBool, false), cexpr, asIC_IMPLICIT_CONV); + if( r >= 0 && !e.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) { Error(TXT_EXPR_MUST_BE_BOOL, cexpr); @@ -7198,101 +7820,182 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx) } else { - // Allocate temporary variable and copy the result to that one - asCTypeInfo temp; - temp = le.type; - temp.dataType.MakeReference(false); - temp.dataType.MakeReadOnly(false); - - // Make sure the variable isn't used in any of the expressions, - // as it would be overwritten which may cause crashes or less visible bugs - int l = int(reservedVariables.GetLength()); - e.bc.GetVarsUsed(reservedVariables); - le.bc.GetVarsUsed(reservedVariables); - re.bc.GetVarsUsed(reservedVariables); - int offset = AllocateVariable(temp.dataType, true, false); - reservedVariables.SetLength(l); - - temp.SetVariable(temp.dataType, offset, true); + // Allow "(a ? b : c) = d;" and "return (a ? b : c);" (where the latter returns the reference) + // + // Restrictions for the condition to be used as lvalue: + // 1. both b and c must be of the same type and be lvalue references + // 2. neither of the expressions can have any deferred arguments + // that would have to be cleaned up after the reference + // 3. neither expression can be temporary + // + // If either expression is local, the resulting lvalue is not valid + // for return since it is not allowed to return references to local + // variables. + // + // The reference to the local variable must be loaded into the register, + // the resulting expression must not be considered as a local variable + // with a stack offset (i.e. it will not be allowed to use asBC_VAR) - // TODO: copy: Use copy constructor if available. See PrepareTemporaryObject() - - CallDefaultConstructor(temp.dataType, offset, IsVariableOnHeap(offset), &ctx->bc, expr); - - // Put the code for the condition expression on the output - MergeExprBytecode(ctx, &e); - - // Add the branch decision - ctx->type = e.type; - ConvertToVariable(ctx); - ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); - ctx->bc.Instr(asBC_ClrHi); - ctx->bc.InstrDWORD(asBC_JZ, elseLabel); - ReleaseTemporaryVariable(ctx->type, &ctx->bc); - - // Assign the result of the left expression to the temporary variable - asCTypeInfo rtemp; - rtemp = temp; - if( rtemp.dataType.IsObjectHandle() ) - rtemp.isExplicitHandle = true; - - PrepareForAssignment(&rtemp.dataType, &le, cexpr->next, true); - MergeExprBytecode(ctx, &le); - - if( !rtemp.dataType.IsPrimitive() ) + if( le.type.isLValue && re.type.isLValue && + le.deferredParams.GetLength() == 0 && re.deferredParams.GetLength() ==0 && + !le.type.isTemporary && !re.type.isTemporary && + le.type.dataType == re.type.dataType ) { - ctx->bc.InstrSHORT(asBC_PSF, (short)offset); - rtemp.dataType.MakeReference(IsVariableOnHeap(offset)); + // Put the code for the condition expression on the output + MergeExprBytecode(ctx, &e); + + // Add the branch decision + ctx->type = e.type; + ConvertToVariable(ctx); + ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JZ, elseLabel); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + // Start of the left expression + MergeExprBytecode(ctx, &le); + if( !le.type.dataType.IsReference() && le.type.isVariable ) + { + // Load the address of the variable into the register + ctx->bc.InstrSHORT(asBC_LDV, le.type.stackOffset); + } + + ctx->bc.InstrINT(asBC_JMP, afterLabel); + + // Start of the right expression + ctx->bc.Label((short)elseLabel); + + MergeExprBytecode(ctx, &re); + if( !re.type.dataType.IsReference() && re.type.isVariable ) + { + // Load the address of the variable into the register + ctx->bc.InstrSHORT(asBC_LDV, re.type.stackOffset); + } + + ctx->bc.Label((short)afterLabel); + + // In case the options were to objects, it is necessary to dereference the pointer on + // the stack so it will point to the actual object, instead of the variable + if( le.type.dataType.IsReference() && le.type.isVariable && le.type.dataType.IsObject() && !le.type.dataType.IsObjectHandle() ) + { + asASSERT( re.type.dataType.IsReference() && re.type.isVariable && re.type.dataType.IsObject() && !re.type.dataType.IsObjectHandle() ); + + ctx->bc.Instr(asBC_RDSPtr); + } + + // The result is an lvalue + ctx->type.isLValue = true; + ctx->type.dataType = le.type.dataType; + if( ctx->type.dataType.IsPrimitive() || ctx->type.dataType.IsObjectHandle() ) + ctx->type.dataType.MakeReference(true); + else + ctx->type.dataType.MakeReference(false); + + // It can't be a treated as a variable, since we don't know which one was used + ctx->type.isVariable = false; + ctx->type.isTemporary = false; + + // Must remember if the reference was to a local variable, since it must not be allowed to be returned + ctx->type.isRefToLocal = le.type.isVariable || le.type.isRefToLocal || re.type.isVariable || re.type.isRefToLocal; } - asCTypeInfo result; - result = rtemp; - PerformAssignment(&result, &le.type, &ctx->bc, cexpr->next); - if( !result.dataType.IsPrimitive() ) - ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer) - - // Release the old temporary variable - ReleaseTemporaryVariable(le.type, &ctx->bc); - - ctx->bc.InstrINT(asBC_JMP, afterLabel); - - // Start of the right expression - ctx->bc.Label((short)elseLabel); - - // Copy the result to the same temporary variable - PrepareForAssignment(&rtemp.dataType, &re, cexpr->next, true); - MergeExprBytecode(ctx, &re); - - if( !rtemp.dataType.IsPrimitive() ) + else { - ctx->bc.InstrSHORT(asBC_PSF, (short)offset); - rtemp.dataType.MakeReference(IsVariableOnHeap(offset)); + // Allocate temporary variable and copy the result to that one + asCTypeInfo temp; + temp = le.type; + temp.dataType.MakeReference(false); + temp.dataType.MakeReadOnly(false); + + // Make sure the variable isn't used in any of the expressions, + // as it would be overwritten which may cause crashes or less visible bugs + int l = int(reservedVariables.GetLength()); + e.bc.GetVarsUsed(reservedVariables); + le.bc.GetVarsUsed(reservedVariables); + re.bc.GetVarsUsed(reservedVariables); + int offset = AllocateVariable(temp.dataType, true, false); + reservedVariables.SetLength(l); + + temp.SetVariable(temp.dataType, offset, true); + + // TODO: copy: Use copy constructor if available. See PrepareTemporaryObject() + + CallDefaultConstructor(temp.dataType, offset, IsVariableOnHeap(offset), &ctx->bc, expr); + + // Put the code for the condition expression on the output + MergeExprBytecode(ctx, &e); + + // Add the branch decision + ctx->type = e.type; + ConvertToVariable(ctx); + ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JZ, elseLabel); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + // Assign the result of the left expression to the temporary variable + asCTypeInfo rtemp; + rtemp = temp; + if( rtemp.dataType.IsObjectHandle() ) + rtemp.isExplicitHandle = true; + + PrepareForAssignment(&rtemp.dataType, &le, cexpr->next, true); + MergeExprBytecode(ctx, &le); + + if( !rtemp.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + rtemp.dataType.MakeReference(IsVariableOnHeap(offset)); + } + asCTypeInfo result; + result = rtemp; + PerformAssignment(&result, &le.type, &ctx->bc, cexpr->next); + if( !result.dataType.IsPrimitive() ) + ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer) + + // Release the old temporary variable + ReleaseTemporaryVariable(le.type, &ctx->bc); + + ctx->bc.InstrINT(asBC_JMP, afterLabel); + + // Start of the right expression + ctx->bc.Label((short)elseLabel); + + // Copy the result to the same temporary variable + PrepareForAssignment(&rtemp.dataType, &re, cexpr->next, true); + MergeExprBytecode(ctx, &re); + + if( !rtemp.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + rtemp.dataType.MakeReference(IsVariableOnHeap(offset)); + } + result = rtemp; + PerformAssignment(&result, &re.type, &ctx->bc, cexpr->next); + if( !result.dataType.IsPrimitive() ) + ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer) + + // Release the old temporary variable + ReleaseTemporaryVariable(re.type, &ctx->bc); + + ctx->bc.Label((short)afterLabel); + + // Make sure both expressions have the same type + if( !le.type.dataType.IsEqualExceptConst(re.type.dataType) ) + Error(TXT_BOTH_MUST_BE_SAME, expr); + + // Set the temporary variable as output + ctx->type = rtemp; + ctx->type.isExplicitHandle = isExplicitHandle; + + if( !ctx->type.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + ctx->type.dataType.MakeReference(IsVariableOnHeap(offset)); + } + + // Make sure the output isn't marked as being a literal constant + ctx->type.isConstant = false; } - result = rtemp; - PerformAssignment(&result, &re.type, &ctx->bc, cexpr->next); - if( !result.dataType.IsPrimitive() ) - ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer) - - // Release the old temporary variable - ReleaseTemporaryVariable(re.type, &ctx->bc); - - ctx->bc.Label((short)afterLabel); - - // Make sure both expressions have the same type - if( !le.type.dataType.IsEqualExceptConst(re.type.dataType) ) - Error(TXT_BOTH_MUST_BE_SAME, expr); - - // Set the temporary variable as output - ctx->type = rtemp; - ctx->type.isExplicitHandle = isExplicitHandle; - - if( !ctx->type.dataType.IsPrimitive() ) - { - ctx->bc.InstrSHORT(asBC_PSF, (short)offset); - ctx->type.dataType.MakeReference(IsVariableOnHeap(offset)); - } - - // Make sure the output isn't marked as being a literal constant - ctx->type.isConstant = false; } } else @@ -7311,6 +8014,46 @@ int asCCompiler::CompileExpression(asCScriptNode *expr, asSExprContext *ctx) { asASSERT(expr->nodeType == snExpression); + // Check if this is an initialization of a temp object with an initialization list + if( expr->firstChild && expr->firstChild->nodeType == snDataType ) + { + // TODO: It should be possible to infer the type of the object from where the + // expression will be used. The compilation of the initialization list + // should be deferred until it is known for what it will be used. It will + // then for example be possible to write expressions like: + // + // @dict = {{'key', 'value'}}; + // funcTakingArrayOfInt({1,2,3,4}); + + // Determine the type of the temporary object + asCDataType dt = builder->CreateDataTypeFromNode(expr->firstChild, script, outFunc->nameSpace); + + // Do not allow constructing non-shared types in shared functions + if( outFunc->IsShared() && + dt.GetObjectType() && !dt.GetObjectType()->IsShared() ) + { + asCString msg; + msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetObjectType()->name.AddressOf()); + Error(msg, expr); + } + + // Allocate and initialize the temporary object + int offset = AllocateVariable(dt, true); + CompileInitialization(expr->lastChild, &ctx->bc, dt, expr, offset, 0, 0); + + // Push the reference to the object on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + ctx->type.SetVariable(dt, offset, true); + ctx->type.isLValue = false; + + // If the variable is allocated on the heap we have a reference, + // otherwise the actual object pointer is pushed on the stack. + if( IsVariableOnHeap(offset) ) + ctx->type.dataType.MakeReference(true); + + return 0; + } + // Convert to polish post fix, i.e: a+b => ab+ // The algorithm that I've implemented here is similar to @@ -7556,6 +8299,26 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s asCObjectProperty *prop = builder->GetObjectProperty(dt, name.AddressOf()); if( prop ) { + // Is the property access allowed? + if( prop->isPrivate && prop->isInherited ) + { + if( engine->ep.privatePropAsProtected ) + { + // The application is allowing inherited classes to access private properties of the parent + // class. This option is allowed to provide backwards compatibility with pre-2.30.0 versions + // as it was how the compiler behaved earlier. + asCString msg; + msg.Format(TXT_ACCESSING_PRIVATE_PROP_s, name.AddressOf()); + Warning(msg, errNode); + } + else + { + asCString msg; + msg.Format(TXT_INHERITED_PRIVATE_PROP_ACCESS_s, name.AddressOf()); + Error(msg, errNode); + } + } + if( !objType ) { // The object pointer is located at stack position 0 @@ -7604,9 +8367,11 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s asCScriptFunction *func = 0; for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) { - if( engine->scriptFunctions[ot->methods[n]]->name == name ) + asCScriptFunction *f = engine->scriptFunctions[ot->methods[n]]; + if( f->name == name && + (builder->module->accessMask & f->accessMask) ) { - func = engine->scriptFunctions[ot->methods[n]]; + func = f; break; } } @@ -7614,7 +8379,7 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s if( func ) { // An object method was found. Keep the name of the method in the expression, but - // don't actually modify the bytecode at this point since it is not yet known what + // don't actually modify the bytecode at this point since it is not yet known what // the method will be used for, or even what overloaded method should be used. ctx->methodName = name; @@ -7730,10 +8495,12 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s // If the object is a value type or a non-handle variable to a reference type, // then we must validate the existance as it could potentially be accessed // before it is initialized. - if( (ctx->type.dataType.GetObjectType()->flags & asOBJ_VALUE) || - !ctx->type.dataType.IsObjectHandle() ) + // This check is not needed for application registered properties, since they + // are guaranteed to be valid by the application itself. + if( !isAppProp && + ((ctx->type.dataType.GetObjectType()->flags & asOBJ_VALUE) || + !ctx->type.dataType.IsObjectHandle()) ) { - // TODO: runtime optimize: This is not necessary for application registered properties ctx->bc.Instr(asBC_ChkRefS); } @@ -7829,7 +8596,7 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s // The ambiguity could be resolved now, but I hesitate // to store too much information in the context. ctx->enumValue = name.AddressOf(); - + // We cannot set a dummy value because it will pass through // cleanly as an integer. ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttIdentifier, true), 0); @@ -7848,7 +8615,7 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s } else { - // If nothing was found because the scope doesn't match a namespace or an enum + // If nothing was found because the scope doesn't match a namespace or an enum // then this should be reported as an error and the search interrupted if( !ns && !scopeType ) { @@ -8424,12 +9191,13 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) // Determine the requested type to = builder->CreateDataTypeFromNode(node->firstChild, script, outFunc->nameSpace); - to = builder->ModifyDataTypeFromNode(to, node->firstChild->next, script, 0, 0); // If the type support object handles, then use it if( to.SupportHandles() ) { to.MakeHandle(true); + if( expr.type.dataType.IsObjectConst() ) + to.MakeHandleToConst(true); } else if( !to.IsObjectHandle() ) { @@ -8487,7 +9255,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) return; } - if( to.IsEqualExceptConst(expr.type.dataType) && to.IsPrimitive() ) + if( to.IsEqualExceptRefAndConst(expr.type.dataType) && to.IsPrimitive() ) { MergeExprBytecode(ctx, &expr); ctx->type = expr.type; @@ -8523,8 +9291,8 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) asCString strTo, strFrom; - strTo = to.Format(); - strFrom = expr.type.dataType.Format(); + strTo = to.Format(outFunc->nameSpace); + strFrom = expr.type.dataType.Format(outFunc->nameSpace); asCString msg; msg.Format(TXT_NO_CONVERSION_s_TO_s, strFrom.AddressOf(), strTo.AddressOf()); @@ -8534,6 +9302,9 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) void asCCompiler::AfterFunctionCall(int funcID, asCArray &args, asSExprContext *ctx, bool deferAll) { + // deferAll is set to true if for example the function returns a reference, since in + // this case the function might be returning a reference to one of the arguments. + asCScriptFunction *descr = builder->GetFunctionDescription(funcID); // Parameters that are sent by reference should be assigned @@ -8543,8 +9314,10 @@ void asCCompiler::AfterFunctionCall(int funcID, asCArray &args, int n = (int)descr->parameterTypes.GetLength() - 1; for( ; n >= 0; n-- ) { + // All &out arguments must be deferred + // If deferAll is set all objects passed by reference or handle must be deferred if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF)) || - (descr->parameterTypes[n].IsObject() && deferAll) ) + (descr->parameterTypes[n].IsObject() && deferAll && (descr->parameterTypes[n].IsReference() || descr->parameterTypes[n].IsObjectHandle())) ) { asASSERT( !(descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] == asTM_OUTREF)) || args[n]->origExpr ); @@ -8638,9 +9411,9 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx) if( !expr->type.IsVoidExpression() && (!expr->type.isConstant || expr->type.IsNullConstant()) ) ctx->bc.Instr(asBC_PopPtr); - // Give a warning, except if the argument is void, null or 0 which indicate the argument is really to be ignored + // Give an error, except if the argument is void, null or 0 which indicate the argument is explicitly to be ignored if( !expr->type.IsVoidExpression() && !expr->type.IsNullConstant() && !(expr->type.isConstant && expr->type.qwordValue == 0) ) - Warning(TXT_ARG_NOT_LVALUE, outParam.argNode); + Error(TXT_ARG_NOT_LVALUE, outParam.argNode); ReleaseTemporaryVariable(outParam.argType, &ctx->bc); } @@ -8691,6 +9464,37 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) return; } + if( dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) ) + { + // Types declared as implicit handle must not attempt to construct a handle + dt.MakeHandle(false); + } + + // Don't accept syntax like object@(expr) + if( dt.IsObjectHandle() ) + { + asCString str; + str.Format(TXT_CANT_CONSTRUCT_s_USE_REF_CAST, dt.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + ctx->type.SetDummy(); + return; + } + + if( !dt.CanBeInstantiated() ) + { + asCString str; + if( dt.IsAbstractClass() ) + str.Format(TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED, dt.Format(outFunc->nameSpace).AddressOf()); + else if( dt.IsInterface() ) + str.Format(TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED, dt.Format(outFunc->nameSpace).AddressOf()); + else + // TODO: Improve error message to explain why + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + ctx->type.SetDummy(); + return; + } + // Do not allow constructing non-shared types in shared functions if( outFunc->IsShared() && dt.GetObjectType() && !dt.GetObjectType()->IsShared() ) @@ -8702,17 +9506,20 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) // Compile the arguments asCArray args; + asCArray namedArgs; asCArray temporaryVariables; - if( CompileArgumentList(node->lastChild, args) >= 0 ) + if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 ) { // Check for a value cast behaviour if( args.GetLength() == 1 && args[0]->type.dataType.GetObjectType() ) { asSExprContext conv(engine); conv.type = args[0]->type; - ImplicitConversion(&conv, dt, node->lastChild, asIC_EXPLICIT_VAL_CAST, false); + asUINT cost = ImplicitConversion(&conv, dt, node->lastChild, asIC_EXPLICIT_VAL_CAST, false); - if( conv.type.dataType.IsEqualExceptRef(dt) ) + // Don't use this if the cost is 0 because it would mean that nothing + // is done and the scipt wants a new value to be constructed + if( conv.type.dataType.IsEqualExceptRef(dt) && cost > 0 ) { ImplicitConversion(args[0], dt, node->lastChild, asIC_EXPLICIT_VAL_CAST); @@ -8726,7 +9533,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) } // Check for possible constructor/factory - name = dt.Format(); + name = dt.Format(outFunc->nameSpace); asSTypeBehaviour *beh = dt.GetBehaviour(); @@ -8748,9 +9555,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) ctx->bc.InstrSHORT(asBC_VAR, tempObj.stackOffset); } else - { funcs = beh->factories; - } // Special case: Allow calling func(void) with a void expression. if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) ) @@ -8790,7 +9595,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) { // TODO: delegate: It is possible that the argument returns a function pointer already, in which // case no object delegate will be created, but instead a delegate for a function pointer - // In theory a simple cast would be good in this case, but this is a construct call so it + // In theory a simple cast would be good in this case, but this is a construct call so it // is expected that a new object is created. dt.MakeHandle(true); @@ -8807,7 +9612,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) for( asUINT n = 0; n < type->methods.GetLength(); n++ ) { asCScriptFunction *func = engine->scriptFunctions[type->methods[n]]; - + if( func->name != args[0]->methodName ) continue; @@ -8861,7 +9666,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) return; } - MatchFunctions(funcs, args, node, name.AddressOf(), NULL, false); + MatchFunctions(funcs, args, node, name.AddressOf(), &namedArgs, 0, false); if( funcs.GetLength() != 1 ) { @@ -8872,14 +9677,10 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) } else { - int r = asSUCCESS; - - // TODO: 2.28.1: Merge this with MakeFunctionCall + // TODO: Clean up: Merge this with MakeFunctionCall // Add the default values for arguments not explicitly supplied - asCScriptFunction *func = (funcs[0] & FUNC_IMPORTED) == 0 ? engine->scriptFunctions[funcs[0]] : 0; - if( func && args.GetLength() < (asUINT)func->GetParamCount() ) - r = CompileDefaultArgs(node, args, func); + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], dt.GetObjectType(), &namedArgs); if( r == asSUCCESS ) { @@ -8939,30 +9740,36 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) { asDELETE(args[n],asSExprContext); } + for( asUINT n = 0; n < namedArgs.GetLength(); n++ ) + if( namedArgs[n].ctx ) + { + asDELETE(namedArgs[n].ctx,asSExprContext); + } } int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope) { - asCString name; asCTypeInfo tempObj; asCArray funcs; int localVar = -1; bool initializeMembers = false; + asSExprContext funcExpr(engine); asCScriptNode *nm = node->lastChild->prev; - name.Assign(&script->code[nm->tokenPos], nm->tokenLength); + asCString name(&script->code[nm->tokenPos], nm->tokenLength); - // First check for a local variable of a function type as it would take precedence + // First check for a local variable as it would take precedence // Must not allow function names, nor global variables to be returned in this instance // If objectType is set then this is a post op expression and we shouldn't look for local variables - asSExprContext funcPtr(engine); if( objectType == 0 ) { - localVar = CompileVariableAccess(name, scope, &funcPtr, node, true, true, true); - if( localVar >= 0 && !funcPtr.type.dataType.GetFuncDef() && funcPtr.methodName == "" ) + localVar = CompileVariableAccess(name, scope, &funcExpr, node, true, true, true); + if( localVar >= 0 && + !(funcExpr.type.dataType.GetFuncDef() || funcExpr.type.dataType.IsObject()) && + funcExpr.methodName == "" ) { - // The variable is not a function + // The variable is not a function or object with opCall asCString msg; msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf()); Error(msg, node); @@ -8970,7 +9777,7 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a } // If the name matches a method name, then reset the indicator that nothing was found - if( funcPtr.methodName != "" ) + if( funcExpr.methodName != "" ) localVar = -1; } @@ -9012,15 +9819,17 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a } else { - // The scope is can be used to specify the base class - builder->GetObjectMethodDescriptions(name.AddressOf(), objectType ? objectType : outFunc->objectType, funcs, objIsConst, scope); + // The scope can be used to specify the base class + builder->GetObjectMethodDescriptions(name.AddressOf(), objectType ? objectType : outFunc->objectType, funcs, objIsConst, scope, node, script); } - // It is still possible that there is a class member of a function type + // It is still possible that there is a class member of a function type or a type with opCall methods if( funcs.GetLength() == 0 ) { - int r = CompileVariableAccess(name, scope, &funcPtr, node, true, true, true, objectType); - if( r >= 0 && !funcPtr.type.dataType.GetFuncDef() && funcPtr.methodName == "" ) + int r = CompileVariableAccess(name, scope, &funcExpr, node, true, true, true, objectType); + if( r >= 0 && + !(funcExpr.type.dataType.GetFuncDef() || funcExpr.type.dataType.IsObject()) && + funcExpr.methodName == "" ) { // The variable is not a function asCString msg; @@ -9028,6 +9837,11 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a Error(msg, node); return -1; } + + // If the name is an access property, make sure the original value isn't + // dereferenced when calling the access property as part a dot post operator + if( objectType && (funcExpr.property_get || funcExpr.property_set) && !ctx->type.dataType.IsReference() ) + funcExpr.property_ref = false; } // If a class method is being called implicitly, then add the this pointer for the call @@ -9050,20 +9864,22 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a // then look for global functions or global function pointers, // unless this is an expression post op, incase only member // functions are expected - if( objectType == 0 && funcs.GetLength() == 0 && funcPtr.type.dataType.GetFuncDef() == 0 ) + if( objectType == 0 && funcs.GetLength() == 0 && (funcExpr.type.dataType.GetFuncDef() == 0 || funcExpr.type.dataType.IsObject()) ) { // The scope is used to define the namespace asSNameSpace *ns = DetermineNameSpace(scope); if( ns ) { // Search recursively in parent namespaces - while( ns && funcs.GetLength() == 0 && funcPtr.type.dataType.GetFuncDef() == 0 ) + while( ns && funcs.GetLength() == 0 && funcExpr.type.dataType.GetFuncDef() == 0 ) { builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns); if( funcs.GetLength() == 0 ) { - int r = CompileVariableAccess(name, scope, &funcPtr, node, true, true); - if( r >= 0 && !funcPtr.type.dataType.GetFuncDef() ) + int r = CompileVariableAccess(name, scope, &funcExpr, node, true, true); + if( r >= 0 && + !(funcExpr.type.dataType.GetFuncDef() || funcExpr.type.dataType.IsObject()) && + funcExpr.methodName == "" ) { // The variable is not a function asCString msg; @@ -9073,7 +9889,7 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a } } - ns = builder->GetParentNameSpace(ns); + ns = engine->GetParentNameSpace(ns); } } else @@ -9086,17 +9902,55 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a } } - if( funcs.GetLength() == 0 && funcPtr.type.dataType.GetFuncDef() ) + if( funcs.GetLength() == 0 ) { - funcs.PushLast(funcPtr.type.dataType.GetFuncDef()->id); + if( funcExpr.type.dataType.GetFuncDef() ) + { + funcs.PushLast(funcExpr.type.dataType.GetFuncDef()->id); + } + else if( funcExpr.type.dataType.IsObject() ) + { + // Keep information about temporary variables as deferred expression so it can be properly cleaned up after the call + if( ctx->type.isTemporary ) + { + asASSERT( objectType ); + + asSDeferredParam deferred; + deferred.origExpr = 0; + deferred.argInOutFlags = asTM_INREF; + deferred.argNode = 0; + deferred.argType.SetVariable(ctx->type.dataType, ctx->type.stackOffset, true); + + ctx->deferredParams.PushLast(deferred); + } + if( funcExpr.property_get == 0 ) + Dereference(ctx, true); + + // Add the bytecode for accessing the object on which opCall will be called + MergeExprBytecodeAndType(ctx, &funcExpr); + ProcessPropertyGetAccessor(ctx, node); + Dereference(ctx, true); + + objectType = funcExpr.type.dataType.GetObjectType(); + + // Get the opCall methods from the object type + if( funcExpr.type.dataType.IsObjectHandle() ) + objIsConst = funcExpr.type.dataType.IsHandleToConst(); + else + objIsConst = funcExpr.type.dataType.IsReadOnly(); + + builder->GetObjectMethodDescriptions("opCall", funcExpr.type.dataType.GetObjectType(), funcs, objIsConst); + } } // Compile the arguments asCArray args; + asCArray namedArgs; - if( CompileArgumentList(node->lastChild, args) >= 0 ) + bool isOK = true; + if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 ) { - // Special case: Allow calling func(void) with an expression that evaluates to no datatype, but isn't exactly 'void' + // Special case: Allow calling func(void) with an expression that evaluates to no datatype, but isn't exactly 'void' if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) && !args[0]->type.IsVoidExpression() ) { // Evaluate the expression before the function call @@ -9105,7 +9959,7 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a args.SetLength(0); } - MatchFunctions(funcs, args, node, name.AddressOf(), objectType, objIsConst, false, true, scope); + MatchFunctions(funcs, args, node, name.AddressOf(), &namedArgs, objectType, objIsConst, false, true, scope); if( funcs.GetLength() != 1 ) { @@ -9113,22 +9967,12 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a // Dummy value ctx->type.SetDummy(); + isOK = false; } else { - int r = asSUCCESS; - // Add the default values for arguments not explicitly supplied - asCScriptFunction *func = builder->GetFunctionDescription(funcs[0]); - if( func && args.GetLength() < (asUINT)func->GetParamCount() ) - { - // Make sure to use the real function for virtual functions - asCScriptFunction *realFunc = func; - if( realFunc->funcType == asFUNC_VIRTUAL ) - realFunc = objectType->virtualFunctionTable[realFunc->vfTableIdx]; - - r = CompileDefaultArgs(node, args, realFunc); - } + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], objectType, &namedArgs); // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or // is it enough to make sure it is in a local variable? @@ -9137,52 +9981,48 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a // by first storing the function pointer in a local variable (if it isn't already in one) if( r == asSUCCESS ) { + asCScriptFunction *func = builder->GetFunctionDescription(funcs[0]); if( func->funcType == asFUNC_FUNCDEF ) { - if( objectType && funcPtr.property_get <= 0 ) + if( objectType && funcExpr.property_get <= 0 ) { - Dereference(ctx, true); // Dereference the object pointer to access the member - - // The actual function should be called as if a global function - objectType = 0; + // Dereference the object pointer to access the member + Dereference(ctx, true); } - if( funcPtr.property_get > 0 ) + if( funcExpr.property_get > 0 ) { - ProcessPropertyGetAccessor(&funcPtr, node); - Dereference(&funcPtr, true); - - // The function call will be made directly from the local variable so the function pointer shouldn't be on the stack - funcPtr.bc.Instr(asBC_PopPtr); + ProcessPropertyGetAccessor(&funcExpr, node); + Dereference(&funcExpr, true); } else { - Dereference(&funcPtr, true); - ConvertToVariable(&funcPtr); - - // The function call will be made directly from the local variable so the function pointer shouldn't be on the stack - if( !funcPtr.type.isTemporary ) - funcPtr.bc.Instr(asBC_PopPtr); + Dereference(&funcExpr, true); + ConvertToVariable(&funcExpr); } - MergeExprBytecodeAndType(ctx, &funcPtr); + // The actual function should be called as if a global function + objectType = 0; + + // The function call will be made directly from the local variable so the function pointer shouldn't be on the stack + funcExpr.bc.Instr(asBC_PopPtr); + + asCTypeInfo tmp = ctx->type; + MergeExprBytecodeAndType(ctx, &funcExpr); + ReleaseTemporaryVariable(tmp, &ctx->bc); } - MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, funcPtr.type.stackOffset); - - // If the function pointer was copied to a local variable for the call, then - // release it again (temporary local variable) - if( (funcs[0] & FUNC_IMPORTED) == 0 && engine->scriptFunctions[funcs[0]]->funcType == asFUNC_FUNCDEF ) - { - ReleaseTemporaryVariable(funcPtr.type, &ctx->bc); - } + MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, funcExpr.type.stackOffset); } + else + isOK = false; } } else { // Failed to compile the argument list, set the dummy type and continue compilation ctx->type.SetDummy(); + isOK = false; } // Cleanup @@ -9191,6 +10031,11 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a { asDELETE(args[n],asSExprContext); } + for( asUINT n = 0; n < namedArgs.GetLength(); n++ ) + if( namedArgs[n].ctx ) + { + asDELETE(namedArgs[n].ctx,asSExprContext); + } if( initializeMembers ) { @@ -9208,7 +10053,7 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a CompileMemberInitialization(&ctx->bc, false); } - return 0; + return isOK ? 0 : -1; } asSNameSpace *asCCompiler::DetermineNameSpace(const asCString &scope) @@ -9334,16 +10179,10 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx // TODO: Should convert this to something similar to CompileOverloadedDualOperator2 ProcessPropertyGetAccessor(ctx, node); - // Is it a const value? - bool isConst = false; - if( ctx->type.dataType.IsObjectHandle() ) - isConst = ctx->type.dataType.IsHandleToConst(); - else - isConst = ctx->type.dataType.IsReadOnly(); - // TODO: If the value isn't const, then first try to find the non const method, and if not found try to find the const method // Find the correct method + bool isConst = ctx->type.dataType.IsObjectConst(); asCArray funcs; asCObjectType *ot = ctx->type.dataType.GetObjectType(); for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) @@ -9360,10 +10199,8 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx // Did we find the method? if( funcs.GetLength() == 1 ) { - asCTypeInfo objType = ctx->type; asCArray args; - MakeFunctionCall(ctx, funcs[0], objType.dataType.GetObjectType(), args, node); - ReleaseTemporaryVariable(objType, &ctx->bc); + MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.GetObjectType(), args, node); return 0; } else if( funcs.GetLength() == 0 ) @@ -9412,8 +10249,6 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx asCDataType to = ctx->type.dataType; - // TODO: The case -2147483648 gives an unecessary warning of changed sign for implicit conversion - if( ctx->type.dataType.IsUnsignedType() ) { if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) @@ -9432,7 +10267,9 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx } if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); - ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV); + + // Use an explicit conversion in case of constants to avoid unnecessary warning about change of sign + ImplicitConversion(ctx, to, node, ctx->type.isConstant ? asIC_EXPLICIT_VAL_CAST : asIC_IMPLICIT_CONV); if( !ctx->type.isConstant ) { @@ -9482,6 +10319,10 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx } else if( op == ttNot ) { + // Allow value types to be converted to bool using 'bool opImplConv()' + if( ctx->type.dataType.GetObjectType() && (ctx->type.dataType.GetObjectType()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(ctx, asCDataType::CreatePrimitive(ttBool, false), node, asIC_IMPLICIT_CONV); + if( ctx->type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) { if( ctx->type.isConstant ) @@ -9766,11 +10607,7 @@ int asCCompiler::FindPropertyAccessor(const asCString &name, asSExprContext *ctx } } - bool isConst = false; - if( ctx->type.dataType.IsObjectHandle() ) - isConst = ctx->type.dataType.IsHandleToConst(); - else - isConst = ctx->type.dataType.IsReadOnly(); + bool isConst = ctx->type.dataType.IsObjectConst(); // Check for multiple matches if( multipleGetFuncs.GetLength() > 0 ) @@ -9949,7 +10786,6 @@ int asCCompiler::ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext return -1; } - asCTypeInfo objType = ctx->type; asCScriptFunction *func = builder->GetFunctionDescription(ctx->property_set); // Make sure the arg match the property @@ -9959,7 +10795,7 @@ int asCCompiler::ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext if( ctx->property_arg ) args.PushLast(ctx->property_arg); args.PushLast(arg); - MatchFunctions(funcs, args, node, func->GetName(), func->objectType, ctx->property_const); + MatchFunctions(funcs, args, node, func->GetName(), 0, func->objectType, ctx->property_const); if( funcs.GetLength() == 0 ) { // MatchFunctions already reported the error @@ -9991,19 +10827,6 @@ int asCCompiler::ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext // Call the accessor MakeFunctionCall(ctx, ctx->property_set, func->objectType, args, node); - if( func->objectType ) - { - // TODO: This is from CompileExpressionPostOp, can we unify the code? - if( !objType.isTemporary || - !ctx->type.dataType.IsReference() || - ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member - { - // As the method didn't return a reference to a member - // we can safely release the original object now - ReleaseTemporaryVariable(objType, &ctx->bc); - } - } - ctx->property_get = 0; ctx->property_set = 0; if( ctx->property_arg ) @@ -10015,6 +10838,131 @@ int asCCompiler::ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext return 0; } +int asCCompiler::ProcessPropertyGetSetAccessor(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, eTokenType op, asCScriptNode *errNode) +{ + // TODO: Perhaps it might be interesting to allow the definition of compound setters for better + // performance, e.g. set_add_prop, set_mul_prop, etc. With these it would also be possible + // to support value types, since it would be a single call + + // Compound assignment for indexed property accessors is not supported yet + if( lctx->property_arg != 0 ) + { + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, errNode); + Error(TXT_COMPOUND_ASGN_WITH_IDX_PROP, errNode); + return -1; + } + + // Compound assignments require both get and set accessors + if( lctx->property_set == 0 || lctx->property_get == 0 ) + { + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, errNode); + Error(TXT_COMPOUND_ASGN_REQUIRE_GET_SET, errNode); + return -1; + } + + // Property accessors on value types (or scoped references types) are not supported since + // it is not possible to guarantee that the object will stay alive between the two calls + asCScriptFunction *func = engine->scriptFunctions[lctx->property_set]; + if( func->objectType && (func->objectType->flags & (asOBJ_VALUE | asOBJ_SCOPED)) ) + { + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, errNode); + Error(TXT_COMPOUND_ASGN_ON_VALUE_TYPE, errNode); + return -1; + } + + // Translate the compound assignment to the corresponding dual operator + switch( op ) + { + case ttAddAssign: op = ttPlus; break; + case ttSubAssign: op = ttMinus; break; + case ttMulAssign: op = ttStar; break; + case ttDivAssign: op = ttSlash; break; + case ttModAssign: op = ttPercent; break; + case ttPowAssign: op = ttStarStar; break; + + case ttAndAssign: op = ttAmp; break; + case ttOrAssign: op = ttBitOr; break; + case ttXorAssign: op = ttBitXor; break; + + case ttShiftLeftAssign: op = ttBitShiftLeft; break; + case ttShiftRightAAssign: op = ttBitShiftRightArith; break; + case ttShiftRightLAssign: op = ttBitShiftRight; break; + + default: op = ttUnrecognizedToken; break; + } + + if( op == ttUnrecognizedToken ) + { + // Shouldn't happen + asASSERT(false); + + // Process the property to free the memory + ProcessPropertySetAccessor(lctx, rctx, errNode); + return -1; + } + + asSExprContext before(engine); + if( func->objectType && (func->objectType->flags & (asOBJ_REF|asOBJ_SCOPED)) == asOBJ_REF ) + { + // Keep a reference to the object in a local variable + before.bc.AddCode(&lctx->bc); + + asUINT len = reservedVariables.GetLength(); + rctx->bc.GetVarsUsed(reservedVariables); + before.bc.GetVarsUsed(reservedVariables); + + asCDataType dt = asCDataType::CreateObjectHandle(func->objectType, false); + int offset = AllocateVariable(dt, true); + + reservedVariables.SetLength(len); + + before.type.SetVariable(dt, offset, true); + + if( lctx->property_ref ) + before.bc.Instr(asBC_RDSPtr); + before.bc.InstrSHORT(asBC_PSF, (short)offset); + before.bc.InstrPTR(asBC_REFCPY, func->objectType); + before.bc.Instr(asBC_PopPtr); + + // Update the left expression to use the local variable + lctx->bc.InstrSHORT(asBC_PSF, (short)offset); + lctx->type.stackOffset = (short)offset; + lctx->property_ref = true; + + ctx->bc.AddCode(&before.bc); + } + + // Keep the original information on the property + asSExprContext llctx(engine); + llctx.type = lctx->type; + llctx.property_arg = lctx->property_arg; + llctx.property_const = lctx->property_const; + llctx.property_get = lctx->property_get; + llctx.property_handle = lctx->property_handle; + llctx.property_ref = lctx->property_ref; + llctx.property_set = lctx->property_set; + + // Compile the dual operator using the get accessor + CompileOperator(errNode, lctx, rctx, ctx, op); + + // If we made a local variable to hold the reference it must be reused + if( before.type.stackOffset ) + llctx.bc.InstrSHORT(asBC_PSF, before.type.stackOffset); + + // Compile the assignment using the set accessor + ProcessPropertySetAccessor(&llctx, ctx, errNode); + + MergeExprBytecodeAndType(ctx, &llctx); + + if( before.type.stackOffset ) + ReleaseTemporaryVariable(before.type.stackOffset, &ctx->bc); + + return 0; +} + void asCCompiler::ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode *node) { // If no property accessor has been prepared then don't do anything @@ -10038,7 +10986,7 @@ void asCCompiler::ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode asCArray args; if( ctx->property_arg ) args.PushLast(ctx->property_arg); - MatchFunctions(funcs, args, node, func->GetName(), func->objectType, ctx->property_const); + MatchFunctions(funcs, args, node, func->GetName(), 0, func->objectType, ctx->property_const); if( funcs.GetLength() == 0 ) { // MatchFunctions already reported the error @@ -10068,25 +11016,15 @@ void asCCompiler::ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode } } + // The explicit handle flag must be remembered + bool isExplicitHandle = ctx->type.isExplicitHandle; + // Call the accessor MakeFunctionCall(ctx, ctx->property_get, func->objectType, args, node); + if( isExplicitHandle ) + ctx->type.isExplicitHandle = true; - if( func->objectType ) - { - // TODO: This is from CompileExpressionPostOp, can we unify the code? - - // If the method returned a reference, then we can't release the original - // object yet, because the reference may be to a member of it - if( !objType.isTemporary || - !(ctx->type.dataType.IsReference() || (ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle())) || - ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member - { - // As the method didn't return a reference to a member - // we can safely release the original object now - ReleaseTemporaryVariable(objType, &ctx->bc); - } - } - + // Clear the property get/set ids ctx->property_get = 0; ctx->property_set = 0; if( ctx->property_arg ) @@ -10130,16 +11068,10 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct // TODO: Should convert this to something similar to CompileOverloadedDualOperator2 ProcessPropertyGetAccessor(ctx, node); - // Is it a const value? - bool isConst = false; - if( ctx->type.dataType.IsObjectHandle() ) - isConst = ctx->type.dataType.IsHandleToConst(); - else - isConst = ctx->type.dataType.IsReadOnly(); - // TODO: If the value isn't const, then first try to find the non const method, and if not found try to find the const method // Find the correct method + bool isConst = ctx->type.dataType.IsObjectConst(); asCArray funcs; asCObjectType *ot = ctx->type.dataType.GetObjectType(); for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) @@ -10156,10 +11088,8 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct // Did we find the method? if( funcs.GetLength() == 1 ) { - asCTypeInfo objType = ctx->type; asCArray args; - MakeFunctionCall(ctx, funcs[0], objType.dataType.GetObjectType(), args, node); - ReleaseTemporaryVariable(objType, &ctx->bc); + MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.GetObjectType(), args, node); return 0; } else if( funcs.GetLength() == 0 ) @@ -10301,16 +11231,19 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct ctx->type.isLValue = true; } - bool isConst = ctx->type.dataType.IsReadOnly(); + bool isConst = ctx->type.dataType.IsObjectConst(); asCObjectProperty *prop = builder->GetObjectProperty(ctx->type.dataType, name.AddressOf()); if( prop ) { // Is the property access allowed? - if( prop->isPrivate && (!outFunc || outFunc->objectType != ctx->type.dataType.GetObjectType()) ) + if( (prop->isPrivate || prop->isProtected) && (!outFunc || outFunc->objectType != ctx->type.dataType.GetObjectType()) ) { asCString msg; - msg.Format(TXT_PRIVATE_PROP_ACCESS_s, name.AddressOf()); + if( prop->isPrivate ) + msg.Format(TXT_PRIVATE_PROP_ACCESS_s, name.AddressOf()); + else + msg.Format(TXT_PROTECTED_PROP_ACCESS_s, name.AddressOf()); Error(msg, node); } @@ -10371,14 +11304,14 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct if( func ) { // An object method was found. Keep the name of the method in the expression, but - // don't actually modify the bytecode at this point since it is not yet known what + // don't actually modify the bytecode at this point since it is not yet known what // the method will be used for, or even what overloaded method should be used. ctx->methodName = name; } else { asCString str; - str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format().AddressOf()); + str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); return -1; } @@ -10387,7 +11320,7 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct else { asCString str; - str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format().AddressOf()); + str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); return -1; } @@ -10398,7 +11331,7 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct if( !ctx->type.dataType.IsObject() ) { asCString str; - str.Format(TXT_ILLEGAL_OPERATION_ON_s, ctx->type.dataType.Format().AddressOf()); + str.Format(TXT_ILLEGAL_OPERATION_ON_s, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); return -1; } @@ -10406,30 +11339,9 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct // Process the get property accessor ProcessPropertyGetAccessor(ctx, node); - bool isConst = false; - if( ctx->type.dataType.IsObjectHandle() ) - isConst = ctx->type.dataType.IsHandleToConst(); - else - isConst = ctx->type.dataType.IsReadOnly(); - - asCObjectType *trueObj = ctx->type.dataType.GetObjectType(); - - asCTypeInfo objType = ctx->type; - // Compile function call - int r = CompileFunctionCall(node->firstChild, ctx, trueObj, isConst); + int r = CompileFunctionCall(node->firstChild, ctx, ctx->type.dataType.GetObjectType(), ctx->type.dataType.IsObjectConst()); if( r < 0 ) return r; - - // If the method returned a reference, then we can't release the original - // object yet, because the reference may be to a member of it - if( !objType.isTemporary || - !(ctx->type.dataType.IsReference() || (ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle())) || - ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member - { - // As the method didn't return a reference to a member - // we can safely release the original object now - ReleaseTemporaryVariable(objType, &ctx->bc); - } } } else if( op == ttOpenBracket ) @@ -10480,7 +11392,7 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct if( !ctx->type.dataType.IsObject() ) { asCString str; - str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format().AddressOf()); + str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); return -1; } @@ -10488,41 +11400,94 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct ProcessPropertyGetAccessor(ctx, node); } - Dereference(ctx, true); - // Compile the expression - asSExprContext expr(engine); - CompileAssignment(node->firstChild, &expr); - - // Check for the existence of the opIndex method - asSExprContext lctx(engine); - MergeExprBytecodeAndType(&lctx, ctx); - int r = 0; - if( propertyName == "" ) - r = CompileOverloadedDualOperator2(node, "opIndex", &lctx, &expr, ctx); - if( r == 0 ) + bool isOK = true; + asCArray args; + asCArray namedArgs; + asASSERT( node->firstChild->nodeType == snArgList ); + if( CompileArgumentList(node->firstChild, args, namedArgs) >= 0 ) { - // Check for accessors methods for the opIndex - r = FindPropertyAccessor(propertyName == "" ? "opIndex" : propertyName.AddressOf(), &lctx, &expr, node, ns); - if( r == 0 ) + // Check for the existence of the opIndex method + bool lookForProperty = true; + if( propertyName == "" ) { - asCString str; - str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format().AddressOf()); - Error(str, node); - return -1; - } - else if( r < 0 ) - return -1; + bool isConst = ctx->type.dataType.IsObjectConst(); + asCObjectType *objectType = ctx->type.dataType.GetObjectType(); - MergeExprBytecodeAndType(ctx, &lctx); + asCArray funcs; + builder->GetObjectMethodDescriptions("opIndex", objectType, funcs, isConst); + if( funcs.GetLength() > 0 ) + { + // Since there are opIndex methods, the compiler should not look for get/set_opIndex accessors + lookForProperty = false; + + // Determine which of opIndex methods that match + MatchFunctions(funcs, args, node, "opIndex", 0, objectType, isConst); + if( funcs.GetLength() != 1 ) + { + // The error has already been reported by MatchFunctions + isOK = false; + } + else + { + // Add the default values for arguments not explicitly supplied + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], objectType); + + if( r == 0 ) + MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, ctx->type.stackOffset); + else + isOK = false; + } + } + } + if( lookForProperty && isOK ) + { + if( args.GetLength() != 1 ) + { + // TODO: opIndex: Implement this + Error("Property accessor with index only support 1 index argument for now", node); + isOK = false; + } + + Dereference(ctx, true); + asSExprContext lctx(engine); + MergeExprBytecodeAndType(&lctx, ctx); + + // Check for accessors methods for the opIndex, either as get/set_opIndex or as get/set with the property name + int r = FindPropertyAccessor(propertyName == "" ? "opIndex" : propertyName.AddressOf(), &lctx, args[0], node, ns); + if( r == 0 ) + { + asCString str; + str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); + Error(str, node); + isOK = false; + } + else if( r < 0 ) + isOK = false; + + if( isOK ) + MergeExprBytecodeAndType(ctx, &lctx); + } } + else + isOK = false; + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n],asSExprContext); + } + + if( !isOK ) + return -1; } else if( op == ttOpenParanthesis ) { // TODO: Most of this is already done by CompileFunctionCall(). Can we share the code? - // Make sure the expression is a funcdef - if( !ctx->type.dataType.GetFuncDef() ) + // Make sure the expression is a funcdef or an object that may have opCall methods + if( !ctx->type.dataType.GetFuncDef() && !ctx->type.dataType.IsObject() ) { Error(TXT_EXPR_DOESNT_EVAL_TO_FUNC, node); return -1; @@ -10530,12 +11495,23 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct // Compile arguments asCArray args; - if( CompileArgumentList(node->lastChild, args) >= 0 ) + asCArray namedArgs; + if( CompileArgumentList(node->lastChild, args, namedArgs) >= 0 ) { // Match arguments with the funcdef asCArray funcs; - funcs.PushLast(ctx->type.dataType.GetFuncDef()->id); - MatchFunctions(funcs, args, node, ctx->type.dataType.GetFuncDef()->name.AddressOf()); + if( ctx->type.dataType.GetFuncDef() ) + { + funcs.PushLast(ctx->type.dataType.GetFuncDef()->id); + MatchFunctions(funcs, args, node, ctx->type.dataType.GetFuncDef()->name.AddressOf(), &namedArgs); + } + else + { + bool isConst = ctx->type.dataType.IsObjectConst(); + + builder->GetObjectMethodDescriptions("opCall", ctx->type.dataType.GetObjectType(), funcs, isConst); + MatchFunctions(funcs, args, node, "opCall", &namedArgs, ctx->type.dataType.GetObjectType(), isConst); + } if( funcs.GetLength() != 1 ) { @@ -10546,12 +11522,8 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct } else { - int r = asSUCCESS; - // Add the default values for arguments not explicitly supplied - asCScriptFunction *func = (funcs[0] & FUNC_IMPORTED) == 0 ? engine->scriptFunctions[funcs[0]] : 0; - if( func && args.GetLength() < (asUINT)func->GetParamCount() ) - r = CompileDefaultArgs(node, args, func); + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], ctx->type.dataType.GetObjectType(), &namedArgs); // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or // is it enough to make sure it is in a local variable? @@ -10561,18 +11533,16 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct if( r == asSUCCESS ) { Dereference(ctx, true); - if( !ctx->type.isVariable ) - ConvertToVariable(ctx); - else + if( ctx->type.dataType.GetFuncDef() ) { + if( !ctx->type.isVariable ) + ConvertToVariable(ctx); + // Remove the reference from the stack as the asBC_CALLPTR instruction takes the variable as argument ctx->bc.Instr(asBC_PopPtr); } - asCTypeInfo t = ctx->type; - MakeFunctionCall(ctx, funcs[0], 0, args, node, false, 0, ctx->type.stackOffset); - - ReleaseTemporaryVariable(t, &ctx->bc); + MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.GetFuncDef() ? 0 : ctx->type.dataType.GetObjectType(), args, node, false, 0, ctx->type.stackOffset); } } } @@ -10585,6 +11555,11 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct { asDELETE(args[n],asSExprContext); } + for( asUINT n = 0; n < namedArgs.GetLength(); n++ ) + if( namedArgs[n].ctx ) + { + asDELETE(namedArgs[n].ctx,asSExprContext); + } } return 0; @@ -10667,73 +11642,84 @@ asUINT asCCompiler::MatchArgument(asCArray &funcs, asCArrayparameterTypes.GetLength() <= paramNum ) continue; - // void expressions can match any out parameter, but nothing else - if( argExpr->type.IsVoidExpression() ) - { - if( desc->inOutFlags[paramNum] == asTM_OUTREF ) - matches.PushLast(asSOverloadCandidate(funcs[n], 0)); - continue; - } - - // Can we make the match by implicit conversion? - asSExprContext ti(engine); - ti.type = argExpr->type; - ti.methodName = argExpr->methodName; - ti.enumValue = argExpr->enumValue; - if( argExpr->type.dataType.IsPrimitive() ) ti.type.dataType.MakeReference(false); - asUINT cost = ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, allowObjectConstruct); - - // If the function parameter is an inout-reference then it must not be possible to call the - // function with an incorrect argument type, even though the type can normally be converted. - if( desc->parameterTypes[paramNum].IsReference() && - desc->inOutFlags[paramNum] == asTM_INOUTREF && - desc->parameterTypes[paramNum].GetTokenType() != ttQuestion ) - { - // Observe, that the below checks are only necessary for when unsafe references have been - // enabled by the application. Without this the &inout reference form wouldn't be allowed - // for these value types. - - // Don't allow a primitive to be converted to a reference of another primitive type - if( desc->parameterTypes[paramNum].IsPrimitive() && - desc->parameterTypes[paramNum].GetTokenType() != argExpr->type.dataType.GetTokenType() ) - { - asASSERT( engine->ep.allowUnsafeReferences ); - continue; - } - - // Don't allow an enum to be converted to a reference of another enum type - if( desc->parameterTypes[paramNum].IsEnumType() && - desc->parameterTypes[paramNum].GetObjectType() != argExpr->type.dataType.GetObjectType() ) - { - asASSERT( engine->ep.allowUnsafeReferences ); - continue; - } - - // Don't allow a non-handle expression to be converted to a reference to a handle - if( desc->parameterTypes[paramNum].IsObjectHandle() && - !argExpr->type.dataType.IsObjectHandle() ) - { - asASSERT( engine->ep.allowUnsafeReferences ); - continue; - } - - // Don't allow a value type to be converted - if( (desc->parameterTypes[paramNum].GetObjectType() && (desc->parameterTypes[paramNum].GetObjectType()->GetFlags() & asOBJ_VALUE)) && - (desc->parameterTypes[paramNum].GetObjectType() != argExpr->type.dataType.GetObjectType()) ) - { - asASSERT( engine->ep.allowUnsafeReferences ); - continue; - } - } - - // How well does the argument match the function parameter? - if( desc->parameterTypes[paramNum].IsEqualExceptRef(ti.type.dataType) ) - matches.PushLast(asSOverloadCandidate(funcs[n], cost)); + int cost = MatchArgument(desc, argExpr, paramNum, allowObjectConstruct); + if( cost != -1 ) + matches.PushLast(asSOverloadCandidate(funcs[n], asUINT(cost))); } return (asUINT)matches.GetLength(); } +int asCCompiler::MatchArgument(asCScriptFunction *desc, const asSExprContext *argExpr, int paramNum, bool allowObjectConstruct) +{ + // void expressions can match any out parameter, but nothing else + if( argExpr->type.IsVoidExpression() ) + { + if( desc->inOutFlags[paramNum] == asTM_OUTREF ) + return 0; + return -1; + } + + // Can we make the match by implicit conversion? + asSExprContext ti(engine); + ti.type = argExpr->type; + ti.methodName = argExpr->methodName; + ti.enumValue = argExpr->enumValue; + if( argExpr->type.dataType.IsPrimitive() ) + ti.type.dataType.MakeReference(false); + int cost = ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, allowObjectConstruct); + + // If the function parameter is an inout-reference then it must not be possible to call the + // function with an incorrect argument type, even though the type can normally be converted. + if( desc->parameterTypes[paramNum].IsReference() && + desc->inOutFlags[paramNum] == asTM_INOUTREF && + desc->parameterTypes[paramNum].GetTokenType() != ttQuestion ) + { + // Observe, that the below checks are only necessary for when unsafe references have been + // enabled by the application. Without this the &inout reference form wouldn't be allowed + // for these value types. + + // Don't allow a primitive to be converted to a reference of another primitive type + if( desc->parameterTypes[paramNum].IsPrimitive() && + desc->parameterTypes[paramNum].GetTokenType() != argExpr->type.dataType.GetTokenType() ) + { + asASSERT( engine->ep.allowUnsafeReferences ); + return -1; + } + + // Don't allow an enum to be converted to a reference of another enum type + if( desc->parameterTypes[paramNum].IsEnumType() && + desc->parameterTypes[paramNum].GetObjectType() != argExpr->type.dataType.GetObjectType() ) + { + asASSERT( engine->ep.allowUnsafeReferences ); + return -1; + } + + // Don't allow a non-handle expression to be converted to a reference to a handle + if( desc->parameterTypes[paramNum].IsObjectHandle() && + !argExpr->type.dataType.IsObjectHandle() ) + { + asASSERT( engine->ep.allowUnsafeReferences ); + return -1; + } + + // Don't allow a value type to be converted + if( (desc->parameterTypes[paramNum].GetObjectType() && (desc->parameterTypes[paramNum].GetObjectType()->GetFlags() & asOBJ_VALUE)) && + (desc->parameterTypes[paramNum].GetObjectType() != argExpr->type.dataType.GetObjectType()) ) + { + asASSERT( engine->ep.allowUnsafeReferences ); + return -1; + } + } + + // How well does the argument match the function parameter? + if( desc->parameterTypes[paramNum].IsEqualExceptRef(ti.type.dataType) ) + return cost; + + // No match is available + return -1; +} + void asCCompiler::PrepareArgument2(asSExprContext *ctx, asSExprContext *arg, asCDataType *paramType, bool isFunction, int refType, bool isMakingCopy) { // Reference parameters whose value won't be used don't evaluate the expression @@ -10756,7 +11742,7 @@ void asCCompiler::PrepareArgument2(asSExprContext *ctx, asSExprContext *arg, asC ctx->bc.AddCode(&arg->bc); } -bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, bool isHandle, eTokenType token) { DetermineSingleFunc(lctx, node); DetermineSingleFunc(rctx, node); @@ -10764,7 +11750,8 @@ bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asSExprCont ctx->exprNode = node; // What type of operator is it? - int token = node->tokenType; + if( token == ttUnrecognizedToken ) + token = node->tokenType; if( token == ttUnrecognizedToken ) { // This happens when the compiler is inferring an assignment @@ -10869,7 +11856,7 @@ bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asSExprCont // The rest of the operators are not commutative, and doesn't require specific return type const char *op = 0, *op_r = 0; - switch( token ) + switch( int(token) ) // convert to int to avoid warning in gnuc that not all values are tested { case ttPlus: op = "opAdd"; op_r = "opAdd_r"; break; case ttMinus: op = "opSub"; op_r = "opSub_r"; break; @@ -10913,26 +11900,38 @@ bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asSExprCont // Assignment operators op = 0; - switch( token ) + if( isHandle ) { - case ttAssignment: op = "opAssign"; break; - case ttAddAssign: op = "opAddAssign"; break; - case ttSubAssign: op = "opSubAssign"; break; - case ttMulAssign: op = "opMulAssign"; break; - case ttDivAssign: op = "opDivAssign"; break; - case ttModAssign: op = "opModAssign"; break; - case ttPowAssign: op = "opPowAssign"; break; - case ttOrAssign: op = "opOrAssign"; break; - case ttAndAssign: op = "opAndAssign"; break; - case ttXorAssign: op = "opXorAssign"; break; - case ttShiftLeftAssign: op = "opShlAssign"; break; - case ttShiftRightLAssign: op = "opShrAssign"; break; - case ttShiftRightAAssign: op = "opUShrAssign"; break; + // Only asOBJ_ASHANDLE types can get here + asASSERT( lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE) ); + asASSERT( token == ttAssignment ); + + if( token == ttAssignment ) + op = "opHndlAssign"; + } + else + { + switch( int(token) ) // convert to int to avoid warning in gnuc that not all values are tested + { + case ttAssignment: op = "opAssign"; break; + case ttAddAssign: op = "opAddAssign"; break; + case ttSubAssign: op = "opSubAssign"; break; + case ttMulAssign: op = "opMulAssign"; break; + case ttDivAssign: op = "opDivAssign"; break; + case ttModAssign: op = "opModAssign"; break; + case ttPowAssign: op = "opPowAssign"; break; + case ttOrAssign: op = "opOrAssign"; break; + case ttAndAssign: op = "opAndAssign"; break; + case ttXorAssign: op = "opXorAssign"; break; + case ttShiftLeftAssign: op = "opShlAssign"; break; + case ttShiftRightLAssign: op = "opShrAssign"; break; + case ttShiftRightAAssign: op = "opUShrAssign"; break; + } } if( op ) { - if( builder->engine->ep.disallowValueAssignForRefType && + if( builder->engine->ep.disallowValueAssignForRefType && lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_REF) && !(lctx->type.dataType.GetObjectType()->flags & asOBJ_SCOPED) ) { if( token == ttAssignment ) @@ -10979,18 +11978,15 @@ int asCCompiler::CompileOverloadedDualOperator2(asCScriptNode *node, const char asUINT n; // Is the left value a const? - bool isConst = false; - if( lctx->type.dataType.IsObjectHandle() ) - isConst = lctx->type.dataType.IsHandleToConst(); - else - isConst = lctx->type.dataType.IsReadOnly(); + bool isConst = lctx->type.dataType.IsObjectConst(); asCArray funcs; asCObjectType *ot = lctx->type.dataType.GetObjectType(); for( n = 0; n < ot->methods.GetLength(); n++ ) { asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; - if( func->name == methodName && + asASSERT( func ); + if( func && func->name == methodName && (!specificReturn || func->returnType == returnType) && func->parameterTypes.GetLength() == 1 && (!isConst || func->isReadOnly) ) @@ -11032,24 +12028,35 @@ int asCCompiler::CompileOverloadedDualOperator2(asCScriptNode *node, const char // Process the lctx expression as get accessor ProcessPropertyGetAccessor(lctx, node); + // Make sure the rvalue doesn't have deferred temporary variables that are also used in the lvalue, + // since that would cause the VM to overwrite the variable while executing the bytecode for the lvalue. + asCArray usedVars; + lctx->bc.GetVarsUsed(usedVars); + asUINT oldReservedVars = reservedVariables.GetLength(); + for( asUINT n = 0; n < rctx->deferredParams.GetLength(); n++ ) + { + if( rctx->deferredParams[n].argType.isTemporary && + usedVars.Exists(rctx->deferredParams[n].argType.stackOffset) ) + { + if( reservedVariables.GetLength() == oldReservedVars ) + reservedVariables.Concatenate(usedVars); + + // Allocate a new variable for the deferred argument + int offset = AllocateVariableNotIn(rctx->deferredParams[n].argType.dataType, true, false, rctx); + int oldVar = rctx->deferredParams[n].argType.stackOffset; + rctx->deferredParams[n].argType.stackOffset = short(offset); + rctx->bc.ExchangeVar(oldVar, offset); + ReleaseTemporaryVariable(oldVar, 0); + } + } + reservedVariables.SetLength(oldReservedVars); + // Merge the bytecode so that it forms lvalue.methodName(rvalue) - asCTypeInfo objType = lctx->type; asCArray args; args.PushLast(rctx); MergeExprBytecode(ctx, lctx); ctx->type = lctx->type; - MakeFunctionCall(ctx, ops[0], objType.dataType.GetObjectType(), args, node); - - // If the method returned a reference, then we can't release the original - // object yet, because the reference may be to a member of it - if( !objType.isTemporary || - !(ctx->type.dataType.IsReference() || (ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle())) || - ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not to a member - { - // As the index operator didn't return a reference to a - // member we can release the original object now - ReleaseTemporaryVariable(objType, &ctx->bc); - } + MakeFunctionCall(ctx, ops[0], ctx->type.dataType.GetObjectType(), args, node); // Found matching operator return 1; @@ -11145,7 +12152,7 @@ void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectTyp PerformFunctionCall(funcId, ctx, false, &args, 0, useVariable, stackOffset, funcPtrVar); } -int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, eTokenType op) { // Don't allow any operators on expressions that take address of class method, but allow it on global functions if( (lctx->IsClassMethod()) || (rctx->IsClassMethod()) ) @@ -11161,27 +12168,30 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSE return -1; } + if( op == ttUnrecognizedToken ) + op = node->tokenType; + IsVariableInitialized(&lctx->type, node); IsVariableInitialized(&rctx->type, node); if( lctx->type.isExplicitHandle || rctx->type.isExplicitHandle || lctx->type.IsNullConstant() || rctx->type.IsNullConstant() || - node->tokenType == ttIs || node->tokenType == ttNotIs ) + op == ttIs || op == ttNotIs ) { - CompileOperatorOnHandles(node, lctx, rctx, ctx); + CompileOperatorOnHandles(node, lctx, rctx, ctx, op); return 0; } else { // Compile an overloaded operator for the two operands - if( CompileOverloadedDualOperator(node, lctx, rctx, ctx) ) + if( CompileOverloadedDualOperator(node, lctx, rctx, ctx, false, op) ) return 0; // If both operands are objects, then we shouldn't continue if( lctx->type.dataType.IsObject() && rctx->type.dataType.IsObject() ) { asCString str; - str.Format(TXT_NO_MATCHING_OP_FOUND_FOR_TYPES_s_AND_s, lctx->type.dataType.Format().AddressOf(), rctx->type.dataType.Format().AddressOf()); + str.Format(TXT_NO_MATCHING_OP_FOUND_FOR_TYPES_s_AND_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), rctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.SetDummy(); return -1; @@ -11205,7 +12215,6 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSE // Math operators // + - * / % ** += -= *= /= %= **= - int op = node->tokenType; if( op == ttPlus || op == ttAddAssign || op == ttMinus || op == ttSubAssign || op == ttStar || op == ttMulAssign || @@ -11213,7 +12222,7 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSE op == ttPercent || op == ttModAssign || op == ttStarStar || op == ttPowAssign ) { - CompileMathOperator(node, lctx, rctx, ctx); + CompileMathOperator(node, lctx, rctx, ctx, op); return 0; } @@ -11226,7 +12235,7 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSE op == ttBitShiftRight || op == ttShiftRightLAssign || op == ttBitShiftRightArith || op == ttShiftRightAAssign ) { - CompileBitwiseOperator(node, lctx, rctx, ctx); + CompileBitwiseOperator(node, lctx, rctx, ctx, op); return 0; } @@ -11236,7 +12245,7 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSE op == ttLessThan || op == ttLessThanOrEqual || op == ttGreaterThan || op == ttGreaterThanOrEqual ) { - CompileComparisonOperator(node, lctx, rctx, ctx); + CompileComparisonOperator(node, lctx, rctx, ctx, op); return 0; } @@ -11244,7 +12253,7 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSE // && || ^^ if( op == ttAnd || op == ttOr || op == ttXor ) { - CompileBooleanOperator(node, lctx, rctx, ctx); + CompileBooleanOperator(node, lctx, rctx, ctx, op); return 0; } } @@ -11306,15 +12315,21 @@ void asCCompiler::ConvertToVariable(asSExprContext *ctx) } else { + Dereference(ctx, true); + // Copy the object handle to a variable ctx->bc.InstrSHORT(asBC_PSF, (short)offset); ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType()); ctx->bc.Instr(asBC_PopPtr); } + // As this is an object the reference must be placed on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); ctx->type.SetVariable(ctx->type.dataType, offset, true); ctx->type.dataType.MakeHandle(true); + ctx->type.dataType.MakeReference(true); } else if( (!ctx->type.isVariable || ctx->type.dataType.IsReference()) && ctx->type.dataType.IsPrimitive() ) @@ -11366,46 +12381,118 @@ void asCCompiler::ConvertToVariableNotIn(asSExprContext *ctx, asSExprContext *ex reservedVariables.SetLength(l); } +void asCCompiler::ImplicitConvObjectToBestMathType(asSExprContext *ctx, asCScriptNode *node) +{ + asCArray funcs; + asCObjectType *ot = ctx->type.dataType.GetObjectType(); + if( ot ) + { + for( unsigned int n = 0; n < ot->methods.GetLength(); n++ ) + { + // Consider only implicit casts + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( func->name == "opImplConv" && + func->returnType.IsPrimitive() && + func->parameterTypes.GetLength() == 0 ) + funcs.PushLast(ot->methods[n]); + } -void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) + // Use the one with the highest precision + const eTokenType match[10] = {ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}; + while( funcs.GetLength() > 1 ) + { + eTokenType returnType = builder->GetFunctionDescription(funcs[0])->returnType.GetTokenType(); + int value1 = 11, value2 = 11; + for( asUINT i = 0; i < 10; i++ ) + { + if( returnType == match[i] ) + { + value1 = i; + break; + } + } + + for( asUINT n = 1; n < funcs.GetLength(); n++ ) + { + returnType = builder->GetFunctionDescription(funcs[n])->returnType.GetTokenType(); + for( asUINT i = 0; i < 10; i++ ) + { + if( returnType == match[i] ) + { + value2 = i; + break; + } + } + + if( value2 >= value1 ) + { + // Remove this and continue searching + funcs.RemoveIndexUnordered(n--); + } + else + { + // Remove the first, and start over + funcs.RemoveIndexUnordered(0); + break; + } + } + } + + // Do the conversion + if( funcs.GetLength() ) + ImplicitConvObjectToPrimitive(ctx, builder->GetFunctionDescription(funcs[0])->returnType, node, asIC_IMPLICIT_CONV); + } +} + +void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, eTokenType op) { // TODO: If a constant is only using 32bits, then a 32bit operation is preferred // TODO: clean up: This initial part is identical to CompileComparisonOperator. Make a common function out of it - // Implicitly convert the operands to a number type - asCDataType to; - // If either operand is a non-primitive then use the primitive type if( !lctx->type.dataType.IsPrimitive() ) - to.SetTokenType(rctx->type.dataType.GetTokenType()); - else if( !rctx->type.dataType.IsPrimitive() ) - to.SetTokenType(lctx->type.dataType.GetTokenType()); - else if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() ) + { + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + ImplicitConvObjectToBestMathType(lctx, node); + reservedVariables.SetLength(l); + } + if( !rctx->type.dataType.IsPrimitive() ) + { + int l = int(reservedVariables.GetLength()); + lctx->bc.GetVarsUsed(reservedVariables); + ImplicitConvObjectToBestMathType(rctx, node); + reservedVariables.SetLength(l); + } + + // Both types must now be primitives. Implicitly convert them so they match + asCDataType to; + if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() ) to.SetTokenType(ttDouble); else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() ) to.SetTokenType(ttFloat); else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) { // Convert to int64 if both are signed or if one is non-constant and signed - if( (lctx->type.dataType.IsIntegerType() && rctx->type.dataType.IsIntegerType()) || - (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || + if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || (rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) ) to.SetTokenType(ttInt64); - else + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) to.SetTokenType(ttUInt64); + else + to.SetTokenType(ttInt64); } else { // Convert to int32 if both are signed or if one is non-constant and signed - if( (lctx->type.dataType.IsIntegerType() && rctx->type.dataType.IsIntegerType()) || - (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || + if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || (rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) ) to.SetTokenType(ttInt); else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) to.SetTokenType(ttUInt); - else if( lctx->type.dataType.IsBooleanType() || rctx->type.dataType.IsBooleanType() ) - to.SetTokenType(ttBool); + else + to.SetTokenType(ttInt); } // If doing an operation with double constant and float variable, the constant should be converted to float @@ -11413,6 +12500,20 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, (rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) ) to.SetTokenType(ttFloat); + if( op == ttUnrecognizedToken ) + op = node->tokenType; + + // If integer division is disabled, convert to floating-point + if( engine->ep.disableIntegerDivision && + (op == ttSlash || op == ttDivAssign) && + (to.IsIntegerType() || to.IsUnsignedType()) ) + { + // Use double to avoid losing precision when dividing with 32bit ints + // For 64bit ints there is unfortunately no greater type so with those + // there is still a risk of loosing precision + to.SetTokenType(ttDouble); + } + // Do the actual conversion int l = int(reservedVariables.GetLength()); rctx->bc.GetVarsUsed(reservedVariables); @@ -11423,7 +12524,6 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, if( rctx->type.dataType.IsReference() ) ConvertToVariable(rctx); - int op = node->tokenType; if( to.IsPrimitive() ) { // ttStarStar allows an integer, right-hand operand and a double @@ -11452,7 +12552,7 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, !lctx->type.dataType.IsDoubleType() ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, lctx->type.dataType.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.SetDummy(); @@ -11465,7 +12565,7 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, !rctx->type.dataType.IsDoubleType() ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, rctx->type.dataType.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.SetDummy(); @@ -11674,6 +12774,9 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, v = as_powi(lctx->type.intValue, rctx->type.intValue, isOverflow); else v = as_powu(lctx->type.dwordValue, rctx->type.dwordValue, isOverflow); + + if( isOverflow ) + Error(TXT_POW_OVERFLOW, node); } ctx->type.SetConstantDW(lctx->type.dataType, v); @@ -11720,6 +12823,9 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, v = as_powi64(asINT64(lctx->type.qwordValue), asINT64(rctx->type.qwordValue), isOverflow); else v = as_powu64(lctx->type.qwordValue, rctx->type.qwordValue, isOverflow); + + if( isOverflow ) + Error(TXT_POW_OVERFLOW, node); } ctx->type.SetConstantQW(lctx->type.dataType, v); @@ -11753,8 +12859,13 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, v = fmodf(lctx->type.floatValue, rctx->type.floatValue); } else if( op == ttStarStar ) + { v = pow(lctx->type.floatValue, rctx->type.floatValue); + if( v == HUGE_VAL ) + Error(TXT_POW_OVERFLOW, node); + } + ctx->type.SetConstantF(lctx->type.dataType, v); } else if( lctx->type.dataType.IsDoubleType() ) @@ -11765,7 +12876,11 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, asASSERT(rctx->type.dataType.GetSizeInMemoryDWords() == 1); if( op == ttStarStar || op == ttPowAssign ) + { v = pow(lctx->type.doubleValue, rctx->type.intValue); + if( v == HUGE_VAL ) + Error(TXT_POW_OVERFLOW, node); + } else asASSERT(false); // Should not be possible } @@ -11792,7 +12907,11 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, v = fmod(lctx->type.doubleValue, rctx->type.doubleValue); } else if( op == ttStarStar ) + { v = pow(lctx->type.doubleValue, rctx->type.doubleValue); + if( v == HUGE_VAL ) + Error(TXT_POW_OVERFLOW, node); + } } ctx->type.SetConstantD(lctx->type.dataType, v); @@ -11805,11 +12924,12 @@ void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, } } -void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, eTokenType op) { // TODO: If a constant is only using 32bits, then a 32bit operation is preferred - int op = node->tokenType; + if( op == ttUnrecognizedToken ) + op = node->tokenType; if( op == ttAmp || op == ttAndAssign || op == ttBitOr || op == ttOrAssign || op == ttBitXor || op == ttXorAssign ) @@ -11818,33 +12938,41 @@ void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lc asCDataType to; if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) - to.SetTokenType(ttUInt64); + to.SetTokenType(ttInt64); else - to.SetTokenType(ttUInt); + to.SetTokenType(ttInt); - // Do the actual conversion + // Do the actual conversion (keep sign/unsigned if possible) int l = int(reservedVariables.GetLength()); rctx->bc.GetVarsUsed(reservedVariables); + if( lctx->type.dataType.IsUnsignedType() ) + to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttUInt : ttUInt64 ); + else + to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttInt : ttInt64 ); ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true); reservedVariables.SetLength(l); // Verify that the conversion was successful - if( !lctx->type.dataType.IsUnsignedType() ) + if( lctx->type.dataType != to ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); Error(str, node); } - // Convert right hand operand to same type as left hand operand + // Convert right hand operand to same size as left hand l = int(reservedVariables.GetLength()); lctx->bc.GetVarsUsed(reservedVariables); - ImplicitConversion(rctx, lctx->type.dataType, node, asIC_IMPLICIT_CONV, true); + if( rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttUInt : ttUInt64 ); + else + to.SetTokenType( to.GetSizeOnStackDWords() == 1 ? ttInt : ttInt64 ); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true); reservedVariables.SetLength(l); - if( !rctx->type.dataType.IsEqualExceptRef(lctx->type.dataType) ) + if( rctx->type.dataType != to ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); } @@ -11937,7 +13065,7 @@ void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lc if( lctx->type.dataType.IsObject() ) { asCString str; - str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf()); + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf()); Error(str, node); // Set an integer value and allow the compiler to continue @@ -11971,7 +13099,7 @@ void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lc if( lctx->type.dataType != to ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); Error(str, node); } @@ -11983,7 +13111,7 @@ void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lc if( !rctx->type.dataType.IsUnsignedType() ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), "uint"); + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "uint"); Error(str, node); } @@ -12068,43 +13196,55 @@ void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lc } } -void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, eTokenType op) { // Both operands must be of the same type - // Implicitly convert the operands to a number type - asCDataType to; - - // If either operand is a non-primitive then use the primitive type + // If either operand is a non-primitive then first convert them to the best number type if( !lctx->type.dataType.IsPrimitive() ) - to.SetTokenType(rctx->type.dataType.GetTokenType()); - else if( !rctx->type.dataType.IsPrimitive() ) - to.SetTokenType(lctx->type.dataType.GetTokenType()); - else if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() ) + { + int l = int(reservedVariables.GetLength()); + rctx->bc.GetVarsUsed(reservedVariables); + ImplicitConvObjectToBestMathType(lctx, node); + reservedVariables.SetLength(l); + } + if( !rctx->type.dataType.IsPrimitive() ) + { + int l = int(reservedVariables.GetLength()); + lctx->bc.GetVarsUsed(reservedVariables); + ImplicitConvObjectToBestMathType(rctx, node); + reservedVariables.SetLength(l); + } + + // Implicitly convert the operands to matching types + asCDataType to; + if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() ) to.SetTokenType(ttDouble); else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() ) to.SetTokenType(ttFloat); else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) { // Convert to int64 if both are signed or if one is non-constant and signed - if( (lctx->type.dataType.IsIntegerType() && rctx->type.dataType.IsIntegerType()) || - (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || + if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || (rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) ) to.SetTokenType(ttInt64); - else + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) to.SetTokenType(ttUInt64); + else + to.SetTokenType(ttInt64); } else { // Convert to int32 if both are signed or if one is non-constant and signed - if( (lctx->type.dataType.IsIntegerType() && rctx->type.dataType.IsIntegerType()) || - (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || + if( (lctx->type.dataType.IsIntegerType() && !lctx->type.isConstant) || (rctx->type.dataType.IsIntegerType() && !rctx->type.isConstant) ) to.SetTokenType(ttInt); else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) to.SetTokenType(ttUInt); else if( lctx->type.dataType.IsBooleanType() || rctx->type.dataType.IsBooleanType() ) to.SetTokenType(ttBool); + else + to.SetTokenType(ttInt); } // If doing an operation with double constant and float variable, the constant should be converted to float @@ -12147,6 +13287,12 @@ void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext if( signMismatch ) Warning(TXT_SIGNED_UNSIGNED_MISMATCH, node); + // Attempt to resolve ambiguous enumerations + if( lctx->type.dataType.IsEnumType() && rctx->enumValue != "" ) + ImplicitConversion(rctx, lctx->type.dataType, node, asIC_IMPLICIT_CONV); + else if( rctx->type.dataType.IsEnumType() && lctx->enumValue != "" ) + ImplicitConversion(lctx, rctx->type.dataType, node, asIC_IMPLICIT_CONV); + // Do the actual conversion int l = int(reservedVariables.GetLength()); rctx->bc.GetVarsUsed(reservedVariables); @@ -12165,7 +13311,7 @@ void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext if( !lctx->type.dataType.IsEqualExceptConst(to) ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ok = false; } @@ -12173,7 +13319,7 @@ void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext if( !rctx->type.dataType.IsEqualExceptConst(to) ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ok = false; } @@ -12187,13 +13333,14 @@ void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext } bool isConstant = lctx->type.isConstant && rctx->type.isConstant; - int op = node->tokenType; + + if( op == ttUnrecognizedToken ) + op = node->tokenType; if( !isConstant ) { if( to.IsBooleanType() ) { - int op = node->tokenType; if( op == ttEqual || op == ttNotEqual ) { // Must convert to temporary variable, because we are changing the value before comparison @@ -12292,7 +13439,6 @@ void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext { if( to.IsBooleanType() ) { - int op = node->tokenType; if( op == ttEqual || op == ttNotEqual ) { // Make sure they are equal if not false @@ -12397,7 +13543,7 @@ void asCCompiler::PushVariableOnStack(asSExprContext *ctx, bool asReference) } } -void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, eTokenType op) { // Both operands must be booleans asCDataType to; @@ -12407,15 +13553,19 @@ void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asSExprContext *lc int l = int(reservedVariables.GetLength()); rctx->bc.GetVarsUsed(reservedVariables); lctx->bc.GetVarsUsed(reservedVariables); - ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV); - ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV); + + // Allow value types to be converted to bool using 'bool opImplConv()' + if( lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV); + if( rctx->type.dataType.GetObjectType() && (rctx->type.dataType.GetObjectType()->GetFlags() & asOBJ_VALUE) ) + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV); reservedVariables.SetLength(l); // Verify that the conversion was successful if( !lctx->type.dataType.IsBooleanType() ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), "bool"); + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "bool"); Error(str, node); // Force the conversion to allow compilation to proceed lctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); @@ -12424,7 +13574,7 @@ void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asSExprContext *lc if( !rctx->type.dataType.IsBooleanType() ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), "bool"); + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), "bool"); Error(str, node); // Force the conversion to allow compilation to proceed rctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); @@ -12435,7 +13585,8 @@ void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asSExprContext *lc ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true)); // What kind of operator is it? - int op = node->tokenType; + if( op == ttUnrecognizedToken ) + op = node->tokenType; if( op == ttXor ) { if( !isConstant ) @@ -12559,7 +13710,7 @@ void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asSExprContext *lc } } -void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, eTokenType opToken) { // Process the property accessor as get ProcessPropertyGetAccessor(lctx, node); @@ -12578,8 +13729,11 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext * ReleaseTemporaryVariable(offset, 0); } + if( opToken == ttUnrecognizedToken ) + opToken = node->tokenType; + // Warn if not both operands are explicit handles or null handles - if( (node->tokenType == ttEqual || node->tokenType == ttNotEqual) && + if( (opToken == ttEqual || opToken == ttNotEqual) && ((!(lctx->type.isExplicitHandle || lctx->type.IsNullConstant()) && !(lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE))) || (!(rctx->type.isExplicitHandle || rctx->type.IsNullConstant()) && !(rctx->type.dataType.GetObjectType() && (rctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE)))) ) { @@ -12589,8 +13743,8 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext * // If one of the operands is a value type used as handle, we should look for the opEquals method if( ((lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE)) || (rctx->type.dataType.GetObjectType() && (rctx->type.dataType.GetObjectType()->flags & asOBJ_ASHANDLE))) && - (node->tokenType == ttEqual || node->tokenType == ttIs || - node->tokenType == ttNotEqual || node->tokenType == ttNotIs) ) + (opToken == ttEqual || opToken == ttIs || + opToken == ttNotEqual || opToken == ttNotIs) ) { // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used // Find the matching opEquals method @@ -12603,7 +13757,7 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext * if( r == 1 ) { - if( node->tokenType == ttNotEqual || node->tokenType == ttNotIs ) + if( opToken == ttNotEqual || opToken == ttNotIs ) ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset); // Success, don't continue @@ -12629,8 +13783,17 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext * to = lctx->type.dataType; else { - // TODO: Use the common base type - to = lctx->type.dataType; + // Find a common base type + asSExprContext tmp(engine); + tmp.type = rctx->type; + ImplicitConversion(&tmp, lctx->type.dataType, 0, asIC_IMPLICIT_CONV, false); + if( tmp.type.dataType.GetObjectType() == lctx->type.dataType.GetObjectType() ) + to = lctx->type.dataType; + else + to = rctx->type.dataType; + + // Assume handle-to-const as it is not possible to convert handle-to-const to handle-to-non-const + to.MakeHandleToConst(true); } // Need to pop the value if it is a null constant @@ -12661,14 +13824,14 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext * if( !lctx->type.dataType.IsEqualExceptConst(to) ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); Error(str, node); } if( !rctx->type.dataType.IsEqualExceptConst(to) ) { asCString str; - str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format(outFunc->nameSpace).AddressOf(), to.Format(outFunc->nameSpace).AddressOf()); Error(str, node); } @@ -12680,19 +13843,23 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext * ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true)); - int op = node->tokenType; - if( op == ttEqual || op == ttNotEqual || op == ttIs || op == ttNotIs ) + if( opToken == ttEqual || opToken == ttNotEqual || opToken == ttIs || opToken == ttNotIs ) { - // If the object handle already is in a variable we must manually pop it from the stack - if( lctx->type.isVariable ) - lctx->bc.Instr(asBC_PopPtr); - if( rctx->type.isVariable ) - rctx->bc.Instr(asBC_PopPtr); + // Make sure handles received as parameters by reference are copied to a local variable before the + // asBC_CmpPtr, so we don't end up comparing the reference to the handle instead of the handle itself + if( lctx->type.isVariable && !lctx->type.isTemporary && lctx->type.stackOffset <= 0 ) + lctx->type.isVariable = false; + if( rctx->type.isVariable && !rctx->type.isTemporary && rctx->type.stackOffset <= 0 ) + rctx->type.isVariable = false; - // TODO: runtime optimize: don't do REFCPY + // TODO: runtime optimize: don't do REFCPY if not necessary ConvertToVariableNotIn(lctx, rctx); ConvertToVariable(rctx); + // Pop the pointers from the stack as they will not be used + lctx->bc.Instr(asBC_PopPtr); + rctx->bc.Instr(asBC_PopPtr); + MergeExprBytecode(ctx, lctx); MergeExprBytecode(ctx, rctx); @@ -12702,9 +13869,9 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext * ctx->bc.InstrW_W(asBC_CmpPtr, b, c); - if( op == ttEqual || op == ttIs ) + if( opToken == ttEqual || opToken == ttIs ) ctx->bc.Instr(asBC_TZ); - else if( op == ttNotEqual || op == ttNotIs ) + else if( opToken == ttNotEqual || opToken == ttNotIs ) ctx->bc.Instr(asBC_TNZ); ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); @@ -12735,42 +13902,77 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo Error(msg, ctx->exprNode); } - // Check if the function is private + // Check if the function is private or protected if( descr->isPrivate && descr->GetObjectType() != outFunc->GetObjectType() ) { asCString msg; msg.Format(TXT_PRIVATE_METHOD_CALL_s, descr->GetDeclarationStr().AddressOf()); Error(msg, ctx->exprNode); } + else if( descr->isProtected && + !(descr->GetObjectType() == outFunc->GetObjectType() || + (outFunc->GetObjectType() && outFunc->GetObjectType()->DerivesFrom(descr->GetObjectType()))) ) + { + asCString msg; + msg.Format(TXT_PROTECTED_METHOD_CALL_s, descr->GetDeclarationStr().AddressOf()); + Error(msg, ctx->exprNode); + } int argSize = descr->GetSpaceNeededForArguments(); - if( descr->objectType && descr->returnType.IsReference() && - !(ctx->type.isVariable || ctx->type.isTemporary) && + // If we're calling a class method we must make sure the object is guaranteed to stay + // alive throughout the call by holding on to a reference in a local variable. This must + // be done for any methods that return references, and any calls on script objects. + // Application registered objects are assumed to know to keep themselves alive even + // if the method doesn't return a refernce. + if( descr->objectType && (ctx->type.dataType.IsObjectHandle() || ctx->type.dataType.SupportHandles()) && + (descr->returnType.IsReference() || (ctx->type.dataType.GetObjectType()->GetFlags() & asOBJ_SCRIPT_OBJECT)) && + !(ctx->type.isVariable || ctx->type.isTemporary) && !(ctx->type.dataType.GetObjectType()->GetFlags() & asOBJ_SCOPED) && !(ctx->type.dataType.GetObjectType()->GetFlags() & asOBJ_ASHANDLE) ) { - // The class method we're calling is returning a reference, which may be to a member of the object. - // In order to guarantee the lifetime of the reference, we must hold a local reference to the object. + // TODO: runtime optimize: Avoid this for global variables, by storing a reference to the global variable once in a + // local variable and then refer to the same for each call. An alias for the global variable + // should be stored in the variable scope so that the compiler can find it. For loops and + // scopes that will always be executed, i.e. non-if scopes the alias should be stored in the + // higher scope to increase the probability of re-use. + // TODO: runtime optimize: This can be avoided for local variables (non-handles) as they have a well defined life time + int tempRef = AllocateVariable(ctx->type.dataType, true); ctx->bc.InstrSHORT(asBC_PSF, (short)tempRef); ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType()); - // Add the release of this reference, as a deferred expression + // Add the release of this reference as a deferred expression asSDeferredParam deferred; deferred.origExpr = 0; deferred.argInOutFlags = asTM_INREF; deferred.argNode = 0; deferred.argType.SetVariable(ctx->type.dataType, tempRef, true); - ctx->deferredParams.PushLast(deferred); // Forget the current type ctx->type.SetDummy(); } + // Check if there is a need to add a hidden pointer for when the function returns an object by value + if( descr->DoesReturnOnStack() && !useVariable ) + { + useVariable = true; + varOffset = AllocateVariable(descr->returnType, true); + + // Push the pointer to the pre-allocated space for the return value + ctx->bc.InstrSHORT(asBC_PSF, short(varOffset)); + + if( descr->objectType ) + { + // The object pointer is already on the stack, but should be the top + // one, so we need to swap the pointers in order to get the correct + ctx->bc.Instr(asBC_SwapPtr); + } + } + if( isConstructor ) { // Sometimes the value types are allocated on the heap, @@ -12778,7 +13980,30 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo asASSERT(useVariable == false); - ctx->bc.Alloc(asBC_ALLOC, objType, descr->id, argSize+AS_PTR_SIZE); + if( (objType->flags & asOBJ_TEMPLATE) ) + { + asASSERT( descr->funcType == asFUNC_SCRIPT ); + + // Find the id of the real constructor and not the generated stub + asUINT id = 0; + asDWORD *bc = descr->scriptData->byteCode.AddressOf(); + while( bc ) + { + if( (*(asBYTE*)bc) == asBC_CALLSYS ) + { + id = asBC_INTARG(bc); + break; + } + bc += asBCTypeSize[asBCInfo[*(asBYTE*)bc].type]; + } + + asASSERT( id ); + + ctx->bc.InstrPTR(asBC_OBJTYPE, objType); + ctx->bc.Alloc(asBC_ALLOC, objType, id, argSize + AS_PTR_SIZE + AS_PTR_SIZE); + } + else + ctx->bc.Alloc(asBC_ALLOC, objType, descr->id, argSize+AS_PTR_SIZE); // The instruction has already moved the returned object to the variable ctx->type.Set(asCDataType::CreatePrimitive(ttVoid, false)); @@ -12800,9 +14025,7 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo // If the function returns an object by value the address of the location // where the value should be stored is passed as an argument too if( descr->DoesReturnOnStack() ) - { argSize += AS_PTR_SIZE; - } // TODO: runtime optimize: If it is known that a class method cannot be overridden the call // should be made with asBC_CALL as it is faster. Examples where this @@ -12828,6 +14051,8 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo { int returnOffset = 0; + asCTypeInfo tmpExpr = ctx->type; + if( descr->DoesReturnOnStack() ) { asASSERT( useVariable ); @@ -12860,6 +14085,8 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset); } + ReleaseTemporaryVariable(tmpExpr, &ctx->bc); + ctx->type.dataType.MakeReference(IsVariableOnHeap(returnOffset)); ctx->type.isLValue = false; // It is a reference, but not an lvalue @@ -12916,6 +14143,8 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo { asASSERT(useVariable == false); + asCTypeInfo tmpExpr = ctx->type; + if( descr->returnType.GetSizeInMemoryBytes() ) { // Allocate a temporary variable to hold the value, but make sure @@ -12941,6 +14170,8 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo else ctx->type.Set(descr->returnType); + ReleaseTemporaryVariable(tmpExpr, &ctx->bc); + ctx->type.isLValue = false; // Clean up arguments diff --git a/lib/angelscript/source/as_compiler.h b/lib/angelscript/source/as_compiler.h index 69ae45069..bd0ff7fd8 100644 --- a/lib/angelscript/source/as_compiler.h +++ b/lib/angelscript/source/as_compiler.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -103,6 +103,7 @@ struct asSExprContext property_handle = false; property_ref = false; methodName = ""; + enumValue = ""; } bool IsClassMethod() { @@ -143,6 +144,13 @@ struct asSOverloadCandidate asUINT cost; }; +struct asSNamedArgument +{ + asCString name; + asSExprContext *ctx; + asUINT match; +}; + enum EImplicitConv { asIC_IMPLICIT_CONV, @@ -206,31 +214,34 @@ protected: int CompileFunctionCall(asCScriptNode *node, asSExprContext *out, asCObjectType *objectType, bool objIsConst, const asCString &scope = ""); void CompileConstructCall(asCScriptNode *node, asSExprContext *out); void CompileConversion(asCScriptNode *node, asSExprContext *out); - int CompileOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); - void CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); - void CompileMathOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); - void CompileBitwiseOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); - void CompileComparisonOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); - void CompileBooleanOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); - bool CompileOverloadedDualOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); + int CompileOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileMathOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileBitwiseOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileComparisonOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); + void CompileBooleanOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, eTokenType opToken = ttUnrecognizedToken); + bool CompileOverloadedDualOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out, bool isHandle = false, eTokenType opToken = ttUnrecognizedToken); int CompileOverloadedDualOperator2(asCScriptNode *node, const char *methodName, asSExprContext *l, asSExprContext *r, asSExprContext *out, bool specificReturn = false, const asCDataType &returnType = asCDataType::CreatePrimitive(ttVoid, false)); void CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByteCode *bc, int isVarGlobOrMem); - int CompileInitListElement(asSListPatternNode *&patternNode, asCScriptNode *&valueNode, int bufferTypeId, short bufferVar, asUINT &bufferSize, asCByteCode &byteCode); + int CompileInitListElement(asSListPatternNode *&patternNode, asCScriptNode *&valueNode, int bufferTypeId, short bufferVar, asUINT &bufferSize, asCByteCode &byteCode, int &elementsInSubList); - int CallDefaultConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem = 0, bool derefDest = false); + int CallDefaultConstructor(const asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asCScriptNode *node, int isVarGlobOrMem = 0, bool derefDest = false); int CallCopyConstructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc, asSExprContext *arg, asCScriptNode *node, bool isGlobalVar = false, bool derefDestination = false); void CallDestructor(asCDataType &type, int offset, bool isObjectOnHeap, asCByteCode *bc); - int CompileArgumentList(asCScriptNode *node, asCArray &args); - int CompileDefaultArgs(asCScriptNode *node, asCArray &args, asCScriptFunction *func); - asUINT MatchFunctions(asCArray &funcs, asCArray &args, asCScriptNode *node, const char *name, asCObjectType *objectType = NULL, bool isConstMethod = false, bool silent = false, bool allowObjectConstruct = true, const asCString &scope = ""); + int CompileArgumentList(asCScriptNode *node, asCArray &args, asCArray &namedArgs); + int CompileDefaultAndNamedArgs(asCScriptNode *node, asCArray &args, int funcId, asCObjectType *type, asCArray *namedArgs = 0); + asUINT MatchFunctions(asCArray &funcs, asCArray &args, asCScriptNode *node, const char *name, asCArray *namedArgs = NULL, asCObjectType *objectType = NULL, bool isConstMethod = false, bool silent = false, bool allowObjectConstruct = true, const asCString &scope = ""); int CompileVariableAccess(const asCString &name, const asCString &scope, asSExprContext *ctx, asCScriptNode *errNode, bool isOptional = false, bool noFunction = false, bool noGlobal = false, asCObjectType *objType = 0); void CompileMemberInitialization(asCByteCode *bc, bool onlyDefaults); - bool CompileInitialization(asCScriptNode *node, asCByteCode *bc, asCDataType &type, asCScriptNode *errNode, int offset, asQWORD *constantValue, int isVarGlobOrMem); + bool CompileAutoType(asCDataType &autoType, asSExprContext &compiledCtx, asCScriptNode *exprNode, asCScriptNode *errNode); + bool CompileInitialization(asCScriptNode *node, asCByteCode *bc, asCDataType &type, asCScriptNode *errNode, int offset, asQWORD *constantValue, int isVarGlobOrMem, asSExprContext *preCompiled = 0); + void CompileInitAsCopy(asCDataType &type, int offset, asCByteCode *bc, asSExprContext *arg, asCScriptNode *node, bool derefDestination); // Helper functions void ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode *node); int ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext *arg, asCScriptNode *node); + int ProcessPropertyGetSetAccessor(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, eTokenType op, asCScriptNode *errNode); int FindPropertyAccessor(const asCString &name, asSExprContext *ctx, asCScriptNode *node, asSNameSpace *ns, bool isThisAccess = false); int FindPropertyAccessor(const asCString &name, asSExprContext *ctx, asSExprContext *arg, asCScriptNode *node, asSNameSpace *ns, bool isThisAccess = false); void PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ctx, bool forceOnHeap = false); @@ -241,16 +252,17 @@ protected: void Dereference(asSExprContext *ctx, bool generateCode); bool CompileRefCast(asSExprContext *ctx, const asCDataType &to, bool isExplicit, asCScriptNode *node, bool generateCode = true); asUINT MatchArgument(asCArray &funcs, asCArray &matches, const asSExprContext *argExpr, int paramNum, bool allowObjectConstruct = true); + int MatchArgument(asCScriptFunction *desc, const asSExprContext *argExpr, int paramNum, bool allowObjectConstruct = true); void PerformFunctionCall(int funcId, asSExprContext *out, bool isConstructor = false, asCArray *args = 0, asCObjectType *objTypeForConstruct = 0, bool useVariable = false, int varOffset = 0, int funcPtrVar = 0); void MoveArgsToStack(int funcId, asCByteCode *bc, asCArray &args, bool addOneToOffset); void MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode *node, bool useVariable = false, int stackOffset = 0, int funcPtrVar = 0); void PrepareFunctionCall(int funcId, asCByteCode *bc, asCArray &args); void AfterFunctionCall(int funcId, asCArray &args, asSExprContext *ctx, bool deferAll); void ProcessDeferredParams(asSExprContext *ctx); - void PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction = false, int refType = 0, bool isMakingCopy = false); + int PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction = false, int refType = 0, bool isMakingCopy = false); void PrepareArgument2(asSExprContext *ctx, asSExprContext *arg, asCDataType *paramType, bool isFunction = false, int refType = 0, bool isMakingCopy = false); bool IsLValue(asCTypeInfo &type); - int DoAssignment(asSExprContext *out, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, int op, asCScriptNode *opNode); + int DoAssignment(asSExprContext *out, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, eTokenType op, asCScriptNode *opNode); void MergeExprBytecode(asSExprContext *before, asSExprContext *after); void MergeExprBytecodeAndType(asSExprContext *before, asSExprContext *after); void FilterConst(asCArray &funcs, bool removeConst = true); @@ -275,6 +287,7 @@ protected: asUINT ImplicitConvObjectRef(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode); asUINT ImplicitConvObjectValue(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode); void ImplicitConversionConstant(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType); + void ImplicitConvObjectToBestMathType(asSExprContext *ctx, asCScriptNode *node); void LineInstr(asCByteCode *bc, size_t pos); @@ -284,7 +297,7 @@ protected: void Error(const asCString &msg, asCScriptNode *node); void Warning(const asCString &msg, asCScriptNode *node); void Information(const asCString &msg, asCScriptNode *node); - void PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node); + void PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node, asCObjectType *inType = 0); void AddVariableScope(bool isBreakScope = false, bool isContinueScope = false); void RemoveVariableScope(); void FinalizeFunction(); diff --git a/lib/angelscript/source/as_config.h b/lib/angelscript/source/as_config.h index 492dcc6aa..c9771e4ac 100644 --- a/lib/angelscript/source/as_config.h +++ b/lib/angelscript/source/as_config.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -101,6 +101,10 @@ // if the new order of the member initialization caused null pointer exceptions in older // scripts (e.g. if a base class accessed members of a derived class through a virtual method). +// AS_USE_NAMESPACE +// Adds the AngelScript namespace on the declarations. + + // // Library usage @@ -140,12 +144,12 @@ // STDCALL // This is used to declare a function to use the stdcall calling convention. -// AS_USE_NAMESPACE -// Adds the AngelScript namespace on the declarations. - // AS_NO_MEMORY_H // Some compilers don't come with the memory.h header file. +// AS_NO_THISCALL_FUNCTOR_METHOD +// Defined if the support for functor methods hasn't been implemented on the platform. + // @@ -166,6 +170,9 @@ // Embarcadero C++Builder // __BORLANDC__ is defined +// Sun CC compiler +// __SUNPRO_CC is defined + // @@ -213,6 +220,9 @@ // AS_BIG_ENDIAN // Define this for CPUs that use big endian memory layout, e.g. PPC +// AS_SPARC +// Define this for SPARC CPU family + // @@ -230,6 +240,7 @@ // AS_XBOX - Microsoft XBox // AS_XBOX360 - Microsoft XBox 360 // AS_PSP - Sony Playstation Portable +// AS_PSVITA - Sony Playstation Vita // AS_PS2 - Sony Playstation 2 // AS_PS3 - Sony Playstation 3 // AS_DC - Sega Dreamcast @@ -241,6 +252,7 @@ // AS_HAIKU - Haiku // AS_ILLUMOS - Illumos like (OpenSolaris, OpenIndiana, NCP, etc) // AS_MARMALADE - Marmalade cross platform SDK (a layer on top of the OS) +// AS_SUN - Sun UNIX @@ -339,6 +351,9 @@ #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 #define THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER +// Not implemented by default. Undefined with tested platforms. +#define AS_NO_THISCALL_FUNCTOR_METHOD + // Embarcadero C++Builder #if defined(__BORLANDC__) @@ -437,6 +452,7 @@ #define THISCALL_CALLEE_POPS_ARGUMENTS #define STDCALL __stdcall #define AS_SIZEOF_BOOL 1 + #define COMPLEX_OBJS_PASSED_BY_REF #define ASM_INTEL // Intel style for inline assembly on microsoft compilers @@ -452,14 +468,18 @@ #else #if defined(_XBOX) || (defined(_M_IX86) && !defined(__LP64__)) #define AS_X86 + #ifndef _XBOX + // Not tested with xbox (only enabled if is Windows) + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #endif #elif defined(_M_X64) #define AS_X64_MSVC + #undef AS_NO_THISCALL_FUNCTOR_METHOD #define AS_CALLEE_DESTROY_OBJ_BY_VAL #define AS_LARGE_OBJS_PASSED_BY_REF #define AS_LARGE_OBJ_MIN_SIZE 3 - #define COMPLEX_OBJS_PASSED_BY_REF - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) - #define COMPLEX_MASK (asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #define COMPLEX_MASK (asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #endif #endif @@ -468,18 +488,22 @@ #define AS_CALLEE_DESTROY_OBJ_BY_VAL #define CDECL_RETURN_SIMPLE_IN_MEMORY #define STDCALL_RETURN_SIMPLE_IN_MEMORY - #define COMPLEX_OBJS_PASSED_BY_REF - #define COMPLEX_MASK asOBJ_APP_CLASS_ASSIGNMENT - #define COMPLEX_RETURN_MASK asOBJ_APP_CLASS_ASSIGNMENT - #define AS_SOFTFP + #define COMPLEX_MASK (asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_ARRAY) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_ARRAY) + + // Windows CE uses softfp calling convention, while Windows RT uses hardfp calling convention + // ref: http://stackoverflow.com/questions/16375355/what-is-the-windows-rt-on-arm-native-code-calling-convention + #if defined(_WIN32_WCE) + #define AS_SOFTFP + #endif #endif #ifndef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT) + #define COMPLEX_MASK (asOBJ_APP_ARRAY) #endif #ifndef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_ARRAY) #endif #define UNREACHABLE_RETURN @@ -511,13 +535,26 @@ // SN Systems ProDG #if defined(__SNC__) || defined(SNSYS) - #define GNU_STYLE_VIRTUAL_METHOD #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) #define CALLEE_POPS_HIDDEN_RETURN_POINTER #define COMPLEX_OBJS_PASSED_BY_REF - #define ASM_AT_N_T // AT&T style inline assembly - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR) - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR) + + #ifdef __psp2__ + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #else + #define GNU_STYLE_VIRTUAL_METHOD + #define ASM_AT_N_T // AT&T style inline assembly + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR) + #endif + #define AS_SIZEOF_BOOL 1 #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) @@ -541,11 +578,22 @@ // Support native calling conventions on PS3 #define AS_PS3 #define AS_PPC_64 + #define AS_NO_MEMORY_H + #define AS_NO_EXCEPTIONS + #include // PSP #elif defined(__psp__) #define AS_NO_MEMORY_H #define AS_MIPS #define AS_PSP + // PSVita + #elif defined(__psp2__) + #define AS_PSVITA + #define AS_ARM + #define AS_NO_MEMORY_H + #define AS_NO_EXCEPTIONS + #define AS_CALLEE_DESTROY_OBJ_BY_VAL + #undef AS_NO_THISCALL_FUNCTOR_METHOD #endif #define UNREACHABLE_RETURN @@ -555,16 +603,12 @@ // Use the following command to determine predefined macros: echo . | g++ -dM -E - #if (defined(__GNUC__) && !defined(__SNC__)) || defined(EPPC) || defined(__CYGWIN__) // JWC -- use this instead for Wii #define GNU_STYLE_VIRTUAL_METHOD -#if !defined( __amd64__ ) - #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) -#else - #define MULTI_BASE_OFFSET(x) (*((asQWORD*)(&x)+1)) -#endif + #define MULTI_BASE_OFFSET(x) (*((asPWORD*)(&x)+1)) #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) #define CALLEE_POPS_HIDDEN_RETURN_POINTER #define COMPLEX_OBJS_PASSED_BY_REF - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR) - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_ARRAY) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_ARRAY) #define AS_NO_MEMORY_H #define AS_SIZEOF_BOOL 1 #define STDCALL __attribute__((stdcall)) @@ -618,9 +662,9 @@ #undef GNU_STYLE_VIRTUAL_METHOD #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #define AS_CALLEE_DESTROY_OBJ_BY_VAL #endif @@ -651,6 +695,7 @@ #if (defined(_ARM_) || defined(__arm__)) // iOS use ARM processor #define AS_ARM + #undef AS_NO_THISCALL_FUNCTOR_METHOD #define CDECL_RETURN_SIMPLE_IN_MEMORY #define STDCALL_RETURN_SIMPLE_IN_MEMORY #define THISCALL_RETURN_SIMPLE_IN_MEMORY @@ -666,9 +711,9 @@ #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 #define COMPLEX_OBJS_PASSED_BY_REF #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) // iOS uses soft-float ABI #define AS_SOFTFP @@ -679,8 +724,8 @@ #elif (defined(__arm64__)) // The IPhone 5S+ uses an ARM64 processor - - // AngelScript currently doesn't support native calling + + // AngelScript currently doesn't support native calling // for 64bit ARM processors so it's necessary to turn on // portability mode #define AS_MAX_PORTABILITY @@ -692,21 +737,23 @@ #elif (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) // Support native calling conventions on Mac OS X + Intel 32bit CPU #define AS_X86 + #undef AS_NO_THISCALL_FUNCTOR_METHOD #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #elif defined(__LP64__) && !defined(__ppc__) && !defined(__PPC__) && !defined(__arm64__) // http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/140-x86-64_Function_Calling_Conventions/x86_64.html#//apple_ref/doc/uid/TP40005035-SW1 #define AS_X64_GCC + #undef AS_NO_THISCALL_FUNCTOR_METHOD #define HAS_128_BIT_PRIMITIVES #define SPLIT_OBJS_BY_MEMBER_TYPES #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #define AS_LARGE_OBJS_PASSED_BY_REF #define AS_LARGE_OBJ_MIN_SIZE 5 // STDCALL is not available on 64bit Mac @@ -720,9 +767,9 @@ #define CDECL_RETURN_SIMPLE_IN_MEMORY #define STDCALL_RETURN_SIMPLE_IN_MEMORY #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #elif (defined(__ppc__) || defined(__PPC__)) && defined(__LP64__) #define AS_PPC_64 @@ -740,13 +787,14 @@ //#define STDCALL_RETURN_SIMPLE_IN_MEMORY #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) // Support native calling conventions on Intel 32bit CPU #define AS_X86 + #undef AS_NO_THISCALL_FUNCTOR_METHOD // As of version 4.7 MinGW changed the ABI, presumably // to be better aligned with how MSVC works @@ -760,6 +808,7 @@ #elif defined(__x86_64__) #define AS_X64_MINGW + #undef AS_NO_THISCALL_FUNCTOR_METHOD #define AS_LARGE_OBJS_PASSED_BY_REF #define AS_LARGE_OBJ_MIN_SIZE 3 #define COMPLEX_OBJS_PASSED_BY_REF @@ -773,9 +822,9 @@ #elif defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__) #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) #define THISCALL_RETURN_SIMPLE_IN_MEMORY @@ -785,8 +834,10 @@ // Support native calling conventions on Intel 32bit CPU #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK #define AS_X86 + #undef AS_NO_THISCALL_FUNCTOR_METHOD #elif defined(__LP64__) && !defined(__arm64__) #define AS_X64_GCC + #undef AS_NO_THISCALL_FUNCTOR_METHOD #define HAS_128_BIT_PRIMITIVES #define SPLIT_OBJS_BY_MEMBER_TYPES #define AS_LARGE_OBJS_PASSED_BY_REF @@ -797,6 +848,9 @@ #elif defined(__ARMEL__) || defined(__arm__) #define AS_ARM + // TODO: The stack unwind on exceptions currently fails due to the assembler code in as_callfunc_arm_gcc.S + #define AS_NO_EXCEPTIONS + #undef STDCALL #define STDCALL @@ -822,9 +876,12 @@ // Verify if soft-float or hard-float ABI is used #if defined(__SOFTFP__) && __SOFTFP__ == 1 // -ffloat-abi=softfp or -ffloat-abi=soft - #define AS_SOFTFP + #define AS_SOFTFP #endif + // Tested with both hard float and soft float abi + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #elif defined(__mips__) #define AS_MIPS #define AS_BIG_ENDIAN @@ -848,9 +905,9 @@ #define AS_BSD #if (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK #define AS_X86 #elif defined(__LP64__) @@ -858,9 +915,9 @@ #define HAS_128_BIT_PRIMITIVES #define SPLIT_OBJS_BY_MEMBER_TYPES #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #define AS_LARGE_OBJS_PASSED_BY_REF #define AS_LARGE_OBJ_MIN_SIZE 5 #undef STDCALL @@ -915,36 +972,47 @@ #elif defined(ANDROID) || defined(__ANDROID__) #define AS_ANDROID - // Android NDK 9+ supports posix threads + // Android 2.3+ supports posix threads #define AS_POSIX_THREADS + // Common configuration with Android arm and x86 #define CDECL_RETURN_SIMPLE_IN_MEMORY #define STDCALL_RETURN_SIMPLE_IN_MEMORY #define THISCALL_RETURN_SIMPLE_IN_MEMORY - #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE - #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE - #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE - - #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 - #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 - #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #undef COMPLEX_MASK + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) + #undef COMPLEX_RETURN_MASK + #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | asOBJ_APP_ARRAY) #if (defined(_ARM_) || defined(__arm__)) + // Android ARM + + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + // The stdcall calling convention is not used on the arm cpu #undef STDCALL #define STDCALL #undef GNU_STYLE_VIRTUAL_METHOD - #undef COMPLEX_MASK - #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) - #undef COMPLEX_RETURN_MASK - #define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) - #define AS_ARM + #undef AS_NO_THISCALL_FUNCTOR_METHOD #define AS_SOFTFP #define AS_CALLEE_DESTROY_OBJ_BY_VAL + #elif (defined(i386) || defined(__i386) || defined(__i386__)) && !defined(__LP64__) + // Android Intel x86 (same config as Linux x86). Tested with Intel x86 Atom System Image. + + // Support native calling conventions on Intel 32bit CPU + #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + #define AS_X86 + #undef AS_NO_THISCALL_FUNCTOR_METHOD #endif // Haiku OS @@ -1001,6 +1069,39 @@ #define UNREACHABLE_RETURN #endif +// Sun CC +// Initial information provided by Andrey Bergman +#if defined(__SUNPRO_CC) + #if defined(__sparc) + #define AS_SPARC + #endif + + #if defined(__sun) + #define AS_SUN + #endif + + // Native calling conventions is not yet supported for Sun CC + #if !defined(AS_MAX_PORTABILITY) + #define AS_MAX_PORTABILITY + #endif + + // I presume Sun CC uses a similar structure of method pointers as gnuc + #define MULTI_BASE_OFFSET(x) (*((asPWORD*)(&x)+1)) + + #if !defined(AS_SIZEOF_BOOL) + #define AS_SIZEOF_BOOL 1 // sizeof(bool) == 1 + #endif + #if !defined(UNREACHABLE_RETURN) + #define UNREACHABLE_RETURN + #endif + #if !defined(STDCALL) + #define STDCALL // There is no stdcall on Solaris/SunPro/SPARC + #endif + #if !defined(asVSNPRINTF) + #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) + #endif +#endif + // // Detect target hardware diff --git a/lib/angelscript/source/as_configgroup.cpp b/lib/angelscript/source/as_configgroup.cpp index 1ae08ea8d..882fc4645 100644 --- a/lib/angelscript/source/as_configgroup.cpp +++ b/lib/angelscript/source/as_configgroup.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -88,10 +88,29 @@ void asCConfigGroup::RefConfigGroup(asCConfigGroup *group) group->AddRef(); } +void asCConfigGroup::AddReferencesForFunc(asCScriptEngine *engine, asCScriptFunction *func) +{ + AddReferencesForType(engine, func->returnType.GetObjectType()); + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + AddReferencesForType(engine, func->parameterTypes[n].GetObjectType()); +} + +void asCConfigGroup::AddReferencesForType(asCScriptEngine *engine, asCObjectType *type) +{ + if( type == 0 ) return; + + // Keep reference to other groups + RefConfigGroup(engine->FindConfigGroupForObjectType(type)); + + // Keep track of which generated template instances the config group uses + if( type->flags & asOBJ_TEMPLATE && engine->generatedTemplateTypes.Exists(type) && !generatedTemplateInstances.Exists(type) ) + generatedTemplateInstances.PushLast(type); +} + bool asCConfigGroup::HasLiveObjects() { for( asUINT n = 0; n < objTypes.GetLength(); n++ ) - if( objTypes[n]->GetRefCount() != 0 ) + if( objTypes[n]->externalRefCount.get() != 0 ) return true; return false; @@ -123,7 +142,7 @@ void asCConfigGroup::RemoveConfiguration(asCScriptEngine *engine, bool notUsed) int index = engine->registeredGlobalFuncs.GetIndex(scriptFunctions[n]); if( index >= 0 ) engine->registeredGlobalFuncs.Erase(index); - scriptFunctions[n]->Release(); + scriptFunctions[n]->ReleaseInternal(); if( engine->stringFactory == scriptFunctions[n] ) engine->stringFactory = 0; } @@ -141,27 +160,24 @@ void asCConfigGroup::RemoveConfiguration(asCScriptEngine *engine, bool notUsed) for( n = 0; n < funcDefs.GetLength(); n++ ) { engine->registeredFuncDefs.RemoveValue(funcDefs[n]); - funcDefs[n]->Release(); + funcDefs[n]->ReleaseInternal(); + engine->RemoveFuncdef(funcDefs[n]); + funcDefs[n]->ReleaseInternal(); } funcDefs.SetLength(0); - engine->ClearUnusedTypes(); - // Remove object types (skip this if it is possible other groups are still using the types) if( !notUsed ) { - for( n = 0; n < objTypes.GetLength(); n++ ) + for( n = asUINT(objTypes.GetLength()); n-- > 0; ) { asCObjectType *t = objTypes[n]; asSMapNode *cursor; if( engine->allRegisteredTypes.MoveTo(&cursor, asSNameSpaceNamePair(t->nameSpace, t->name)) && cursor->value == t ) { -#ifdef AS_DEBUG - ValidateNoUsage(engine, t); -#endif - engine->allRegisteredTypes.Erase(cursor); + if( engine->defaultArrayObjectType == t ) engine->defaultArrayObjectType = 0; @@ -174,21 +190,17 @@ void asCConfigGroup::RemoveConfiguration(asCScriptEngine *engine, bool notUsed) else engine->registeredObjTypes.RemoveValue(t); - asDELETE(t, asCObjectType); + t->DestroyInternal(); + t->ReleaseInternal(); } else { int idx = engine->templateInstanceTypes.IndexOf(t); if( idx >= 0 ) { -#ifdef AS_DEBUG - ValidateNoUsage(engine, t); -#endif - engine->templateInstanceTypes.RemoveIndexUnordered(idx); - t->templateSubTypes.SetLength(0); - - asDELETE(t, asCObjectType); + t->DestroyInternal(); + t->ReleaseInternal(); } } } @@ -201,57 +213,4 @@ void asCConfigGroup::RemoveConfiguration(asCScriptEngine *engine, bool notUsed) referencedConfigGroups.SetLength(0); } -#ifdef AS_DEBUG -void asCConfigGroup::ValidateNoUsage(asCScriptEngine *engine, asCObjectType *type) -{ - for( asUINT n = 0; n < engine->scriptFunctions.GetLength(); n++ ) - { - asCScriptFunction *func = engine->scriptFunctions[n]; - if( func == 0 ) continue; - - // Ignore factory, list factory, and members - if( func->name == "_beh_3_" || func->name == "_beh_4_" || func->objectType == type ) - continue; - - // Ignore function definitions too, as they aren't released until the engine is destroyed - if( func->funcType == asFUNC_FUNCDEF ) - continue; - - // Ignore functions whose object type has already reached refCount 0 as they are to be removed - if( func->objectType && func->objectType->GetRefCount() == 0 ) - continue; - - if( func->returnType.GetObjectType() == type ) - { - asCString msg; - // We can only use the function name here, because the types used by the function may have been deleted already - msg.Format(TXT_TYPE_s_IS_STILL_USED_BY_FUNC_s, type->name.AddressOf(), func->GetDeclaration()); - engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf()); - } - else - { - for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) - { - if( func->parameterTypes[p].GetObjectType() == type ) - { - asCString msg; - // We can only use the function name here, because the types used by the function may have been deleted already - msg.Format(TXT_TYPE_s_IS_STILL_USED_BY_FUNC_s, type->name.AddressOf(), func->GetDeclaration()); - engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf()); - break; - } - } - } - } - - // TODO: Check also usage of the type in global variables - - // TODO: Check also usage of the type in local variables in script functions - - // TODO: Check also usage of the type as members of classes - - // TODO: Check also usage of the type as sub types in other types -} -#endif - END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_configgroup.h b/lib/angelscript/source/as_configgroup.h index 4e8c6abce..ccf785e6c 100644 --- a/lib/angelscript/source/as_configgroup.h +++ b/lib/angelscript/source/as_configgroup.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -63,9 +63,8 @@ public: bool HasLiveObjects(); void RemoveConfiguration(asCScriptEngine *engine, bool notUsed = false); -#ifdef AS_DEBUG - void ValidateNoUsage(asCScriptEngine *engine, asCObjectType *type); -#endif + void AddReferencesForFunc(asCScriptEngine *engine, asCScriptFunction *func); + void AddReferencesForType(asCScriptEngine *engine, asCObjectType *type); asCString groupName; int refCount; @@ -75,6 +74,10 @@ public: asCArray globalProps; asCArray referencedConfigGroups; asCArray funcDefs; + + // This array holds the generated template instances that are used + // by the config group as part of function signature or property + asCArray generatedTemplateInstances; }; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_context.cpp b/lib/angelscript/source/as_context.cpp index 25a1e376c..489877a0f 100644 --- a/lib/angelscript/source/as_context.cpp +++ b/lib/angelscript/source/as_context.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -61,7 +61,6 @@ const int RESERVE_STACK = 2*AS_PTR_SIZE; // For each script function call we push 9 PTRs on the call stack const int CALLSTACK_FRAME_SIZE = 9; - #if defined(AS_DEBUG) class asCDebugStats @@ -254,15 +253,27 @@ void asCContext::DetachEngine() { if( m_stackBlocks[n] ) { +#ifndef WIP_16BYTE_ALIGN asDELETEARRAY(m_stackBlocks[n]); +#else + asDELETEARRAYALIGNED(m_stackBlocks[n]); +#endif } } m_stackBlocks.SetLength(0); m_stackBlockSize = 0; // Clean the user data - if( m_userData && m_engine->cleanContextFunc ) - m_engine->cleanContextFunc(this); + for( asUINT n = 0; n < m_userData.GetLength(); n += 2 ) + { + if( m_userData[n+1] ) + { + for( asUINT c = 0; c < m_engine->cleanContextFuncs.GetLength(); c++ ) + if( m_engine->cleanContextFuncs[c].type == m_userData[n] ) + m_engine->cleanContextFuncs[c].cleanFunc(this); + } + } + m_userData.SetLength(0); // Clear engine pointer if( m_holdEngineRef ) @@ -277,17 +288,55 @@ asIScriptEngine *asCContext::GetEngine() const } // interface -void *asCContext::SetUserData(void *data) +void *asCContext::SetUserData(void *data, asPWORD type) { - void *oldData = m_userData; - m_userData = data; - return oldData; + // As a thread might add a new new user data at the same time as another + // it is necessary to protect both read and write access to the userData member + ACQUIREEXCLUSIVE(m_engine->engineRWLock); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < m_userData.GetLength(); n += 2 ) + { + if( m_userData[n] == type ) + { + void *oldData = reinterpret_cast(m_userData[n+1]); + m_userData[n+1] = reinterpret_cast(data); + + RELEASEEXCLUSIVE(m_engine->engineRWLock); + + return oldData; + } + } + + m_userData.PushLast(type); + m_userData.PushLast(reinterpret_cast(data)); + + RELEASEEXCLUSIVE(m_engine->engineRWLock); + + return 0; } // interface -void *asCContext::GetUserData() const +void *asCContext::GetUserData(asPWORD type) const { - return m_userData; + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + ACQUIRESHARED(m_engine->engineRWLock); + + for( asUINT n = 0; n < m_userData.GetLength(); n += 2 ) + { + if( m_userData[n] == type ) + { + RELEASESHARED(m_engine->engineRWLock); + return reinterpret_cast(m_userData[n+1]); + } + } + + RELEASESHARED(m_engine->engineRWLock); + + return 0; } // interface @@ -322,6 +371,16 @@ int asCContext::Prepare(asIScriptFunction *func) // Release the returned object (if any) CleanReturnObject(); + // Release the object if it is a script object + if( m_initialFunction && m_initialFunction->objectType && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) ) + { + asCScriptObject *obj = *(asCScriptObject**)&m_regs.stackFramePointer[0]; + if( obj ) + obj->Release(); + + *(asPWORD*)&m_regs.stackFramePointer[0] = 0; + } + if( m_initialFunction && m_initialFunction == func ) { // If the same function is executed again, we can skip a lot of the setup @@ -339,7 +398,7 @@ int asCContext::Prepare(asIScriptFunction *func) asASSERT( m_engine ); // Make sure the function is from the same engine as the context to avoid mixups - if (m_engine != func->GetEngine()) + if( m_engine != func->GetEngine() ) { asCString str; str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, "Prepare", func->GetDeclaration(true, true), asINVALID_ARG); @@ -437,6 +496,14 @@ int asCContext::Unprepare() // Release the returned object (if any) CleanReturnObject(); + // Release the object if it is a script object + if( m_initialFunction && m_initialFunction->objectType && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) ) + { + asCScriptObject *obj = *(asCScriptObject**)&m_regs.stackFramePointer[0]; + if( obj ) + obj->Release(); + } + // Release the initial function if( m_initialFunction ) { @@ -624,8 +691,15 @@ int asCContext::SetObject(void *obj) return asERROR; } + asASSERT( *(asPWORD*)&m_regs.stackFramePointer[0] == 0 ); + *(asPWORD*)&m_regs.stackFramePointer[0] = (asPWORD)obj; + // TODO: This should be optional by having a flag where the application can chose whether it should be done or not + // The flag could be named something like takeOwnership and have default value of true + if( obj && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) ) + reinterpret_cast(obj)->AddRef(); + return 0; } @@ -977,6 +1051,44 @@ int asCContext::SetArgObject(asUINT arg, void *obj) return 0; } +int asCContext::SetArgVarType(asUINT arg, void *ptr, int typeId) +{ + if( m_status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &m_initialFunction->parameterTypes[arg]; + if( dt->GetTokenType() != ttQuestion ) + { + m_status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( m_initialFunction->objectType ) + offset += AS_PTR_SIZE; + + // If function returns object by value an extra pointer is pushed on the stack + if( m_returnValueSize ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the typeId and pointer + *(asPWORD*)(&m_regs.stackFramePointer[offset]) = (asPWORD)ptr; + offset += AS_PTR_SIZE; + *(int*)(&m_regs.stackFramePointer[offset]) = typeId; + + return 0; +} // TODO: Instead of GetAddressOfArg, maybe we need a SetArgValue(int arg, void *value, bool takeOwnership) instead. @@ -1119,6 +1231,14 @@ int asCContext::Execute() SetInternalException(TXT_NULL_POINTER_ACCESS); } } + else if( m_currentFunction->funcType == asFUNC_IMPORTED ) + { + int funcId = m_engine->importedFunctions[m_currentFunction->id & ~FUNC_IMPORTED]->boundFunctionId; + if( funcId > 0 ) + m_currentFunction = m_engine->scriptFunctions[funcId]; + else + SetInternalException(TXT_UNBOUND_FUNCTION); + } if( m_currentFunction->funcType == asFUNC_SCRIPT ) { @@ -1132,7 +1252,7 @@ int asCContext::Execute() // The current function is an application registered function // Call the function directly - CallSystemFunction(m_currentFunction->id, this, 0); + CallSystemFunction(m_currentFunction->id, this); // Was the call successful? if( m_status == asEXECUTION_ACTIVE ) @@ -1174,13 +1294,12 @@ int asCContext::Execute() if( gcPosObjects > gcPreObjects ) { // Execute as many steps as there were new objects created - while( gcPosObjects-- > gcPreObjects ) - m_engine->GarbageCollect(asGC_ONE_STEP | asGC_DESTROY_GARBAGE | asGC_DETECT_GARBAGE); + m_engine->GarbageCollect(asGC_ONE_STEP | asGC_DESTROY_GARBAGE | asGC_DETECT_GARBAGE, gcPosObjects - gcPreObjects); } else if( gcPosObjects > 0 ) { // Execute at least one step, even if no new objects were created - m_engine->GarbageCollect(asGC_ONE_STEP | asGC_DESTROY_GARBAGE | asGC_DETECT_GARBAGE); + m_engine->GarbageCollect(asGC_ONE_STEP | asGC_DESTROY_GARBAGE | asGC_DETECT_GARBAGE, 1); } } @@ -1430,22 +1549,46 @@ int asCContext::GetLineNumber(asUINT stackLevel, int *column, const char **secti // internal bool asCContext::ReserveStackSpace(asUINT size) { +#ifdef WIP_16BYTE_ALIGN + // Pad size to a multiple of MAX_TYPE_ALIGNMENT. + const asUINT remainder = size % MAX_TYPE_ALIGNMENT; + if(remainder != 0) + { + size = size + (MAX_TYPE_ALIGNMENT - (size % MAX_TYPE_ALIGNMENT)); + } +#endif + // Make sure the first stack block is allocated if( m_stackBlocks.GetLength() == 0 ) { m_stackBlockSize = m_engine->initialContextStackSize; asASSERT( m_stackBlockSize > 0 ); +#ifndef WIP_16BYTE_ALIGN asDWORD *stack = asNEWARRAY(asDWORD,m_stackBlockSize); +#else + asDWORD *stack = asNEWARRAYALIGNED(asDWORD, m_stackBlockSize, MAX_TYPE_ALIGNMENT); +#endif if( stack == 0 ) { // Out of memory return false; } +#ifdef WIP_16BYTE_ALIGN + asASSERT( isAligned(stack, MAX_TYPE_ALIGNMENT) ); +#endif + m_stackBlocks.PushLast(stack); m_stackIndex = 0; m_regs.stackPointer = m_stackBlocks[0] + m_stackBlockSize; + +#ifdef WIP_16BYTE_ALIGN + // Align the stack pointer. This is necessary as the m_stackBlockSize is not necessarily evenly divisable with the max alignment + ((asPWORD&)m_regs.stackPointer) &= ~(MAX_TYPE_ALIGNMENT-1); + + asASSERT( isAligned(m_regs.stackPointer, MAX_TYPE_ALIGNMENT) ); +#endif } // Check if there is enough space on the current stack block, otherwise move @@ -1472,7 +1615,11 @@ bool asCContext::ReserveStackSpace(asUINT size) if( m_stackBlocks.GetLength() == m_stackIndex ) { // Allocate the new stack block, with twice the size of the previous - asDWORD *stack = asNEWARRAY(asDWORD,(m_stackBlockSize << m_stackIndex)); +#ifndef WIP_16BYTE_ALIGN + asDWORD *stack = asNEWARRAY(asDWORD, (m_stackBlockSize << m_stackIndex)); +#else + asDWORD *stack = asNEWARRAYALIGNED(asDWORD, (m_stackBlockSize << m_stackIndex), MAX_TYPE_ALIGNMENT); +#endif if( stack == 0 ) { // Out of memory @@ -1484,6 +1631,11 @@ bool asCContext::ReserveStackSpace(asUINT size) SetInternalException(TXT_STACK_OVERFLOW); return false; } + +#ifdef WIP_16BYTE_ALIGN + asASSERT( isAligned(stack, MAX_TYPE_ALIGNMENT) ); +#endif + m_stackBlocks.PushLast(stack); } @@ -1494,6 +1646,13 @@ bool asCContext::ReserveStackSpace(asUINT size) m_currentFunction->GetSpaceNeededForArguments() - (m_currentFunction->objectType ? AS_PTR_SIZE : 0) - (m_currentFunction->DoesReturnOnStack() ? AS_PTR_SIZE : 0); + +#ifdef WIP_16BYTE_ALIGN + // Align the stack pointer + (asPWORD&)m_regs.stackPointer &= ~(MAX_TYPE_ALIGNMENT-1); + + asASSERT( isAligned(m_regs.stackPointer, MAX_TYPE_ALIGNMENT) ); +#endif } return true; @@ -1512,19 +1671,6 @@ void asCContext::CallScriptFunction(asCScriptFunction *func) m_currentFunction = func; m_regs.programPointer = m_currentFunction->scriptData->byteCode.AddressOf(); - // Make sure there is space on the stack to execute the function - asDWORD *oldStackPointer = m_regs.stackPointer; - if( !ReserveStackSpace(func->scriptData->stackNeeded) ) - return; - - // If a new stack block was allocated then we'll need to move - // over the function arguments to the new block - if( m_regs.stackPointer != oldStackPointer ) - { - int numDwords = func->GetSpaceNeededForArguments() + (func->objectType ? AS_PTR_SIZE : 0) + (func->DoesReturnOnStack() ? AS_PTR_SIZE : 0); - memcpy(m_regs.stackPointer, oldStackPointer, sizeof(asDWORD)*numDwords); - } - PrepareScriptFunction(); } @@ -1532,6 +1678,21 @@ void asCContext::PrepareScriptFunction() { asASSERT( m_currentFunction->scriptData ); + // Make sure there is space on the stack to execute the function + asDWORD *oldStackPointer = m_regs.stackPointer; + if( !ReserveStackSpace(m_currentFunction->scriptData->stackNeeded) ) + return; + + // If a new stack block was allocated then we'll need to move + // over the function arguments to the new block. + if( m_regs.stackPointer != oldStackPointer ) + { + int numDwords = m_currentFunction->GetSpaceNeededForArguments() + + (m_currentFunction->objectType ? AS_PTR_SIZE : 0) + + (m_currentFunction->DoesReturnOnStack() ? AS_PTR_SIZE : 0); + memcpy(m_regs.stackPointer, oldStackPointer, sizeof(asDWORD)*numDwords); + } + // Update framepointer m_regs.stackFramePointer = m_regs.stackPointer; @@ -2329,7 +2490,7 @@ void asCContext::ExecuteNext() m_regs.stackPointer = l_sp; m_regs.stackFramePointer = l_fp; - l_sp += CallSystemFunction(i, this, 0); + l_sp += CallSystemFunction(i, this); // Update the program position after the call so that line number is correct l_bc += 2; @@ -2361,9 +2522,9 @@ void asCContext::ExecuteNext() case asBC_CALLBND: { + // TODO: Clean-up: This code is very similar to asBC_CallPtr. Create a shared method for them // Get the function ID from the stack int i = asBC_INTARG(l_bc); - l_bc += 2; asASSERT( i >= 0 ); asASSERT( i & FUNC_IMPORTED ); @@ -2376,6 +2537,9 @@ void asCContext::ExecuteNext() int funcId = m_engine->importedFunctions[i & ~FUNC_IMPORTED]->boundFunctionId; if( funcId == -1 ) { + // Need to update the program pointer for the exception handler + m_regs.programPointer += 2; + // Tell the exception handler to clean up the arguments to this function m_needToCleanupArgs = true; SetInternalException(TXT_UNBOUND_FUNCTION); @@ -2384,8 +2548,46 @@ void asCContext::ExecuteNext() else { asCScriptFunction *func = m_engine->GetScriptFunction(funcId); + if( func->funcType == asFUNC_SCRIPT ) + { + m_regs.programPointer += 2; + CallScriptFunction(func); + } + else if( func->funcType == asFUNC_DELEGATE ) + { + // Push the object pointer on the stack. There is always a reserved space for this so + // we don't don't need to worry about overflowing the allocated memory buffer + asASSERT( m_regs.stackPointer - AS_PTR_SIZE >= m_stackBlocks[m_stackIndex] ); + m_regs.stackPointer -= AS_PTR_SIZE; + *(asPWORD*)m_regs.stackPointer = asPWORD(func->objForDelegate); - CallScriptFunction(func); + // Call the delegated method + if( func->funcForDelegate->funcType == asFUNC_SYSTEM ) + { + m_regs.stackPointer += CallSystemFunction(func->funcForDelegate->id, this); + + // Update program position after the call so the line number + // is correct in case the system function queries it + m_regs.programPointer += 2; + } + else + { + m_regs.programPointer += 2; + + // TODO: run-time optimize: The true method could be figured out when creating the delegate + CallInterfaceMethod(func->funcForDelegate); + } + } + else + { + asASSERT( func->funcType == asFUNC_SYSTEM ); + + m_regs.stackPointer += CallSystemFunction(func->id, this); + + // Update program position after the call so the line number + // is correct in case the system function queries it + m_regs.programPointer += 2; + } } // Extract the values from the context again @@ -2476,13 +2678,17 @@ void asCContext::ExecuteNext() if( func ) { + // Push the object pointer on the stack (it will be popped by the function) + l_sp -= AS_PTR_SIZE; + *(asPWORD*)l_sp = (asPWORD)mem; + // Need to move the values back to the context as the called functions // may use the debug interface to inspect the registers m_regs.programPointer = l_bc; m_regs.stackPointer = l_sp; m_regs.stackFramePointer = l_fp; - l_sp += CallSystemFunction(func, this, mem); + l_sp += CallSystemFunction(func, this); } // Pop the variable address from the stack @@ -3352,7 +3558,7 @@ void asCContext::ExecuteNext() break; case asBC_u64TOf: -#if _MSC_VER <= 1200 // MSVC6 +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC6 { // MSVC6 doesn't permit UINT64 to double asINT64 v = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)); @@ -3373,7 +3579,7 @@ void asCContext::ExecuteNext() break; case asBC_u64TOd: -#if _MSC_VER <= 1200 // MSVC6 +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC6 { // MSVC6 doesn't permit UINT64 to double asINT64 v = *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)); @@ -3655,7 +3861,7 @@ void asCContext::ExecuteNext() // Call the delegated method if( func->funcForDelegate->funcType == asFUNC_SYSTEM ) { - m_regs.stackPointer += CallSystemFunction(func->funcForDelegate->id, this, 0); + m_regs.stackPointer += CallSystemFunction(func->funcForDelegate->id, this); // Update program position after the call so the line number // is correct in case the system function queries it @@ -3673,7 +3879,7 @@ void asCContext::ExecuteNext() { asASSERT( func->funcType == asFUNC_SYSTEM ); - m_regs.stackPointer += CallSystemFunction(func->id, this, 0); + m_regs.stackPointer += CallSystemFunction(func->id, this); // Update program position after the call so the line number // is correct in case the system function queries it @@ -3906,7 +4112,11 @@ void asCContext::ExecuteNext() asUINT size = asBC_DWORDARG(l_bc); asBYTE **var = (asBYTE**)(l_fp - asBC_SWORDARG0(l_bc)); +#ifndef WIP_16BYTE_ALIGN *var = asNEWARRAY(asBYTE, size); +#else + *var = asNEWARRAYALIGNED(asBYTE, size, MAX_TYPE_ALIGNMENT); +#endif // Clear the buffer for the pointers that will be placed in it memset(*var, 0, size); @@ -4622,23 +4832,6 @@ void asCContext::CleanStackFrame() } } } - - // If the object is a script declared object, then we must release it - // as the compiler adds a reference at the entry of the function. Make sure - // the function has actually been entered - if( m_currentFunction->objectType && m_regs.programPointer != m_currentFunction->scriptData->byteCode.AddressOf() ) - { - // Methods returning a reference or constructors don't add a reference - if( !m_currentFunction->returnType.IsReference() && m_currentFunction->name != m_currentFunction->objectType->name ) - { - asSTypeBehaviour *beh = &m_currentFunction->objectType->beh; - if( beh->release && *(asPWORD*)&m_regs.stackFramePointer[0] != 0 ) - { - m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[0], beh->release); - *(asPWORD*)&m_regs.stackFramePointer[0] = 0; - } - } - } } else m_isStackMemoryNotAllocated = false; @@ -4729,13 +4922,14 @@ asEContextState asCContext::GetState() const // interface int asCContext::SetLineCallback(asSFuncPtr callback, void *obj, int callConv) { - m_lineCallback = true; - m_regs.doProcessSuspend = true; + // First turn off the line callback to avoid a second thread + // attempting to call it while the new one is still being set + m_lineCallback = false; + m_lineCallbackObj = obj; bool isObj = false; - if( (unsigned)callConv == asCALL_GENERIC ) + if( (unsigned)callConv == asCALL_GENERIC || (unsigned)callConv == asCALL_THISCALL_OBJFIRST || (unsigned)callConv == asCALL_THISCALL_OBJLAST ) { - m_lineCallback = false; m_regs.doProcessSuspend = m_doSuspend; return asNOT_SUPPORTED; } @@ -4744,15 +4938,18 @@ int asCContext::SetLineCallback(asSFuncPtr callback, void *obj, int callConv) isObj = true; if( obj == 0 ) { - m_lineCallback = false; m_regs.doProcessSuspend = m_doSuspend; return asINVALID_ARG; } } int r = DetectCallingConvention(isObj, callback, callConv, 0, &m_lineCallbackFunc); - if( r < 0 ) m_lineCallback = false; + + // Turn on the line callback after setting both the function pointer and object pointer + if( r >= 0 ) m_lineCallback = true; + // The BC_SUSPEND instruction should be processed if either line + // callback is set or if the application has requested a suspension m_regs.doProcessSuspend = m_doSuspend || m_lineCallback; return r; @@ -4772,7 +4969,7 @@ int asCContext::SetExceptionCallback(asSFuncPtr callback, void *obj, int callCon m_exceptionCallback = true; m_exceptionCallbackObj = obj; bool isObj = false; - if( (unsigned)callConv == asCALL_GENERIC ) + if( (unsigned)callConv == asCALL_GENERIC || (unsigned)callConv == asCALL_THISCALL_OBJFIRST || (unsigned)callConv == asCALL_THISCALL_OBJLAST ) return asNOT_SUPPORTED; if( (unsigned)callConv >= asCALL_THISCALL ) { @@ -4809,91 +5006,79 @@ void asCContext::ClearExceptionCallback() m_exceptionCallback = false; } -int asCContext::CallGeneric(int id, void *objectPointer) +int asCContext::CallGeneric(asCScriptFunction *descr) { - asCScriptFunction *sysFunction = m_engine->scriptFunctions[id]; - asSSystemFunctionInterface *sysFunc = sysFunction->sysFuncIntf; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; void (*func)(asIScriptGeneric*) = (void (*)(asIScriptGeneric*))sysFunc->func; int popSize = sysFunc->paramSize; asDWORD *args = m_regs.stackPointer; // Verify the object pointer if it is a class method void *currentObject = 0; + asASSERT( sysFunc->callConv == ICC_GENERIC_FUNC || sysFunc->callConv == ICC_GENERIC_METHOD ); if( sysFunc->callConv == ICC_GENERIC_METHOD ) { - if( objectPointer ) + // The object pointer should be popped from the context stack + popSize += AS_PTR_SIZE; + + // Check for null pointer + currentObject = (void*)*(asPWORD*)(args); + if( currentObject == 0 ) { - currentObject = objectPointer; - - // Don't increase the reference of this pointer - // since it will not have been constructed yet + SetInternalException(TXT_NULL_POINTER_ACCESS); + return 0; } - else - { - // The object pointer should be popped from the context stack - popSize += AS_PTR_SIZE; - // Check for null pointer - currentObject = (void*)*(asPWORD*)(args); - if( currentObject == 0 ) - { - SetInternalException(TXT_NULL_POINTER_ACCESS); - return 0; - } + asASSERT( sysFunc->baseOffset == 0 ); - // Add the base offset for multiple inheritance - currentObject = (void*)(asPWORD(currentObject) + sysFunc->baseOffset); - - // Skip object pointer - args += AS_PTR_SIZE; - } + // Skip object pointer + args += AS_PTR_SIZE; } - if( sysFunction->DoesReturnOnStack() ) + if( descr->DoesReturnOnStack() ) { // Skip the address where the return value will be stored args += AS_PTR_SIZE; popSize += AS_PTR_SIZE; } - asCGeneric gen(m_engine, sysFunction, currentObject, args); + asCGeneric gen(m_engine, descr, currentObject, args); - m_callingSystemFunction = sysFunction; + m_callingSystemFunction = descr; func(&gen); m_callingSystemFunction = 0; m_regs.valueRegister = gen.returnVal; m_regs.objectRegister = gen.objectRegister; - m_regs.objectType = sysFunction->returnType.GetObjectType(); + m_regs.objectType = descr->returnType.GetObjectType(); - // Clean up function parameters - int offset = 0; - for( asUINT n = 0; n < sysFunction->parameterTypes.GetLength(); n++ ) + // Clean up arguments + const asUINT cleanCount = sysFunc->cleanArgs.GetLength(); + if( cleanCount ) { - if( sysFunction->parameterTypes[n].IsObject() && !sysFunction->parameterTypes[n].IsReference() ) + asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf(); + for( asUINT n = 0; n < cleanCount; n++, clean++ ) { - void *obj = *(void**)&args[offset]; - if( obj ) + void **addr = (void**)&args[clean->off]; + if( clean->op == 0 ) { - // Release the object - asSTypeBehaviour *beh = &sysFunction->parameterTypes[n].GetObjectType()->beh; - if( sysFunction->parameterTypes[n].GetObjectType()->flags & asOBJ_REF ) + if( *addr != 0 ) { - asASSERT( (sysFunction->parameterTypes[n].GetObjectType()->flags & asOBJ_NOCOUNT) || beh->release ); - if( beh->release ) - m_engine->CallObjectMethod(obj, beh->release); - } - else - { - // Call the destructor then free the memory - if( beh->destruct ) - m_engine->CallObjectMethod(obj, beh->destruct); - - m_engine->CallFree(obj); + m_engine->CallObjectMethod(*addr, clean->ot->beh.release); + *addr = 0; } } + else + { + asASSERT( clean->op == 1 || clean->op == 2 ); + asASSERT( *addr ); + + if( clean->op == 2 ) + m_engine->CallObjectMethod(*addr, clean->ot->beh.destruct); + + m_engine->CallFree(*addr); + } } - offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); } // Return how much should be popped from the stack diff --git a/lib/angelscript/source/as_context.h b/lib/angelscript/source/as_context.h index 8b29227ad..8396067e1 100644 --- a/lib/angelscript/source/as_context.h +++ b/lib/angelscript/source/as_context.h @@ -76,14 +76,15 @@ public: int SetObject(void *obj); // Arguments - int SetArgByte(asUINT arg, asBYTE value); - int SetArgWord(asUINT arg, asWORD value); - int SetArgDWord(asUINT arg, asDWORD value); - int SetArgQWord(asUINT arg, asQWORD value); - int SetArgFloat(asUINT arg, float value); - int SetArgDouble(asUINT arg, double value); - int SetArgAddress(asUINT arg, void *addr); - int SetArgObject(asUINT arg, void *obj); + int SetArgByte(asUINT arg, asBYTE value); + int SetArgWord(asUINT arg, asWORD value); + int SetArgDWord(asUINT arg, asDWORD value); + int SetArgQWord(asUINT arg, asQWORD value); + int SetArgFloat(asUINT arg, float value); + int SetArgDouble(asUINT arg, double value); + int SetArgAddress(asUINT arg, void *addr); + int SetArgObject(asUINT arg, void *obj); + int SetArgVarType(asUINT arg, void *ptr, int typeId); void *GetAddressOfArg(asUINT arg); // Return value @@ -122,8 +123,8 @@ public: asIScriptFunction *GetSystemFunction(); // User data - void *SetUserData(void *data); - void *GetUserData() const; + void *SetUserData(void *data, asPWORD type); + void *GetUserData(asPWORD type) const; public: // Internal public functions @@ -136,7 +137,7 @@ public: void CallLineCallback(); void CallExceptionCallback(); - int CallGeneric(int funcID, void *objectPointer); + int CallGeneric(asCScriptFunction *func); void DetachEngine(); @@ -204,7 +205,7 @@ public: asSSystemFunctionInterface m_exceptionCallbackFunc; void * m_exceptionCallbackObj; - void *m_userData; + asCArray m_userData; // Registers available to JIT compiler functions asSVMRegisters m_regs; diff --git a/lib/angelscript/source/as_datatype.cpp b/lib/angelscript/source/as_datatype.cpp index 783929b16..05dc7b3ce 100644 --- a/lib/angelscript/source/as_datatype.cpp +++ b/lib/angelscript/source/as_datatype.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -46,24 +46,28 @@ BEGIN_AS_NAMESPACE asCDataType::asCDataType() { - tokenType = ttUnrecognizedToken; - objectType = 0; - isReference = false; - isReadOnly = false; - isObjectHandle = false; - isConstHandle = false; - funcDef = 0; + tokenType = ttUnrecognizedToken; + objectType = 0; + isReference = false; + isReadOnly = false; + isAuto = false; + isObjectHandle = false; + isConstHandle = false; + funcDef = 0; + isHandleToAsHandleType = false; } asCDataType::asCDataType(const asCDataType &dt) { - tokenType = dt.tokenType; - objectType = dt.objectType; - isReference = dt.isReference; - isReadOnly = dt.isReadOnly; - isObjectHandle = dt.isObjectHandle; - isConstHandle = dt.isConstHandle; - funcDef = dt.funcDef; + tokenType = dt.tokenType; + objectType = dt.objectType; + isReference = dt.isReference; + isReadOnly = dt.isReadOnly; + isAuto = dt.isAuto; + isObjectHandle = dt.isObjectHandle; + isConstHandle = dt.isConstHandle; + funcDef = dt.funcDef; + isHandleToAsHandleType = dt.isHandleToAsHandleType; } asCDataType::~asCDataType() @@ -90,6 +94,17 @@ asCDataType asCDataType::CreateObject(asCObjectType *ot, bool isConst) return dt; } +asCDataType asCDataType::CreateAuto(bool isConst) +{ + asCDataType dt; + + dt.tokenType = ttIdentifier; + dt.isReadOnly = isConst; + dt.isAuto = true; + + return dt; +} + asCDataType asCDataType::CreateObjectHandle(asCObjectType *ot, bool isConst) { asCDataType dt; @@ -144,7 +159,7 @@ bool asCDataType::IsNullHandle() const return false; } -asCString asCDataType::Format(bool includeNamespace) const +asCString asCDataType::Format(asSNameSpace *currNs, bool includeNamespace) const { if( IsNullHandle() ) return ""; @@ -154,11 +169,13 @@ asCString asCDataType::Format(bool includeNamespace) const if( isReadOnly ) str = "const "; - if( includeNamespace ) + // If the type is not declared in the current namespace, then the namespace + // must always be informed to guarantee that the correct type is informed + if( includeNamespace || (objectType && objectType->nameSpace != currNs) || (funcDef && funcDef->nameSpace != currNs) ) { - if( objectType ) + if( objectType && objectType->nameSpace->name != "" ) str += objectType->nameSpace->name + "::"; - else if( funcDef ) + else if( funcDef && funcDef->nameSpace->name != "" ) str += funcDef->nameSpace->name + "::"; } @@ -169,7 +186,7 @@ asCString asCDataType::Format(bool includeNamespace) const else if( IsArrayType() && objectType && !objectType->engine->ep.expandDefaultArrayToTemplate ) { asASSERT( objectType->templateSubTypes.GetLength() == 1 ); - str += objectType->templateSubTypes[0].Format(includeNamespace); + str += objectType->templateSubTypes[0].Format(currNs, includeNamespace); str += "[]"; } else if( funcDef ) @@ -184,13 +201,20 @@ asCString asCDataType::Format(bool includeNamespace) const str += "<"; for( asUINT subtypeIndex = 0; subtypeIndex < objectType->templateSubTypes.GetLength(); subtypeIndex++ ) { - str += objectType->templateSubTypes[subtypeIndex].Format(includeNamespace); + str += objectType->templateSubTypes[subtypeIndex].Format(currNs, includeNamespace); if( subtypeIndex != objectType->templateSubTypes.GetLength()-1 ) str += ","; } str += ">"; } } + else if( isAuto ) + { + if( isObjectHandle ) + str += ""; + else + str += ""; + } else { str = ""; @@ -211,13 +235,15 @@ asCString asCDataType::Format(bool includeNamespace) const asCDataType &asCDataType::operator =(const asCDataType &dt) { - tokenType = dt.tokenType; - isReference = dt.isReference; - objectType = dt.objectType; - isReadOnly = dt.isReadOnly; - isObjectHandle = dt.isObjectHandle; - isConstHandle = dt.isConstHandle; - funcDef = dt.funcDef; + tokenType = dt.tokenType; + isReference = dt.isReference; + objectType = dt.objectType; + isReadOnly = dt.isReadOnly; + isObjectHandle = dt.isObjectHandle; + isConstHandle = dt.isConstHandle; + isAuto = dt.isAuto; + funcDef = dt.funcDef; + isHandleToAsHandleType = dt.isHandleToAsHandleType; return (asCDataType &)*this; } @@ -226,35 +252,46 @@ int asCDataType::MakeHandle(bool b, bool acceptHandleForScope) { if( !b ) { - isObjectHandle = b; + isObjectHandle = false; isConstHandle = false; + isHandleToAsHandleType = false; } - else if( b && !isObjectHandle ) + else { - // Only reference types are allowed to be handles, - // but not nohandle reference types, and not scoped references - // (except when returned from registered function) - // funcdefs are special reference types and support handles - // value types with asOBJ_ASHANDLE are treated as a handle - if( !funcDef && - (!objectType || - !((objectType->flags & asOBJ_REF) || (objectType->flags & asOBJ_TEMPLATE_SUBTYPE) || (objectType->flags & asOBJ_ASHANDLE)) || - (objectType->flags & asOBJ_NOHANDLE) || - ((objectType->flags & asOBJ_SCOPED) && !acceptHandleForScope)) ) - return -1; + if( isAuto ) + { + isObjectHandle = true; + } + else if( !isObjectHandle ) + { + // Only reference types are allowed to be handles, + // but not nohandle reference types, and not scoped references + // (except when returned from registered function) + // funcdefs are special reference types and support handles + // value types with asOBJ_ASHANDLE are treated as a handle + if( !funcDef && + (!objectType || + !((objectType->flags & asOBJ_REF) || (objectType->flags & asOBJ_TEMPLATE_SUBTYPE) || (objectType->flags & asOBJ_ASHANDLE)) || + (objectType->flags & asOBJ_NOHANDLE) || + ((objectType->flags & asOBJ_SCOPED) && !acceptHandleForScope)) ) + return -1; - isObjectHandle = b; - isConstHandle = false; + isObjectHandle = b; + isConstHandle = false; - // ASHANDLE supports being handle, but as it really is a value type it will not be marked as a handle - if( (objectType->flags & asOBJ_ASHANDLE) ) - isObjectHandle = false; + // ASHANDLE supports being handle, but as it really is a value type it will not be marked as a handle + if( (objectType->flags & asOBJ_ASHANDLE) ) + { + isObjectHandle = false; + isHandleToAsHandleType = true; + } + } } return 0; } -int asCDataType::MakeArray(asCScriptEngine *engine) +int asCDataType::MakeArray(asCScriptEngine *engine, asCModule *module) { if( engine->defaultArrayObjectType == 0 ) return asINVALID_TYPE; @@ -263,7 +300,7 @@ int asCDataType::MakeArray(asCScriptEngine *engine) isReadOnly = false; asCArray subTypes; subTypes.PushLast(*this); - asCObjectType *at = engine->GetTemplateInstanceType(engine->defaultArrayObjectType, subTypes); + asCObjectType *at = engine->GetTemplateInstanceType(engine->defaultArrayObjectType, subTypes, module); isReadOnly = tmpIsReadOnly; isObjectHandle = false; @@ -305,7 +342,7 @@ int asCDataType::MakeHandleToConst(bool b) bool asCDataType::SupportHandles() const { if( objectType && - (objectType->flags & asOBJ_REF) && + (objectType->flags & (asOBJ_REF | asOBJ_ASHANDLE)) && !(objectType->flags & asOBJ_NOHANDLE) && !isObjectHandle ) return true; @@ -313,19 +350,39 @@ bool asCDataType::SupportHandles() const return false; } -bool asCDataType::CanBeInstanciated() const +bool asCDataType::CanBeInstantiated() const { - if( GetSizeOnStackDWords() == 0 || - (IsObject() && - (objectType->flags & asOBJ_REF) && // It's a ref type and - ((objectType->flags & asOBJ_NOHANDLE) || // the ref type doesn't support handles or - (!IsObjectHandle() && // it's not a handle and - objectType->beh.factories.GetLength() == 0))) ) // the ref type cannot be instanciated + if( GetSizeOnStackDWords() == 0 ) // Void + return false; + + if( !IsObject() ) // Primitives + return true; + + if( IsObjectHandle() && !(objectType->flags & asOBJ_NOHANDLE) ) // Handles + return true; + + if( funcDef ) // Funcdefs can be instantiated as delegates + return true; + + if( (objectType->flags & asOBJ_REF) && objectType->beh.factories.GetLength() == 0 ) // ref types without factories + return false; + + if( (objectType->flags & asOBJ_ABSTRACT) && !IsObjectHandle() ) // Can't instantiate abstract classes return false; return true; } +bool asCDataType::IsAbstractClass() const +{ + return objectType && (objectType->flags & asOBJ_ABSTRACT) ? true : false; +} + +bool asCDataType::IsInterface() const +{ + return objectType && objectType->IsInterface(); +} + bool asCDataType::CanBeCopied() const { // All primitives can be copied @@ -334,8 +391,8 @@ bool asCDataType::CanBeCopied() const // Plain-old-data structures can always be copied if( objectType->flags & asOBJ_POD ) return true; - // It must be possible to instanciate the type - if( !CanBeInstanciated() ) return false; + // It must be possible to instantiate the type + if( !CanBeInstantiated() ) return false; // It must have a default constructor or factory if( objectType->beh.construct == 0 && @@ -361,6 +418,14 @@ bool asCDataType::IsHandleToConst() const return isReadOnly; } +bool asCDataType::IsObjectConst() const +{ + if( IsObjectHandle() ) + return IsHandleToConst(); + + return IsReadOnly(); +} + // TODO: 3.0.0: This should be removed bool asCDataType::IsArrayType() const { @@ -441,7 +506,7 @@ bool asCDataType::IsEqualExceptConst(const asCDataType &dt) const bool asCDataType::IsPrimitive() const { - // Enumerations are primitives + // Enumerations are primitives if( IsEnumType() ) return true; @@ -456,6 +521,16 @@ bool asCDataType::IsPrimitive() const return true; } +bool asCDataType::IsMathType() const +{ + if( tokenType == ttInt || tokenType == ttInt8 || tokenType == ttInt16 || tokenType == ttInt64 || + tokenType == ttUInt || tokenType == ttUInt8 || tokenType == ttUInt16 || tokenType == ttUInt64 || + tokenType == ttFloat || tokenType == ttDouble ) + return true; + + return false; +} + bool asCDataType::IsIntegerType() const { if( tokenType == ttInt || @@ -505,13 +580,14 @@ bool asCDataType::IsBooleanType() const bool asCDataType::IsObject() const { - // Enumerations are not objects, even though they are described with an objectType. - if( IsEnumType() ) + if( IsPrimitive() ) return false; - if( objectType ) return true; + // Null handle doesn't have an object type but should still be considered an object + if( objectType == 0 ) + return IsNullHandle(); - return false; + return true; } int asCDataType::GetSizeInMemoryBytes() const @@ -569,6 +645,19 @@ int asCDataType::GetSizeOnStackDWords() const return GetSizeInMemoryDWords() + size; } +#ifdef WIP_16BYTE_ALIGN +int asCDataType::GetAlignment() const +{ + if( objectType == NULL ) + { + // TODO: Small primitives should not be aligned to 4 byte boundaries + return 4; //Default alignment + } + + return objectType->alignment; +} +#endif + asSTypeBehaviour *asCDataType::GetBehaviour() const { return objectType ? &objectType->beh : 0; @@ -576,6 +665,9 @@ asSTypeBehaviour *asCDataType::GetBehaviour() const bool asCDataType::IsEnumType() const { + // Do a sanity check on the objectType, to verify that we aren't trying to access memory after it has been released + asASSERT( objectType == 0 || objectType->name.GetLength() < 100 ); + if( objectType && (objectType->flags & asOBJ_ENUM) ) return true; diff --git a/lib/angelscript/source/as_datatype.h b/lib/angelscript/source/as_datatype.h index c329683b4..b079e447c 100644 --- a/lib/angelscript/source/as_datatype.h +++ b/lib/angelscript/source/as_datatype.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -49,6 +49,8 @@ struct asSTypeBehaviour; class asCScriptEngine; class asCObjectType; class asCScriptFunction; +class asCModule; +struct asSNameSpace; // TODO: refactor: Reference should not be part of the datatype. This should be stored separately, e.g. in asCTypeInfo // MakeReference, MakeReadOnly, IsReference, IsReadOnly should be removed @@ -62,36 +64,45 @@ public: bool IsValid() const; - asCString Format(bool includeNamespace = false) const; + asCString Format(asSNameSpace *currNs, bool includeNamespace = false) const; static asCDataType CreatePrimitive(eTokenType tt, bool isConst); static asCDataType CreateObject(asCObjectType *ot, bool isConst); + static asCDataType CreateAuto(bool isConst); static asCDataType CreateObjectHandle(asCObjectType *ot, bool isConst); static asCDataType CreateFuncDef(asCScriptFunction *ot); static asCDataType CreateNullHandle(); int MakeHandle(bool b, bool acceptHandleForScope = false); - int MakeArray(asCScriptEngine *engine); + int MakeArray(asCScriptEngine *engine, asCModule *requestingModule); int MakeReference(bool b); int MakeReadOnly(bool b); int MakeHandleToConst(bool b); - bool IsTemplate() const; - bool IsScriptObject() const; - bool IsPrimitive() const; - bool IsObject() const; - bool IsReference() const {return isReference;} - bool IsReadOnly() const; - bool IsIntegerType() const; - bool IsUnsignedType() const; - bool IsFloatType() const; - bool IsDoubleType() const; - bool IsBooleanType() const; - bool IsObjectHandle() const {return isObjectHandle;} - bool IsHandleToConst() const; - bool IsArrayType() const; - bool IsEnumType() const; - bool IsAnyType() const {return tokenType == ttQuestion;} + bool IsTemplate() const; + bool IsScriptObject() const; + bool IsPrimitive() const; + bool IsMathType() const; + bool IsObject() const; + bool IsReference() const {return isReference;} + bool IsAuto() const {return isAuto;} + bool IsReadOnly() const; + bool IsIntegerType() const; + bool IsUnsignedType() const; + bool IsFloatType() const; + bool IsDoubleType() const; + bool IsBooleanType() const; + bool IsObjectHandle() const {return isObjectHandle;} + bool IsHandleToAuto() const {return isAuto && isObjectHandle;} + bool IsHandleToConst() const; + bool IsArrayType() const; + bool IsEnumType() const; + bool IsAnyType() const {return tokenType == ttQuestion;} + bool IsHandleToAsHandleType() const {return isHandleToAsHandleType;} + bool IsAbstractClass() const; + bool IsInterface() const; + + bool IsObjectConst() const; bool IsEqualExceptRef(const asCDataType &) const; bool IsEqualExceptRefAndConst(const asCDataType &) const; @@ -99,7 +110,7 @@ public: bool IsNullHandle() const; bool SupportHandles() const; - bool CanBeInstanciated() const; + bool CanBeInstantiated() const; bool CanBeCopied() const; bool operator ==(const asCDataType &) const; @@ -113,6 +124,9 @@ public: int GetSizeOnStackDWords() const; int GetSizeInMemoryBytes() const; int GetSizeInMemoryDWords() const; +#ifdef WIP_16BYTE_ALIGN + int GetAlignment() const; +#endif void SetTokenType(eTokenType tt) {tokenType = tt;} void SetObjectType(asCObjectType *obj) {objectType = obj;} @@ -135,7 +149,9 @@ protected: bool isReadOnly:1; bool isObjectHandle:1; bool isConstHandle:1; - char dummy:4; + bool isAuto:1; + bool isHandleToAsHandleType:1; // Used by the compiler to know how to initialize the object + char dummy:2; }; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_debug.h b/lib/angelscript/source/as_debug.h index 8d9ebad19..a86428d41 100644 --- a/lib/angelscript/source/as_debug.h +++ b/lib/angelscript/source/as_debug.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -38,12 +38,16 @@ #include "as_config.h" +#if defined(AS_DEBUG) + #ifndef AS_WII // The Wii SDK doesn't have these, we'll survive without AS_DEBUG #ifndef _WIN32_WCE // Neither does WinCE +#ifndef AS_PSVITA +// Possible on PSVita, but requires SDK access #if defined(__GNUC__) || defined( AS_MARMALADE ) @@ -256,10 +260,17 @@ END_AS_NAMESPACE - +#endif // AS_PSVITA #endif // _WIN32_WCE #endif // AS_WII +#else // !defined(AS_DEBUG) + +// Define it so nothing is done +#define TimeIt(x) + +#endif // !defined(AS_DEBUG) + #endif diff --git a/lib/angelscript/source/as_gc.cpp b/lib/angelscript/source/as_gc.cpp index 4c3edc94c..36f6230db 100644 --- a/lib/angelscript/source/as_gc.cpp +++ b/lib/angelscript/source/as_gc.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -156,7 +156,14 @@ int asCGarbageCollector::GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj, a return asSUCCESS; } -int asCGarbageCollector::GarbageCollect(asDWORD flags) +// TODO: Should have a flag to tell the garbage collector to automatically determine how many iterations are needed +// It should then gather statistics such as how many objects has been created since last run, and how many objects +// are destroyed per iteration, and how many objects are detected as cyclic garbage per iteration. +// It should try to reach a stable number of objects, i.e. so that on average the number of objects added to +// the garbage collector is the same as the number of objects destroyed. And it should try to minimize the number +// of iterations of detections that must be executed per cycle while still identifying the cyclic garbage +// These variables should also be available for inspection through the gcstatistics. +int asCGarbageCollector::GarbageCollect(asDWORD flags, asUINT iterations) { // If the GC is already processing in another thread, then don't enter here again if( TRYENTERCRITICALSECTION(gcCollecting) ) @@ -178,9 +185,8 @@ int asCGarbageCollector::GarbageCollect(asDWORD flags) // Reset the state if( doDetect ) { - // Move all objects to the old list, so we guarantee that all is detected - for( asUINT n = (asUINT)gcNewObjects.GetLength(); n-- > 0; ) - MoveObjectToOldList(n); + // Move all new objects to the old list, so we guarantee that all is detected + MoveAllObjectsToOldList(); detectState = clearCounters_init; } if( doDestroy ) @@ -189,7 +195,10 @@ int asCGarbageCollector::GarbageCollect(asDWORD flags) destroyOldState = destroyGarbage_init; } - unsigned int count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); + // The full cycle only works with the objects in the old list so that the + // set of objects scanned for garbage is fixed even if new objects are added + // by other threads in parallel. + unsigned int count = (unsigned int)(gcOldObjects.GetLength()); for(;;) { // Detect all garbage with cyclic references @@ -199,25 +208,16 @@ int asCGarbageCollector::GarbageCollect(asDWORD flags) // Now destroy all known garbage if( doDestroy ) { - while( DestroyNewGarbage() == 1 ) {} + if( !doDetect ) + while( DestroyNewGarbage() == 1 ) {} while( DestroyOldGarbage() == 1 ) {} } // Run another iteration if any garbage was destroyed - if( count != (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()) ) - count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); + if( count != (unsigned int)(gcOldObjects.GetLength()) ) + count = (unsigned int)(gcOldObjects.GetLength()); else - { - // Let the engine destroy the types that reached refCount 0 - // If none were destroyed, then leave the GC - // TODO: The asCObjectType should destroy its content when refCount reaches 0 - // since no-one is using them. The registered types should have their - // refcount increased by the config groups. Doing it like that will allow - // me to remove this call to ClearUnusedTypes() that the GC really - // shouldn't be calling. - if( engine->ClearUnusedTypes() == 0 ) - break; - } + break; } isProcessing = false; @@ -226,16 +226,19 @@ int asCGarbageCollector::GarbageCollect(asDWORD flags) } else { - // Destroy the garbage that we know of - if( doDestroy ) + while( iterations-- > 0 ) { - DestroyNewGarbage(); - DestroyOldGarbage(); - } + // Destroy the garbage that we know of + if( doDestroy ) + { + DestroyNewGarbage(); + DestroyOldGarbage(); + } - // Run another incremental step of the identification of cyclic references - if( doDetect ) - IdentifyGarbageWithCyclicRefs(); + // Run another incremental step of the identification of cyclic references + if( doDetect && gcOldObjects.GetLength() > 0 ) + IdentifyGarbageWithCyclicRefs(); + } } isProcessing = false; @@ -246,19 +249,33 @@ int asCGarbageCollector::GarbageCollect(asDWORD flags) return 1; } +// TODO: Additional statistics to gather +// +// - How many objects are added on average between each destroyed object +// - How many objects are added on average between each detected object +// - how many iterations are needed for each destroyed object +// - how many iterations are needed for each detected object +// +// The average must have a decay so that long running applications will not suffer +// from objects being created early on in the application and then never destroyed. +// +// This ought to be possible to accomplish by holding two buckets. +// Numbers will be accumulated in one bucket while the other is held fixed. +// When returning the average it should use a weighted average between the two buckets using the size as weight. +// When a bucket is filled up, the buckets are switched, and then new bucket is emptied to gather new statistics. void asCGarbageCollector::GetStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const { - // It's not necessary to protect this access, as - // it doesn't matter if another thread is currently - // appending a new object. + // It is not necessary to protect this with critical sections, however + // as it is not protected the variables can be filled in slightly different + // moments and might not match perfectly when inspected by the application + // afterwards. + if( currentSize ) *currentSize = (asUINT)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); if( totalDestroyed ) *totalDestroyed = numDestroyed; - asASSERT( numAdded == gcNewObjects.GetLength() + gcOldObjects.GetLength() + numDestroyed ); - if( totalDetected ) *totalDetected = numDetected; @@ -328,6 +345,16 @@ void asCGarbageCollector::MoveObjectToOldList(int idx) LEAVECRITICALSECTION(gcCritical); } +void asCGarbageCollector::MoveAllObjectsToOldList() +{ + // We need to protect this update with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + if( gcOldObjects.Concatenate(gcNewObjects) ) + gcNewObjects.SetLength(0); + LEAVECRITICALSECTION(gcCritical); +} + int asCGarbageCollector::DestroyNewGarbage() { // This function will only be called within the critical section gcCollecting @@ -900,8 +927,15 @@ asCGarbageCollector::asSMapNode_t *asCGarbageCollector::GetNode(void *obj, asSIn if( freeNodes.GetLength() ) node = freeNodes.PopLast(); else + { node = asNEW(asSMapNode_t); - + if( !node ) + { + // Out of memory + return 0; + } + } + node->Init(obj, it); return node; } diff --git a/lib/angelscript/source/as_gc.h b/lib/angelscript/source/as_gc.h index 9dc235b8f..3609ec42b 100644 --- a/lib/angelscript/source/as_gc.h +++ b/lib/angelscript/source/as_gc.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -56,7 +56,7 @@ public: asCGarbageCollector(); ~asCGarbageCollector(); - int GarbageCollect(asDWORD flags); + int GarbageCollect(asDWORD flags, asUINT iterations); void GetStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const; void GCEnumCallback(void *reference); int AddScriptObjectToGC(void *obj, asCObjectType *objType); @@ -104,6 +104,7 @@ protected: void RemoveNewObjectAtIdx(int idx); void RemoveOldObjectAtIdx(int idx); void MoveObjectToOldList(int idx); + void MoveAllObjectsToOldList(); // Holds all the objects known by the garbage collector asCArray gcNewObjects; diff --git a/lib/angelscript/source/as_globalproperty.cpp b/lib/angelscript/source/as_globalproperty.cpp index 210c97f0c..df2e51557 100644 --- a/lib/angelscript/source/as_globalproperty.cpp +++ b/lib/angelscript/source/as_globalproperty.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -50,31 +50,32 @@ asCGlobalProperty::asCGlobalProperty() asCGlobalProperty::~asCGlobalProperty() { +#ifndef WIP_16BYTE_ALIGNED if( memoryAllocated ) { asDELETEARRAY(memory); } +#else + if( memoryAllocated ) { asDELETEARRAYALIGNED(memory); } +#endif + if( initFunc ) - initFunc->Release(); + initFunc->ReleaseInternal(); } void asCGlobalProperty::AddRef() { - gcFlag = false; refCount.atomicInc(); } void asCGlobalProperty::Release() { - gcFlag = false; + if( refCount.atomicDec() == 0 ) + asDELETE(this, asCGlobalProperty); +} - // The property doesn't delete itself. The - // engine will do that at a later time - if( refCount.atomicDec() == 2 && initFunc ) +void asCGlobalProperty::DestroyInternal() +{ + if( initFunc ) { - // Since the initFunc holds references to the property, - // we'll release it when we reach refCount 2. This will - // break the circle and allow the engine to free the property - // without the need for the GC to attempt finding circular - // references. - initFunc->Release(); + initFunc->ReleaseInternal(); initFunc = 0; } } @@ -92,14 +93,19 @@ void asCGlobalProperty::AllocateMemory() { if( type.GetSizeOnStackDWords() > 2 ) { +#ifndef WIP_16BYTE_ALIGNED memory = asNEWARRAY(asDWORD, type.GetSizeOnStackDWords()); +#else + // TODO: Avoid aligned allocation if not needed to reduce the waste of memory for the alignment + memory = asNEWARRAYALIGNED(asDWORD, type.GetSizeOnStackDWords(), type.GetAlignment()); +#endif memoryAllocated = true; } } void asCGlobalProperty::SetRegisteredAddress(void *p) { - realAddress = p; + realAddress = p; if( type.IsObject() && !type.IsReference() && !type.IsObjectHandle() ) { // The global property is a pointer to a pointer @@ -114,60 +120,13 @@ void *asCGlobalProperty::GetRegisteredAddress() const return realAddress; } -int asCGlobalProperty::GetRefCount() -{ - return refCount.get(); -} - -void asCGlobalProperty::SetGCFlag() -{ - gcFlag = true; -} - -bool asCGlobalProperty::GetGCFlag() -{ - return gcFlag; -} - -void asCGlobalProperty::EnumReferences(asIScriptEngine *engine) -{ - engine->GCEnumCallback(initFunc); -} - -void asCGlobalProperty::ReleaseAllHandles(asIScriptEngine *) -{ - if( initFunc ) - { - initFunc->Release(); - initFunc = 0; - } -} - -void asCGlobalProperty::Orphan(asCModule *module) -{ - if( initFunc && initFunc->module == module ) - { - // The owning module is releasing the property, so we need to notify - // the GC in order to resolve any circular references that may exists - - // This will add the property - initFunc->engine->gc.AddScriptObjectToGC(this, &initFunc->engine->globalPropertyBehaviours); - - // This will add the function - initFunc->AddRef(); - initFunc->Orphan(module); - } - - Release(); -} - void asCGlobalProperty::SetInitFunc(asCScriptFunction *initFunc) { // This should only be done once asASSERT( this->initFunc == 0 ); this->initFunc = initFunc; - this->initFunc->AddRef(); + this->initFunc->AddRefInternal(); } asCScriptFunction *asCGlobalProperty::GetInitFunc() @@ -175,81 +134,4 @@ asCScriptFunction *asCGlobalProperty::GetInitFunc() return initFunc; } - -#ifdef AS_MAX_PORTABILITY - -static void GlobalProperty_AddRef_Generic(asIScriptGeneric *gen) -{ - asCGlobalProperty *self = (asCGlobalProperty*)gen->GetObject(); - self->AddRef(); -} - -static void GlobalProperty_Release_Generic(asIScriptGeneric *gen) -{ - asCGlobalProperty *self = (asCGlobalProperty*)gen->GetObject(); - self->Release(); -} - -static void GlobalProperty_GetRefCount_Generic(asIScriptGeneric *gen) -{ - asCGlobalProperty *self = (asCGlobalProperty*)gen->GetObject(); - *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); -} - -static void GlobalProperty_SetGCFlag_Generic(asIScriptGeneric *gen) -{ - asCGlobalProperty *self = (asCGlobalProperty*)gen->GetObject(); - self->SetGCFlag(); -} - -static void GlobalProperty_GetGCFlag_Generic(asIScriptGeneric *gen) -{ - asCGlobalProperty *self = (asCGlobalProperty*)gen->GetObject(); - *(bool*)gen->GetAddressOfReturnLocation() = self->GetGCFlag(); -} - -static void GlobalProperty_EnumReferences_Generic(asIScriptGeneric *gen) -{ - asCGlobalProperty *self = (asCGlobalProperty*)gen->GetObject(); - asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); - self->EnumReferences(engine); -} - -static void GlobalProperty_ReleaseAllHandles_Generic(asIScriptGeneric *gen) -{ - asCGlobalProperty *self = (asCGlobalProperty*)gen->GetObject(); - asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); - self->ReleaseAllHandles(engine); -} - -#endif - - -void asCGlobalProperty::RegisterGCBehaviours(asCScriptEngine *engine) -{ - // Register the gc behaviours for the global properties - int r = 0; - UNUSED_VAR(r); // It is only used in debug mode - engine->globalPropertyBehaviours.engine = engine; - engine->globalPropertyBehaviours.flags = asOBJ_REF | asOBJ_GC; - engine->globalPropertyBehaviours.name = "_builtin_globalprop_"; -#ifndef AS_MAX_PORTABILITY - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCGlobalProperty,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCGlobalProperty,Release), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCGlobalProperty,GetRefCount), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCGlobalProperty,SetGCFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCGlobalProperty,GetGCFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCGlobalProperty,EnumReferences), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCGlobalProperty,ReleaseAllHandles), asCALL_THISCALL, 0); asASSERT( r >= 0 ); -#else - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(GlobalProperty_AddRef_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(GlobalProperty_Release_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(GlobalProperty_GetRefCount_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(GlobalProperty_SetGCFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(GlobalProperty_GetGCFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(GlobalProperty_EnumReferences_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->globalPropertyBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(GlobalProperty_ReleaseAllHandles_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); -#endif -} - END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_memory.cpp b/lib/angelscript/source/as_memory.cpp index edc8c7fc5..13fd081f2 100644 --- a/lib/angelscript/source/as_memory.cpp +++ b/lib/angelscript/source/as_memory.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -49,6 +49,63 @@ BEGIN_AS_NAMESPACE +#ifdef WIP_16BYTE_ALIGN + +// TODO: Add support for 16byte aligned application types (e.g. __m128). The following is a list of things that needs to be implemented: +// +// ok - The script context must make sure to always allocate the local stack memory buffer on 16byte aligned boundaries (asCContext::ReserveStackSpace) +// ok - The engine must make sure to always allocate the memory for the script objects on 16byte aligned boundaries (asCScriptEngine::CallAlloc) +// ok - The application needs to inform a new flag when registering types that require 16byte alignment, e.g. asOBJ_APP_ALIGN16 (asCScriptEngine::RegisterObjectType) +// ok - The script object type must make sure to align member properties of these types correctly (asCObjectType::AddPropertyToClass) +// ok - Script global properties must allocate memory on 16byte boundaries if holding these types (asCGlobalProperty::AllocateMemory) +// TODO - The script compiler must make sure to allocate the local variables on 16byte boundaries (asCCompiler::AllocateVariable) +// TODO - The script compiler must add pad bytes on the stack for all function calls to guarantee that the stack position is 16byte aligned on entry in the called function (asCCompiler) +// TODO - The bytecode serializer must be capable of adjusting these pad bytes to guarantee platform independent saved bytecode. Remember that the registered type may not be 16byte aligned on all platforms (asCWriter & asCReader) +// TODO - The bytecode serializer must also be prepared to adjust the position of the local variables according to the need fro 16byte alignment (asCWriter & asCReader) +// TODO - The code for the native calling conventions must be adjusted for all platforms that should support 16byte aligned types (as_callfunc...) +// ok - When the context needs to grow the local stack memory it must copy the function arguments so that the stack entry position is 16byte aligned (asCContext::CallScriptFunction) +// TODO - When the context is prepared for a new call, it must set the initial stack position so the stack entry position is 16byte aligned (asCContext::Prepare) +// +// http://www.gamedev.net/topic/650555-alignment-requirements/ + + +// TODO: Allow user to register its own aligned memory routines +// Wrappers for aligned allocations +void *debugAlignedMalloc(size_t size, size_t align, const char *file, int line) +{ + void *mem = ((asALLOCFUNCDEBUG_t)userAlloc)(size + (align-1) + sizeof(void*), file, line); + + char *amem = ((char*)mem) + sizeof(void*); + if( (uintptr_t)amem & (align - 1) ) + amem += align - ((uintptr_t)amem & (align - 1)); + + ((void**)amem)[-1] = mem; + return amem; +} + +void *alignedMalloc(size_t size, size_t align) +{ + void *mem = userAlloc(size + (align-1) + sizeof(void*)); + + char *amem = ((char*)mem) + sizeof(void*); + if( (uintptr_t)amem & (align - 1) ) + amem += align - ((uintptr_t)amem & (align - 1)); + + ((void**)amem)[-1] = mem; + return amem; +} + +void alignedFree(void *mem) +{ + userFree( ((void**)mem)[-1] ); +} + +bool isAligned(const void* const pointer, asUINT alignment) +{ + return (uintptr_t(pointer) % alignment) == 0; +} +#endif + // By default we'll use the standard memory management functions // Make sure these globals are initialized first. Otherwise the @@ -61,25 +118,47 @@ BEGIN_AS_NAMESPACE #pragma init_seg(lib) asALLOCFUNC_t userAlloc = malloc; asFREEFUNC_t userFree = free; +#ifdef WIP_16BYTE_ALIGN +#ifdef AS_DEBUG +asALLOCALIGNEDFUNC_t userAllocAligned = (asALLOCALIGNEDFUNC_t)debugAlignedMalloc; +#else +asALLOCALIGNEDFUNC_t userAllocAligned = alignedMalloc; +#endif +asFREEALIGNEDFUNC_t userFreeAligned = alignedFree; +#endif #else // Other compilers will just have to rely on luck. asALLOCFUNC_t userAlloc = malloc; asFREEFUNC_t userFree = free; +#ifdef WIP_16BYTE_ALIGN +asALLOCALIGNEDFUNC_t userAllocAligned = alignedMalloc; +asFREEALIGNEDFUNC_t userFreeAligned = alignedFree; +#endif #endif extern "C" { +// interface int asSetGlobalMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc) { + // Clean-up thread local memory before changing the allocation routines to avoid + // potential problem with trying to free memory using a different allocation + // routine than used when allocating it. + asThreadCleanup(); + userAlloc = allocFunc; userFree = freeFunc; return 0; } +// interface int asResetGlobalMemoryFunctions() { + // Clean-up thread local memory before changing the allocation routines to avoid + // potential problem with trying to free memory using a different allocation + // routine than used when allocating it. asThreadCleanup(); userAlloc = malloc; @@ -88,6 +167,18 @@ int asResetGlobalMemoryFunctions() return 0; } +// interface +void *asAllocMem(size_t size) +{ + return asNEWARRAY(asBYTE, size); +} + +// interface +void asFreeMem(void *mem) +{ + asDELETEARRAY(mem); +} + } // extern "C" asCMemoryMgr::asCMemoryMgr() diff --git a/lib/angelscript/source/as_memory.h b/lib/angelscript/source/as_memory.h index 1b8e06a7f..369af642b 100644 --- a/lib/angelscript/source/as_memory.h +++ b/lib/angelscript/source/as_memory.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -48,6 +48,25 @@ BEGIN_AS_NAMESPACE extern asALLOCFUNC_t userAlloc; extern asFREEFUNC_t userFree; +#ifdef WIP_16BYTE_ALIGN + +// TODO: This declaration should be in angelscript.h +// when the application can register it's own +// aligned memory routines +typedef void *(*asALLOCALIGNEDFUNC_t)(size_t, size_t); +typedef void (*asFREEALIGNEDFUNC_t)(void *); +extern asALLOCALIGNEDFUNC_t userAllocAligned; +extern asFREEALIGNEDFUNC_t userFreeAligned; +typedef void *(*asALLOCALIGNEDFUNCDEBUG_t)(size_t, size_t, const char *, unsigned int); + +// The maximum type alignment supported. +const int MAX_TYPE_ALIGNMENT = 16; + +// Utility function used for assertions. +bool isAligned(const void* const pointer, asUINT alignment); + +#endif // WIP_16BYTE_ALIGN + // We don't overload the new operator as that would affect the application as well #ifndef AS_DEBUG @@ -58,6 +77,11 @@ extern asFREEFUNC_t userFree; #define asNEWARRAY(x,cnt) (x*)userAlloc(sizeof(x)*cnt) #define asDELETEARRAY(ptr) userFree(ptr) +#ifdef WIP_16BYTE_ALIGN + #define asNEWARRAYALIGNED(x,cnt, alignment) (x*)userAllocAligned(sizeof(x)*cnt, alignment) + #define asDELETEARRAYALIGNED(ptr) userFreeAligned(ptr) +#endif + #else typedef void *(*asALLOCFUNCDEBUG_t)(size_t, const char *, unsigned int); @@ -68,6 +92,12 @@ extern asFREEFUNC_t userFree; #define asNEWARRAY(x,cnt) (x*)((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(x)*cnt, __FILE__, __LINE__) #define asDELETEARRAY(ptr) userFree(ptr) +#ifdef WIP_16BYTE_ALIGN + //TODO: Equivalent of debug allocation function with alignment? + #define asNEWARRAYALIGNED(x,cnt, alignment) (x*)userAllocAligned(sizeof(x)*cnt, alignment) + #define asDELETEARRAYALIGNED(ptr) userFreeAligned(ptr) +#endif + #endif END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_module.cpp b/lib/angelscript/source/as_module.cpp index 10aeab0bd..232e17be1 100644 --- a/lib/angelscript/source/as_module.cpp +++ b/lib/angelscript/source/as_module.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -46,6 +46,33 @@ BEGIN_AS_NAMESPACE +// TODO: 2.30.0: redesign: Improved method for discarding modules (faster clean-up, less abuse of garbage collector) +// +// I need to separate the reference counter for internal references and outside references: +// +// - Internal references are for example, when the module refers to a function or object since it is declared in the module, or when +// a function refers to another function since it is being called in the code. +// - Outside references are for example object instances holding a reference to the object type, or a context currently +// executing a function. +// +// If no object instances are alive or no contexts are alive it is known that functions from a discarded module +// can be called, so they can be destroyed without any need to execute the complex garbage collection routines. +// +// If there are live objects, the entire module should be kept for safe keeping, though no longer visible. +// +// TODO: It may not be necessary to keep track of internal references. Without keeping track of internal references, can I still +// handle RemoveFunction and RemoveGlobalVariable correctly? +// +// TODO: How to avoid global variables keeping code alive? For example a script object, or a funcdef? +// Can I do a quick check of the object types and functions to count number of outside references, and then do another +// check over the global variables to subtract the outside references coming from these? What if the outside reference +// is added by an application type in a global variable that the engine doesn't know about? Example, a global dictionary +// holding object instances. Should discarding a module immediately destroy the content of the global variables? What if +// a live object tries to access the global variable after it has been discarded? Throwing a script exception is acceptable? +// Perhaps I need to allow the user to provide a clean-up routine that will be executed before destroying the objects. +// Or I might just put that responsibility on the application. + + // internal asCModule::asCModule(const char *name, asCScriptEngine *engine) { @@ -66,6 +93,8 @@ asCModule::~asCModule() { InternalReset(); + // The builder is not removed by InternalReset because it holds the script + // sections that will be built, so we need to explictly remove it now if it exists if( builder ) { asDELETE(builder,asCBuilder); @@ -75,34 +104,114 @@ asCModule::~asCModule() if( engine ) { // Clean the user data - if( userData && engine->cleanModuleFunc ) - engine->cleanModuleFunc(this); + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n+1] ) + { + for( asUINT c = 0; c < engine->cleanModuleFuncs.GetLength(); c++ ) + if( engine->cleanModuleFuncs[c].type == userData[n] ) + engine->cleanModuleFuncs[c].cleanFunc(this); + } + } // Remove the module from the engine - if( engine->lastModule == this ) - engine->lastModule = 0; - engine->scriptModules.RemoveValue(this); + ACQUIREEXCLUSIVE(engine->engineRWLock); + // The module must have been discarded before it is deleted + asASSERT( !engine->scriptModules.Exists(this) ); + engine->discardedModules.RemoveValue(this); + RELEASEEXCLUSIVE(engine->engineRWLock); } } // interface void asCModule::Discard() { - asDELETE(this,asCModule); + // Reset the global variables already so that no object in the global variables keep the module alive forever. + // If any live object tries to access the global variables during clean up they will fail with a script exception, + // so the application must keep that in mind before discarding a module. + CallExit(); + + // Keep a local copy of the engine pointer, because once the module is moved do the discarded + // pile, it is possible that another thread might discard it while we are still in here. So no + // further access to members may be done after that + asCScriptEngine *lEngine = engine; + + // Instead of deleting the module immediately, move it to the discarded pile + // This will turn it invisible to the application, yet keep it alive until all + // external references to its entities have been released. + ACQUIREEXCLUSIVE(engine->engineRWLock); + if( lEngine->lastModule == this ) + lEngine->lastModule = 0; + lEngine->scriptModules.RemoveValue(this); + lEngine->discardedModules.PushLast(this); + RELEASEEXCLUSIVE(lEngine->engineRWLock); + + // Allow the engine to go over the list of discarded modules to see what can be cleaned up at this moment. + // Don't do this if the engine is already shutting down, as it will be done explicitly by the engine itself with error reporting + if( !lEngine->shuttingDown ) + { + if( lEngine->ep.autoGarbageCollect ) + lEngine->GarbageCollect(); + else + { + // GarbageCollect calls DeleteDiscardedModules, so no need + // to call it again if we already called GarbageCollect + lEngine->DeleteDiscardedModules(); + } + } } // interface -void *asCModule::SetUserData(void *data) +void *asCModule::SetUserData(void *data, asPWORD type) { - void *oldData = userData; - userData = data; - return oldData; + // As a thread might add a new new user data at the same time as another + // it is necessary to protect both read and write access to the userData member + ACQUIREEXCLUSIVE(engine->engineRWLock); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + void *oldData = reinterpret_cast(userData[n+1]); + userData[n+1] = reinterpret_cast(data); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return oldData; + } + } + + userData.PushLast(type); + userData.PushLast(reinterpret_cast(data)); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return 0; } // interface -void *asCModule::GetUserData() const +void *asCModule::GetUserData(asPWORD type) const { - return userData; + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + ACQUIRESHARED(engine->engineRWLock); + + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + void *ud = reinterpret_cast(userData[n+1]); + RELEASESHARED(engine->engineRWLock); + return ud; + } + } + + RELEASESHARED(engine->engineRWLock); + + return 0; } // interface @@ -204,6 +313,15 @@ int asCModule::Build() #else TimeIt("asCModule::Build"); + // Don't allow the module to be rebuilt if there are still + // external references that will need the previous code + // TODO: 2.30.0: interface: The asIScriptModule must have a method for querying if the module is used + if( HasExternalReferences(false) ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_MODULE_IS_IN_USE); + return asMODULE_IS_IN_USE; + } + // Only one thread may build at one time // TODO: It should be possible to have multiple threads perform compilations int r = engine->RequestBuild(); @@ -218,7 +336,7 @@ int asCModule::Build() return asINVALID_CONFIGURATION; } - InternalReset(); + InternalReset(); if( !builder ) { @@ -306,8 +424,8 @@ int asCModule::CallInit(asIScriptContext *myCtx) { if( ctx == 0 ) { - r = engine->CreateContext(&ctx, true); - if( r < 0 ) + ctx = engine->RequestContext(); + if( ctx == 0 ) break; } @@ -346,7 +464,7 @@ int asCModule::CallInit(asIScriptContext *myCtx) if( ctx && !myCtx ) { - ctx->Release(); + engine->ReturnContext(ctx); ctx = 0; } @@ -401,33 +519,98 @@ void asCModule::CallExit() isGlobalVarInitialized = false; } +// internal +bool asCModule::HasExternalReferences(bool shuttingDown) +{ + // Check all entiteis in the module for any external references. + // If there are any external references the module cannot be deleted yet. + + for( asUINT n = 0; n < scriptFunctions.GetLength(); n++ ) + if( scriptFunctions[n] && scriptFunctions[n]->externalRefCount.get() ) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, scriptFunctions[n]->GetName(), scriptFunctions[n]->GetFuncType()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + + for( asUINT n = 0; n < classTypes.GetLength(); n++ ) + if( classTypes[n] && classTypes[n]->externalRefCount.get() ) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + msg.Format(TXT_PREV_TYPE_IS_NAMED_s, classTypes[n]->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + + for( asUINT n = 0; n < funcDefs.GetLength(); n++ ) + if( funcDefs[n] && funcDefs[n]->externalRefCount.get() ) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, funcDefs[n]->GetName(), funcDefs[n]->GetFuncType()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + + for( asUINT n = 0; n < templateInstances.GetLength(); n++ ) + if( templateInstances[n] && templateInstances[n]->externalRefCount.get() ) + { + if( !shuttingDown ) + return true; + else + { + asCString msg; + msg.Format(TXT_EXTRNL_REF_TO_MODULE_s, name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + + msg.Format(TXT_PREV_TYPE_IS_NAMED_s, templateInstances[n]->GetName()); + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); + } + } + + return false; +} + // internal void asCModule::InternalReset() { CallExit(); - size_t n; + asUINT n; - // Release all global functions - asCSymbolTable::iterator funcIt = globalFunctions.List(); - for( ; funcIt; funcIt++ ) - (*funcIt)->Release(); + // Remove all global functions globalFunctions.Clear(); - // First release all compiled functions - for( n = 0; n < scriptFunctions.GetLength(); n++ ) - if( scriptFunctions[n] ) - scriptFunctions[n]->Orphan(this); - scriptFunctions.SetLength(0); - - // Release the global properties declared in the module + // Destroy the internals of the global properties here, but do not yet remove them from the + // engine, because functions need the engine's varAddressMap to get to the property. If the + // property is removed already, it may leak as the refCount doesn't reach 0. asCSymbolTableIterator globIt = scriptGlobals.List(); while( globIt ) { - (*globIt)->Orphan(this); + (*globIt)->DestroyInternal(); globIt++; } - scriptGlobals.Clear(); UnbindAllImportedFunctions(); @@ -436,39 +619,156 @@ void asCModule::InternalReset() { if( bindInformations[n] ) { - asUINT id = bindInformations[n]->importedFunctionSignature->id & ~FUNC_IMPORTED; - engine->importedFunctions[id] = 0; - engine->freeImportedFunctionIdxs.PushLast(id); + bindInformations[n]->importedFunctionSignature->ReleaseInternal(); - asDELETE(bindInformations[n]->importedFunctionSignature, asCScriptFunction); asDELETE(bindInformations[n], sBindInfo); } } bindInformations.SetLength(0); // Free declared types, including classes, typedefs, and enums - // TODO: optimize: Check if it is possible to destroy the object directly without notifying the GC + for( n = 0; n < templateInstances.GetLength(); n++ ) + { + asCObjectType *type = templateInstances[n]; + if( engine->FindNewOwnerForSharedType(type, this) != this ) + { + // The type is owned by another module, just release our reference + type->ReleaseInternal(); + continue; + } + + // Orphan the template instance + type->module = 0; + + // No other module is holding the template type + engine->RemoveTemplateInstanceType(type); + type->ReleaseInternal(); + } + templateInstances.SetLength(0); for( n = 0; n < classTypes.GetLength(); n++ ) - classTypes[n]->Orphan(this); + { + asCObjectType *type = classTypes[n]; + if( type->IsShared() ) + { + // The type is shared, so transfer ownership to another module that also uses it + if( engine->FindNewOwnerForSharedType(type, this) != this ) + { + // The type is owned by another module, just release our reference + type->ReleaseInternal(); + continue; + } + } + + // The type should be destroyed now + type->DestroyInternal(); + + // Remove the type from the engine + if( type->IsShared() ) + { + engine->sharedScriptTypes.RemoveValue(type); + type->ReleaseInternal(); + } + + // Release it from the module + type->module = 0; + type->ReleaseInternal(); + } classTypes.SetLength(0); for( n = 0; n < enumTypes.GetLength(); n++ ) - enumTypes[n]->Release(); + { + asCObjectType *type = enumTypes[n]; + if( type->IsShared() ) + { + // The type is shared, so transfer ownership to another module that also uses it + if( engine->FindNewOwnerForSharedType(type, this) != this ) + { + // The type is owned by another module, just release our reference + type->ReleaseInternal(); + continue; + } + } + + // The type should be destroyed now + type->DestroyInternal(); + + // Remove the type from the engine + if( type->IsShared() ) + { + engine->sharedScriptTypes.RemoveValue(type); + type->ReleaseInternal(); + } + + // Release it from the module + type->module = 0; + type->ReleaseInternal(); + } enumTypes.SetLength(0); for( n = 0; n < typeDefs.GetLength(); n++ ) - typeDefs[n]->Release(); + { + asCObjectType *type = typeDefs[n]; + + // The type should be destroyed now + type->DestroyInternal(); + + // Release it from the module + type->module = 0; + type->ReleaseInternal(); + } typeDefs.SetLength(0); // Free funcdefs for( n = 0; n < funcDefs.GetLength(); n++ ) { - // The funcdefs are not removed from the engine at this moment as they may still be referred - // to by other types. The engine's ClearUnusedTypes will take care of the clean up. - funcDefs[n]->Release(); + asCScriptFunction *func = funcDefs[n]; + if( func->IsShared() ) + { + // The func is shared, so transfer ownership to another module that also uses it + if( engine->FindNewOwnerForSharedFunc(func, this) != this ) + { + // The func is owned by another module, just release our reference + func->ReleaseInternal(); + continue; + } + } + + func->DestroyInternal(); + engine->RemoveFuncdef(func); + func->module = 0; + func->ReleaseInternal(); } funcDefs.SetLength(0); - // Allow the engine to clean up what is not used - engine->CleanupAfterDiscardModule(); + // Then release the functions + for( n = 0; n < scriptFunctions.GetLength(); n++ ) + { + asCScriptFunction *func = scriptFunctions[n]; + if( func->IsShared() ) + { + // The func is shared, so transfer ownership to another module that also uses it + if( engine->FindNewOwnerForSharedFunc(func, this) != this ) + { + // The func is owned by another module, just release our reference + func->ReleaseInternal(); + continue; + } + } + + func->DestroyInternal(); + func->module = 0; + func->ReleaseInternal(); + } + scriptFunctions.SetLength(0); + + // Now remove and release the global properties as there are no more references to them + globIt = scriptGlobals.List(); + while( globIt ) + { + engine->RemoveGlobalProperty(*globIt); + asASSERT( (*globIt)->refCount.get() == 1 ); + (*globIt)->Release(); + globIt++; + } + scriptGlobals.Clear(); asASSERT( IsEmpty() ); } @@ -476,12 +776,22 @@ void asCModule::InternalReset() // interface asIScriptFunction *asCModule::GetFunctionByName(const char *name) const { - const asCArray &idxs = globalFunctions.GetIndexes(defaultNamespace, name); - if( idxs.GetLength() != 1 ) - return 0; + asSNameSpace *ns = defaultNamespace; + while( ns ) + { + const asCArray &idxs = globalFunctions.GetIndexes(ns, name); + if( idxs.GetLength() != 1 ) + return 0; - const asIScriptFunction *func = globalFunctions.Get(idxs[0]); - return const_cast(func); + const asIScriptFunction *func = globalFunctions.Get(idxs[0]); + if( func ) + return const_cast(func); + + // Recursively search parent namespaces + ns = engine->GetParentNameSpace(ns); + } + + return 0; } // interface @@ -562,38 +872,49 @@ asIScriptFunction *asCModule::GetFunctionByDecl(const char *decl) const asSNameSpace *ns = func.nameSpace == engine->nameSpaces[0] ? defaultNamespace : func.nameSpace; // Search script functions for matching interface - asIScriptFunction *f = 0; - const asCArray &idxs = globalFunctions.GetIndexes(ns, func.name); - for( unsigned int n = 0; n < idxs.GetLength(); n++ ) + while( ns ) { - const asCScriptFunction *funcPtr = globalFunctions.Get(idxs[n]); - if( funcPtr->objectType == 0 && - func.returnType == funcPtr->returnType && - func.parameterTypes.GetLength() == funcPtr->parameterTypes.GetLength() - ) + asIScriptFunction *f = 0; + const asCArray &idxs = globalFunctions.GetIndexes(ns, func.name); + for( unsigned int n = 0; n < idxs.GetLength(); n++ ) { - bool match = true; - for( size_t p = 0; p < func.parameterTypes.GetLength(); ++p ) + const asCScriptFunction *funcPtr = globalFunctions.Get(idxs[n]); + if( funcPtr->objectType == 0 && + func.returnType == funcPtr->returnType && + func.parameterTypes.GetLength() == funcPtr->parameterTypes.GetLength() + ) { - if( func.parameterTypes[p] != funcPtr->parameterTypes[p] ) + bool match = true; + for( asUINT p = 0; p < func.parameterTypes.GetLength(); ++p ) { - match = false; - break; + if( func.parameterTypes[p] != funcPtr->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( f == 0 ) + f = const_cast(funcPtr); + else + // Multiple functions + return 0; } } + } - if( match ) - { - if( f == 0 ) - f = const_cast(funcPtr); - else - // Multiple functions - return 0; - } + if( f ) + return f; + else + { + // Search for matching functions in the parent namespace + ns = engine->GetParentNameSpace(ns); } } - return f; + return 0; } // interface @@ -605,21 +926,41 @@ asUINT asCModule::GetGlobalVarCount() const // interface int asCModule::GetGlobalVarIndexByName(const char *name) const { + asSNameSpace *ns = defaultNamespace; + // Find the global var id - int id = scriptGlobals.GetFirstIndex(defaultNamespace, name); + while( ns ) + { + int id = scriptGlobals.GetFirstIndex(ns, name); + if( id >= 0 ) return id; - if( id == -1 ) return asNO_GLOBAL_VAR; + // Recursively search parent namespaces + ns = engine->GetParentNameSpace(ns); + } - return id; + return asNO_GLOBAL_VAR; } // interface int asCModule::RemoveGlobalVar(asUINT index) { + // TODO: 2.30.0: redesign: Before removing the variable, clear it to free the object + // The property shouldn't be orphaned. + asCGlobalProperty *prop = scriptGlobals.Get(index); if( !prop ) return asINVALID_ARG; - prop->Orphan(this); + + // Destroy the internal of the global variable (removes the initialization function) + prop->DestroyInternal(); + + // Check if the module is the only one referring to the module, if so remove it from the engine too + // If the property is not removed now, it will be removed later when the module is discarded + if( prop->refCount.get() == 2 ) + engine->RemoveGlobalProperty(prop); + + // Remove the global variable from the module + prop->Release(); scriptGlobals.Erase(index); return 0; @@ -641,9 +982,15 @@ int asCModule::GetGlobalVarIndexByDecl(const char *decl) const return r; // Search global variables for a match - int id = scriptGlobals.GetFirstIndex(nameSpace, name, asCCompGlobPropType(dt)); - if( id != -1 ) - return id; + while( nameSpace ) + { + int id = scriptGlobals.GetFirstIndex(nameSpace, name, asCCompGlobPropType(dt)); + if( id != -1 ) + return id; + + // Recursively search parent namespace + nameSpace = engine->GetParentNameSpace(nameSpace); + } return asNO_GLOBAL_VAR; } @@ -670,9 +1017,9 @@ const char *asCModule::GetGlobalVarDeclaration(asUINT index, bool includeNamespa if (!prop) return 0; asCString *tempString = &asCThreadManager::GetLocalData()->string; - *tempString = prop->type.Format(); + *tempString = prop->type.Format(defaultNamespace); *tempString += " "; - if( includeNamespace ) + if( includeNamespace && prop->nameSpace->name != "" ) *tempString += prop->nameSpace->name + "::"; *tempString += prop->name; @@ -715,12 +1062,19 @@ asIObjectType *asCModule::GetObjectTypeByIndex(asUINT index) const // interface asIObjectType *asCModule::GetObjectTypeByName(const char *name) const { - for( asUINT n = 0; n < classTypes.GetLength(); n++ ) + asSNameSpace *ns = defaultNamespace; + while( ns ) { - if( classTypes[n] && - classTypes[n]->name == name && - classTypes[n]->nameSpace == defaultNamespace ) - return classTypes[n]; + for( asUINT n = 0; n < classTypes.GetLength(); n++ ) + { + if( classTypes[n] && + classTypes[n]->name == name && + classTypes[n]->nameSpace == ns ) + return classTypes[n]; + } + + // Recursively search parent namespace + ns = engine->GetParentNameSpace(ns); } return 0; @@ -744,6 +1098,24 @@ int asCModule::GetTypeIdByDecl(const char *decl) const return engine->GetTypeIdFromDataType(dt); } +// interface +asIObjectType *asCModule::GetObjectTypeByDecl(const char *decl) const +{ + asCDataType dt; + + // This const cast is safe since we know the engine won't be modified + asCBuilder bld(engine, const_cast(this)); + + // Don't write parser errors to the message callback + bld.silent = true; + + int r = bld.ParseDataType(decl, &dt, defaultNamespace); + if( r < 0 ) + return 0; + + return dt.GetObjectType(); +} + // interface asUINT asCModule::GetEnumCount() const { @@ -827,7 +1199,7 @@ int asCModule::GetNextImportedFunctionId() #ifndef AS_NO_COMPILER // internal -int asCModule::AddScriptFunction(int sectionIdx, int declaredAt, int id, const asCString &name, const asCDataType &returnType, const asCArray ¶ms, const asCArray &inOutFlags, const asCArray &defaultArgs, bool isInterface, asCObjectType *objType, bool isConstMethod, bool isGlobalFunction, bool isPrivate, bool isFinal, bool isOverride, bool isShared, asSNameSpace *ns) +int asCModule::AddScriptFunction(int sectionIdx, int declaredAt, int id, const asCString &name, const asCDataType &returnType, const asCArray ¶ms, const asCArray ¶mNames, const asCArray &inOutFlags, const asCArray &defaultArgs, bool isInterface, asCObjectType *objType, bool isConstMethod, bool isGlobalFunction, bool isPrivate, bool isProtected, bool isFinal, bool isOverride, bool isShared, asSNameSpace *ns) { asASSERT(id >= 0); @@ -860,11 +1232,15 @@ int asCModule::AddScriptFunction(int sectionIdx, int declaredAt, int id, const a func->scriptData->declaredAt = declaredAt; } func->parameterTypes = params; + func->parameterNames = paramNames; func->inOutFlags = inOutFlags; func->defaultArgs = defaultArgs; func->objectType = objType; + if( objType ) + objType->AddRefInternal(); func->isReadOnly = isConstMethod; func->isPrivate = isPrivate; + func->isProtected = isProtected; func->isFinal = isFinal; func->isOverride = isOverride; func->isShared = isShared; @@ -875,9 +1251,9 @@ int asCModule::AddScriptFunction(int sectionIdx, int declaredAt, int id, const a asASSERT( !(!objType && isFinal) ); asASSERT( !(!objType && isOverride) ); - // The script function's refCount was initialized to 1 + // The internal ref count was already set by the constructor scriptFunctions.PushLast(func); - engine->SetScriptFunction(func); + engine->AddScriptFunction(func); // Compute the signature id if( objType ) @@ -885,10 +1261,7 @@ int asCModule::AddScriptFunction(int sectionIdx, int declaredAt, int id, const a // Add reference if( isGlobalFunction ) - { globalFunctions.Put(func); - func->AddRef(); - } return 0; } @@ -897,8 +1270,8 @@ int asCModule::AddScriptFunction(int sectionIdx, int declaredAt, int id, const a int asCModule::AddScriptFunction(asCScriptFunction *func) { scriptFunctions.PushLast(func); - func->AddRef(); - engine->SetScriptFunction(func); + func->AddRefInternal(); + engine->AddScriptFunction(func); return 0; } @@ -982,14 +1355,14 @@ int asCModule::BindImportedFunction(asUINT index, asIScriptFunction *func) if( dst->parameterTypes.GetLength() != src->parameterTypes.GetLength() ) return asINVALID_INTERFACE; - for( size_t n = 0; n < dst->parameterTypes.GetLength(); ++n ) + for( asUINT n = 0; n < dst->parameterTypes.GetLength(); ++n ) { if( dst->parameterTypes[n] != src->parameterTypes[n] ) return asINVALID_INTERFACE; } bindInformations[index]->boundFunctionId = src->GetId(); - src->AddRef(); + src->AddRefInternal(); return asSUCCESS; } @@ -1007,7 +1380,7 @@ int asCModule::UnbindImportedFunction(asUINT index) if( oldFuncID != -1 ) { bindInformations[index]->boundFunctionId = -1; - engine->scriptFunctions[oldFuncID]->Release(); + engine->scriptFunctions[oldFuncID]->ReleaseInternal(); } } @@ -1047,7 +1420,7 @@ int asCModule::BindAllImportedFunctions() asCScriptFunction *importFunc = GetImportedFunction(n); if( importFunc == 0 ) return asERROR; - asCString str = importFunc->GetDeclarationStr(); + asCString str = importFunc->GetDeclarationStr(false, true); // Get module name from where the function should be imported const char *moduleName = GetImportedFunctionSourceModule(n); @@ -1086,7 +1459,7 @@ int asCModule::UnbindAllImportedFunctions() // internal asCObjectType *asCModule::GetObjectType(const char *type, asSNameSpace *ns) { - size_t n; + asUINT n; // TODO: optimize: Improve linear search for( n = 0; n < classTypes.GetLength(); n++ ) @@ -1121,8 +1494,9 @@ asCGlobalProperty *asCModule::AllocateGlobalProperty(const char *name, const asC // Make an entry in the address to variable map engine->varAddressMap.Insert(prop->GetAddressOfValue(), prop); - // Store the variable in the module scope (the reference count is already set to 1) + // Store the variable in the module scope scriptGlobals.Put(prop); + prop->AddRef(); return prop; } @@ -1166,6 +1540,14 @@ int asCModule::LoadByteCode(asIBinaryStream *in, bool *wasDebugInfoStripped) { if( in == 0 ) return asINVALID_ARG; + // Don't allow the module to be rebuilt if there are still + // external references that will need the previous code + if( HasExternalReferences(false) ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_MODULE_IS_IN_USE); + return asMODULE_IS_IN_USE; + } + // Only permit loading bytecode if no other thread is currently compiling // TODO: It should be possible to have multiple threads perform compilations int r = engine->RequestBuild(); @@ -1302,14 +1684,14 @@ int asCModule::CompileFunction(const char *sectionName, const char *code, int li if( r >= 0 && outFunc && func ) { - // Return the function to the caller + // Return the function to the caller and add an external reference *outFunc = func; func->AddRef(); } // Release our reference to the function if( func ) - func->Release(); + func->ReleaseInternal(); return r; #endif @@ -1318,15 +1700,24 @@ int asCModule::CompileFunction(const char *sectionName, const char *code, int li // interface int asCModule::RemoveFunction(asIScriptFunction *func) { + // TODO: 2.30.0: redesign: Check if there are any references before removing the function + // if there are, just hide it from the visible but do not destroy or + // remove it from the module. + // + // Only if the function has no live references, nor internal references + // can it be immediately removed, and its internal references released. + // + // Check if any previously hidden functions are without references, + // if so they should removed too. + // Find the global function asCScriptFunction *f = static_cast(func); int idx = globalFunctions.GetIndex(f); if( idx >= 0 ) { globalFunctions.Erase(idx); - f->Release(); scriptFunctions.RemoveValue(f); - f->Orphan(this); + f->ReleaseInternal(); return 0; } @@ -1348,7 +1739,7 @@ int asCModule::AddFuncDef(const asCString &name, asSNameSpace *ns) engine->funcDefs.PushLast(func); func->id = engine->GetNextScriptFunctionId(); - engine->SetScriptFunction(func); + engine->AddScriptFunction(func); return (int)funcDefs.GetLength()-1; } diff --git a/lib/angelscript/source/as_module.h b/lib/angelscript/source/as_module.h index 6023052ad..22ad598d4 100644 --- a/lib/angelscript/source/as_module.h +++ b/lib/angelscript/source/as_module.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -132,6 +132,7 @@ public: virtual asUINT GetObjectTypeCount() const; virtual asIObjectType *GetObjectTypeByIndex(asUINT index) const; virtual asIObjectType *GetObjectTypeByName(const char *name) const; + virtual asIObjectType *GetObjectTypeByDecl(const char *decl) const; virtual int GetTypeIdByDecl(const char *decl) const; // Enums @@ -159,8 +160,8 @@ public: virtual int LoadByteCode(asIBinaryStream *in, bool *wasDebugInfoStripped); // User data - virtual void *SetUserData(void *data); - virtual void *GetUserData() const; + virtual void *SetUserData(void *data, asPWORD type); + virtual void *GetUserData(asPWORD type) const; //----------------------------------------------- // Internal @@ -177,6 +178,7 @@ public: void InternalReset(); bool IsEmpty() const; + bool HasExternalReferences(bool shuttingDown); int CallInit(asIScriptContext *ctx); void CallExit(); @@ -184,7 +186,7 @@ public: void JITCompile(); #ifndef AS_NO_COMPILER - int AddScriptFunction(int sectionIdx, int declaredAt, int id, const asCString &name, const asCDataType &returnType, const asCArray ¶ms, const asCArray &inOutFlags, const asCArray &defaultArgs, bool isInterface, asCObjectType *objType = 0, bool isConstMethod = false, bool isGlobalFunction = false, bool isPrivate = false, bool isFinal = false, bool isOverride = false, bool isShared = false, asSNameSpace *ns = 0); + int AddScriptFunction(int sectionIdx, int declaredAt, int id, const asCString &name, const asCDataType &returnType, const asCArray ¶ms, const asCArray ¶mNames, const asCArray &inOutFlags, const asCArray &defaultArgs, bool isInterface, asCObjectType *objType = 0, bool isConstMethod = false, bool isGlobalFunction = false, bool isPrivate = false, bool isProtected = false, bool isFinal = false, bool isOverride = false, bool isShared = false, asSNameSpace *ns = 0); int AddScriptFunction(asCScriptFunction *func); int AddImportedFunction(int id, const asCString &name, const asCDataType &returnType, const asCArray ¶ms, const asCArray &inOutFlags, const asCArray &defaultArgs, asSNameSpace *ns, const asCString &moduleName); int AddFuncDef(const asCString &name, asSNameSpace *ns); @@ -197,31 +199,35 @@ public: asCString name; - asCScriptEngine *engine; - asCBuilder *builder; - void *userData; - asDWORD accessMask; - asSNameSpace *defaultNamespace; + asCScriptEngine *engine; + asCBuilder *builder; + asCArray userData; + asDWORD accessMask; + asSNameSpace *defaultNamespace; - // This array holds all functions, class members, factories, etc that were compiled with the module - asCArray scriptFunctions; - // This array holds global functions declared in the module - asCSymbolTable globalFunctions; - // This array holds imported functions in the module - asCArray bindInformations; + // This array holds all functions, class members, factories, etc that were compiled with the module. + // These references hold an internal reference to the function object. + asCArray scriptFunctions; // increases ref count + // This array holds global functions declared in the module. These references are not counted, + // as the same pointer is always present in the scriptFunctions array too. + asCSymbolTable globalFunctions; // doesn't increase ref count + // This array holds imported functions in the module. + asCArray bindInformations; // increases ref count + // This array holds template instance types created for the module's object types + asCArray templateInstances; // increases ref count // This array holds the global variables declared in the script - asCSymbolTable scriptGlobals; + asCSymbolTable scriptGlobals; // increases ref count bool isGlobalVarInitialized; // This array holds class and interface types - asCArray classTypes; + asCArray classTypes; // increases ref count // This array holds enum types - asCArray enumTypes; + asCArray enumTypes; // increases ref count // This array holds typedefs - asCArray typeDefs; + asCArray typeDefs; // increases ref count // This array holds the funcdefs declared in the module - asCArray funcDefs; + asCArray funcDefs; // increases ref count }; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_namespace.h b/lib/angelscript/source/as_namespace.h index 9cb6ed990..ae384e7aa 100644 --- a/lib/angelscript/source/as_namespace.h +++ b/lib/angelscript/source/as_namespace.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2013 Andreas Jonsson + Copyright (c) 2013-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -73,4 +73,5 @@ struct asSNameSpaceNamePair END_AS_NAMESPACE -#endif \ No newline at end of file +#endif + diff --git a/lib/angelscript/source/as_objecttype.cpp b/lib/angelscript/source/as_objecttype.cpp index 3646b122b..92ceaece3 100644 --- a/lib/angelscript/source/as_objecttype.cpp +++ b/lib/angelscript/source/as_objecttype.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -45,140 +45,95 @@ BEGIN_AS_NAMESPACE -#ifdef AS_MAX_PORTABILITY - -static void ObjectType_AddRef_Generic(asIScriptGeneric *gen) -{ - asCObjectType *self = (asCObjectType*)gen->GetObject(); - self->AddRef(); -} - -static void ObjectType_Release_Generic(asIScriptGeneric *gen) -{ - asCObjectType *self = (asCObjectType*)gen->GetObject(); - self->Release(); -} - -static void ObjectType_GetRefCount_Generic(asIScriptGeneric *gen) -{ - asCObjectType *self = (asCObjectType*)gen->GetObject(); - *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); -} - -static void ObjectType_SetGCFlag_Generic(asIScriptGeneric *gen) -{ - asCObjectType *self = (asCObjectType*)gen->GetObject(); - self->SetGCFlag(); -} - -static void ObjectType_GetGCFlag_Generic(asIScriptGeneric *gen) -{ - asCObjectType *self = (asCObjectType*)gen->GetObject(); - *(bool*)gen->GetAddressOfReturnLocation() = self->GetGCFlag(); -} - -static void ObjectType_EnumReferences_Generic(asIScriptGeneric *gen) -{ - asCObjectType *self = (asCObjectType*)gen->GetObject(); - asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); - self->EnumReferences(engine); -} - -static void ObjectType_ReleaseAllHandles_Generic(asIScriptGeneric *gen) -{ - asCObjectType *self = (asCObjectType*)gen->GetObject(); - asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); - self->ReleaseAllHandles(engine); -} - -#endif - - -void RegisterObjectTypeGCBehaviours(asCScriptEngine *engine) -{ - // Register the gc behaviours for the object types - int r = 0; - UNUSED_VAR(r); // It is only used in debug mode - engine->objectTypeBehaviours.engine = engine; - engine->objectTypeBehaviours.flags = asOBJ_REF | asOBJ_GC; - engine->objectTypeBehaviours.name = "_builtin_objecttype_"; -#ifndef AS_MAX_PORTABILITY - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCObjectType,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCObjectType,Release), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCObjectType,GetRefCount), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCObjectType,SetGCFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCObjectType,GetGCFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCObjectType,EnumReferences), asCALL_THISCALL, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCObjectType,ReleaseAllHandles), asCALL_THISCALL, 0); asASSERT( r >= 0 ); -#else - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ObjectType_AddRef_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ObjectType_Release_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ObjectType_GetRefCount_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ObjectType_SetGCFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ObjectType_GetGCFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ObjectType_EnumReferences_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); - r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ObjectType_ReleaseAllHandles_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); -#endif -} - asCObjectType::asCObjectType() { - engine = 0; - module = 0; - refCount.set(0); + externalRefCount.set(0); + internalRefCount.set(1); // start with one internal ref-count + engine = 0; + module = 0; derivedFrom = 0; + size = 0; acceptValueSubType = true; - acceptRefSubType = true; + acceptRefSubType = true; + + scriptSectionIdx = -1; + declaredAt = 0; accessMask = 0xFFFFFFFF; - nameSpace = 0; + nameSpace = 0; +#ifdef WIP_16BYTE_ALIGN + alignment = 4; +#endif } asCObjectType::asCObjectType(asCScriptEngine *engine) { + externalRefCount.set(0); + internalRefCount.set(1); // start with one internal ref count this->engine = engine; - module = 0; - refCount.set(0); + module = 0; derivedFrom = 0; acceptValueSubType = true; - acceptRefSubType = true; + acceptRefSubType = true; + + scriptSectionIdx = -1; + declaredAt = 0; accessMask = 0xFFFFFFFF; - nameSpace = engine->nameSpaces[0]; + nameSpace = engine->nameSpaces[0]; +#ifdef WIP_16BYTE_ALIGN + alignment = 4; +#endif } int asCObjectType::AddRef() const { - gcFlag = false; - return refCount.atomicInc(); + return externalRefCount.atomicInc(); } int asCObjectType::Release() const { - gcFlag = false; - return refCount.atomicDec(); -} + int r = externalRefCount.atomicDec(); -void asCObjectType::Orphan(asCModule *mod) -{ - if( mod && mod == module ) + if( r == 0 ) { - module = 0; - if( flags & asOBJ_SCRIPT_OBJECT ) + // There are no more external references, if there are also no + // internal references then it is time to delete the object type + if( internalRefCount.get() == 0 ) { - // Tell the GC that this type exists so it can resolve potential circular references - engine->gc.AddScriptObjectToGC(this, &engine->objectTypeBehaviours); - - // It's necessary to orphan the template instance types that refer to this object type, - // otherwise the garbage collector cannot identify the circular references that involve - // the type and the template type - engine->OrphanTemplateInstances(this); + // If the engine is no longer set, then it has already been + // released and we must take care of the deletion ourselves + asDELETE(const_cast(this), asCObjectType); } } - Release(); + return r; +} + +int asCObjectType::AddRefInternal() +{ + return internalRefCount.atomicInc(); +} + +int asCObjectType::ReleaseInternal() +{ + int r = internalRefCount.atomicDec(); + + if( r == 0 ) + { + // There are no more internal references, if there are also no + // external references then it is time to delete the object type + if( externalRefCount.get() == 0 ) + { + // If the engine is no longer set, then it has already been + // released and we must take care of the deletion ourselves + asDELETE(const_cast(this), asCObjectType); + } + } + + return r; } // interface @@ -237,36 +192,29 @@ void *asCObjectType::GetUserData(asPWORD type) const return 0; } -int asCObjectType::GetRefCount() +void asCObjectType::DestroyInternal() { - return refCount.get(); -} + if( engine == 0 ) return; -bool asCObjectType::GetGCFlag() -{ - return gcFlag; -} - -void asCObjectType::SetGCFlag() -{ - gcFlag = true; -} - -asCObjectType::~asCObjectType() -{ // Skip this for list patterns as they do not increase the references if( flags & asOBJ_LIST_PATTERN ) + { + // Clear the engine pointer to mark the object type as invalid + engine = 0; return; + } // Release the object types held by the templateSubTypes for( asUINT subtypeIndex = 0; subtypeIndex < templateSubTypes.GetLength(); subtypeIndex++ ) { if( templateSubTypes[subtypeIndex].GetObjectType() ) - templateSubTypes[subtypeIndex].GetObjectType()->Release(); + templateSubTypes[subtypeIndex].GetObjectType()->ReleaseInternal(); } + templateSubTypes.SetLength(0); if( derivedFrom ) - derivedFrom->Release(); + derivedFrom->ReleaseInternal(); + derivedFrom = 0; ReleaseAllProperties(); @@ -290,6 +238,22 @@ asCObjectType::~asCObjectType() engine->cleanObjectTypeFuncs[c].cleanFunc(this); } } + userData.SetLength(0); + + // Remove the type from the engine + engine->RemoveFromTypeIdMap(this); + + // Clear the engine pointer to mark the object type as invalid + engine = 0; +} + +asCObjectType::~asCObjectType() +{ + if( engine == 0 ) + return; + + // TODO: 2.30.0: redesign: Shouldn't this have been done already? + DestroyInternal(); } // interface @@ -369,30 +333,23 @@ int asCObjectType::GetTypeId() const // interface int asCObjectType::GetSubTypeId(asUINT subtypeIndex) const { - if( flags & asOBJ_TEMPLATE ) - { - if( subtypeIndex >= templateSubTypes.GetLength() ) - return asINVALID_ARG; + // This method is only supported for templates and template specializations + if( templateSubTypes.GetLength() == 0 ) + return asERROR; - return engine->GetTypeIdFromDataType(templateSubTypes[subtypeIndex]); - } + if( subtypeIndex >= templateSubTypes.GetLength() ) + return asINVALID_ARG; - // Only template types have sub types - return asERROR; + return engine->GetTypeIdFromDataType(templateSubTypes[subtypeIndex]); } // interface asIObjectType *asCObjectType::GetSubType(asUINT subtypeIndex) const { - if( flags & asOBJ_TEMPLATE ) - { - if( subtypeIndex >= templateSubTypes.GetLength() ) - return 0; + if( subtypeIndex >= templateSubTypes.GetLength() ) + return 0; - return templateSubTypes[subtypeIndex].GetObjectType(); - } - - return 0; + return templateSubTypes[subtypeIndex].GetObjectType(); } asUINT asCObjectType::GetSubTypeCount() const @@ -475,7 +432,7 @@ asIScriptFunction *asCObjectType::GetMethodByIndex(asUINT index, bool getVirtual asIScriptFunction *asCObjectType::GetMethodByName(const char *name, bool getVirtual) const { int id = -1; - for( size_t n = 0; n < methods.GetLength(); n++ ) + for( asUINT n = 0; n < methods.GetLength(); n++ ) { if( engine->scriptFunctions[methods[n]]->name == name ) { @@ -532,23 +489,26 @@ asUINT asCObjectType::GetPropertyCount() const } // interface -int asCObjectType::GetProperty(asUINT index, const char **name, int *typeId, bool *isPrivate, int *offset, bool *isReference, asDWORD *accessMask) const +int asCObjectType::GetProperty(asUINT index, const char **name, int *typeId, bool *isPrivate, bool *isProtected, int *offset, bool *isReference, asDWORD *accessMask) const { if( index >= properties.GetLength() ) return asINVALID_ARG; + asCObjectProperty *prop = properties[index]; if( name ) - *name = properties[index]->name.AddressOf(); + *name = prop->name.AddressOf(); if( typeId ) - *typeId = engine->GetTypeIdFromDataType(properties[index]->type); + *typeId = engine->GetTypeIdFromDataType(prop->type); if( isPrivate ) - *isPrivate = properties[index]->isPrivate; + *isPrivate = prop->isPrivate; + if( isProtected ) + *isProtected = prop->isProtected; if( offset ) - *offset = properties[index]->byteOffset; + *offset = prop->byteOffset; if( isReference ) - *isReference = properties[index]->type.IsReference(); + *isReference = prop->type.IsReference(); if( accessMask ) - *accessMask = properties[index]->accessMask; + *accessMask = prop->accessMask; return 0; } @@ -562,9 +522,11 @@ const char *asCObjectType::GetPropertyDeclaration(asUINT index, bool includeName asCString *tempString = &asCThreadManager::GetLocalData()->string; if( properties[index]->isPrivate ) *tempString = "private "; + else if( properties[index]->isProtected ) + *tempString = "protected "; else *tempString = ""; - *tempString += properties[index]->type.Format(includeNamespace); + *tempString += properties[index]->type.Format(nameSpace, includeNamespace); *tempString += " "; *tempString += properties[index]->name; @@ -596,7 +558,6 @@ asUINT asCObjectType::GetBehaviourCount() const // For reference types, the factories are also stored in the constructor // list, so it is sufficient to enumerate only those count += (asUINT)beh.constructors.GetLength(); - count += (asUINT)beh.operators.GetLength() / 2; return count; } @@ -689,14 +650,6 @@ asIScriptFunction *asCObjectType::GetBehaviourByIndex(asUINT index, asEBehaviour else count += (asUINT)beh.constructors.GetLength(); - if( index - count < beh.operators.GetLength() / 2 ) - { - index = 2*(index - count); - - if( outBehaviour ) *outBehaviour = static_cast(beh.operators[index]); - return engine->scriptFunctions[beh.operators[index + 1]]; - } - return 0; } @@ -717,10 +670,10 @@ asDWORD asCObjectType::GetAccessMask() const } // internal -asCObjectProperty *asCObjectType::AddPropertyToClass(const asCString &name, const asCDataType &dt, bool isPrivate) +asCObjectProperty *asCObjectType::AddPropertyToClass(const asCString &name, const asCDataType &dt, bool isPrivate, bool isProtected, bool isInherited) { asASSERT( flags & asOBJ_SCRIPT_OBJECT ); - asASSERT( dt.CanBeInstanciated() ); + asASSERT( dt.CanBeInstantiated() ); asASSERT( !IsInterface() ); // Store the properties in the object type descriptor @@ -731,9 +684,11 @@ asCObjectProperty *asCObjectType::AddPropertyToClass(const asCString &name, cons return 0; } - prop->name = name; - prop->type = dt; - prop->isPrivate = isPrivate; + prop->name = name; + prop->type = dt; + prop->isPrivate = isPrivate; + prop->isProtected = isProtected; + prop->isInherited = isInherited; int propSize; if( dt.IsObject() ) @@ -755,8 +710,19 @@ asCObjectProperty *asCObjectType::AddPropertyToClass(const asCString &name, cons propSize = dt.GetSizeInMemoryBytes(); // Add extra bytes so that the property will be properly aligned +#ifndef WIP_16BYTE_ALIGN if( propSize == 2 && (size & 1) ) size += 1; if( propSize > 2 && (size & 3) ) size += 4 - (size & 3); +#else + asUINT alignment = dt.GetAlignment(); + const asUINT propSizeAlignmentDifference = size & (alignment-1); + if( propSizeAlignmentDifference != 0 ) + { + size += (alignment - propSizeAlignmentDifference); + } + + asASSERT((size % alignment) == 0); +#endif prop->byteOffset = size; size += propSize; @@ -770,7 +736,7 @@ asCObjectProperty *asCObjectType::AddPropertyToClass(const asCString &name, cons // Add reference to object types asCObjectType *type = prop->type.GetObjectType(); if( type ) - type->AddRef(); + type->AddRefInternal(); return prop; } @@ -791,7 +757,14 @@ void asCObjectType::ReleaseAllProperties() // Release references to objects types asCObjectType *type = properties[n]->type.GetObjectType(); if( type ) - type->Release(); + type->ReleaseInternal(); + } + else + { + // Release template instance types (ref increased by RegisterObjectProperty) + asCObjectType *type = properties[n]->type.GetObjectType(); + if( type ) + type->ReleaseInternal(); } asDELETE(properties[n],asCObjectProperty); @@ -801,13 +774,6 @@ void asCObjectType::ReleaseAllProperties() properties.SetLength(0); } -// internal -void asCObjectType::ReleaseAllHandles(asIScriptEngine *) -{ - ReleaseAllFunctions(); - ReleaseAllProperties(); -} - // internal void asCObjectType::ReleaseAllFunctions() { @@ -816,7 +782,7 @@ void asCObjectType::ReleaseAllFunctions() for( asUINT a = 0; a < beh.factories.GetLength(); a++ ) { if( engine->scriptFunctions[beh.factories[a]] ) - engine->scriptFunctions[beh.factories[a]]->Release(); + engine->scriptFunctions[beh.factories[a]]->ReleaseInternal(); } beh.factories.SetLength(0); @@ -825,155 +791,74 @@ void asCObjectType::ReleaseAllFunctions() for( asUINT b = 0; b < beh.constructors.GetLength(); b++ ) { if( engine->scriptFunctions[beh.constructors[b]] ) - engine->scriptFunctions[beh.constructors[b]]->Release(); + engine->scriptFunctions[beh.constructors[b]]->ReleaseInternal(); } beh.constructors.SetLength(0); if( beh.templateCallback ) - engine->scriptFunctions[beh.templateCallback]->Release(); + engine->scriptFunctions[beh.templateCallback]->ReleaseInternal(); beh.templateCallback = 0; if( beh.listFactory ) - engine->scriptFunctions[beh.listFactory]->Release(); + engine->scriptFunctions[beh.listFactory]->ReleaseInternal(); beh.listFactory = 0; if( beh.destruct ) - engine->scriptFunctions[beh.destruct]->Release(); + engine->scriptFunctions[beh.destruct]->ReleaseInternal(); beh.destruct = 0; if( beh.copy ) - engine->scriptFunctions[beh.copy]->Release(); + engine->scriptFunctions[beh.copy]->ReleaseInternal(); beh.copy = 0; - for( asUINT e = 1; e < beh.operators.GetLength(); e += 2 ) - { - if( engine->scriptFunctions[beh.operators[e]] ) - engine->scriptFunctions[beh.operators[e]]->Release(); - } - beh.operators.SetLength(0); - for( asUINT c = 0; c < methods.GetLength(); c++ ) { if( engine->scriptFunctions[methods[c]] ) - engine->scriptFunctions[methods[c]]->Release(); + engine->scriptFunctions[methods[c]]->ReleaseInternal(); } methods.SetLength(0); for( asUINT d = 0; d < virtualFunctionTable.GetLength(); d++ ) { if( virtualFunctionTable[d] ) - virtualFunctionTable[d]->Release(); + virtualFunctionTable[d]->ReleaseInternal(); } virtualFunctionTable.SetLength(0); // GC behaviours if( beh.addref ) - engine->scriptFunctions[beh.addref]->Release(); + engine->scriptFunctions[beh.addref]->ReleaseInternal(); beh.addref = 0; if( beh.release ) - engine->scriptFunctions[beh.release]->Release(); + engine->scriptFunctions[beh.release]->ReleaseInternal(); beh.release = 0; if( beh.gcEnumReferences ) - engine->scriptFunctions[beh.gcEnumReferences]->Release(); + engine->scriptFunctions[beh.gcEnumReferences]->ReleaseInternal(); beh.gcEnumReferences = 0; if( beh.gcGetFlag ) - engine->scriptFunctions[beh.gcGetFlag]->Release(); + engine->scriptFunctions[beh.gcGetFlag]->ReleaseInternal(); beh.gcGetFlag = 0; if( beh.gcGetRefCount ) - engine->scriptFunctions[beh.gcGetRefCount]->Release(); + engine->scriptFunctions[beh.gcGetRefCount]->ReleaseInternal(); beh.gcGetRefCount = 0; if( beh.gcReleaseAllReferences ) - engine->scriptFunctions[beh.gcReleaseAllReferences]->Release(); + engine->scriptFunctions[beh.gcReleaseAllReferences]->ReleaseInternal(); beh.gcReleaseAllReferences = 0; if( beh.gcSetFlag ) - engine->scriptFunctions[beh.gcSetFlag]->Release(); + engine->scriptFunctions[beh.gcSetFlag]->ReleaseInternal(); beh.gcSetFlag = 0; if ( beh.getWeakRefFlag ) - engine->scriptFunctions[beh.getWeakRefFlag]->Release(); + engine->scriptFunctions[beh.getWeakRefFlag]->ReleaseInternal(); beh.getWeakRefFlag = 0; } -// internal -void asCObjectType::EnumReferences(asIScriptEngine *) -{ - for( asUINT a = 0; a < beh.factories.GetLength(); a++ ) - if( engine->scriptFunctions[beh.factories[a]] ) - engine->GCEnumCallback(engine->scriptFunctions[beh.factories[a]]); - - for( asUINT b = 0; b < beh.constructors.GetLength(); b++ ) - if( engine->scriptFunctions[beh.constructors[b]] ) - engine->GCEnumCallback(engine->scriptFunctions[beh.constructors[b]]); - - if( beh.templateCallback ) - engine->GCEnumCallback(engine->scriptFunctions[beh.templateCallback]); - - if( beh.listFactory ) - engine->GCEnumCallback(engine->scriptFunctions[beh.listFactory]); - - if( beh.destruct ) - engine->GCEnumCallback(engine->scriptFunctions[beh.destruct]); - - if( beh.addref ) - engine->GCEnumCallback(engine->scriptFunctions[beh.addref]); - - if( beh.release ) - engine->GCEnumCallback(engine->scriptFunctions[beh.release]); - - if( beh.copy ) - engine->GCEnumCallback(engine->scriptFunctions[beh.copy]); - - if( beh.gcEnumReferences ) - engine->GCEnumCallback(engine->scriptFunctions[beh.gcEnumReferences]); - - if( beh.gcGetFlag ) - engine->GCEnumCallback(engine->scriptFunctions[beh.gcGetFlag]); - - if( beh.gcGetRefCount ) - engine->GCEnumCallback(engine->scriptFunctions[beh.gcGetRefCount]); - - if( beh.gcReleaseAllReferences ) - engine->GCEnumCallback(engine->scriptFunctions[beh.gcReleaseAllReferences]); - - if( beh.gcSetFlag ) - engine->GCEnumCallback(engine->scriptFunctions[beh.gcSetFlag]); - - for( asUINT e = 1; e < beh.operators.GetLength(); e += 2 ) - if( engine->scriptFunctions[beh.operators[e]] ) - engine->GCEnumCallback(engine->scriptFunctions[beh.operators[e]]); - - for( asUINT c = 0; c < methods.GetLength(); c++ ) - if( engine->scriptFunctions[methods[c]] ) - engine->GCEnumCallback(engine->scriptFunctions[methods[c]]); - - for( asUINT d = 0; d < virtualFunctionTable.GetLength(); d++ ) - if( virtualFunctionTable[d] ) - engine->GCEnumCallback(virtualFunctionTable[d]); - - for( asUINT p = 0; p < properties.GetLength(); p++ ) - { - asCObjectType *type = properties[p]->type.GetObjectType(); - if( type ) - engine->GCEnumCallback(type); - } - - for( asUINT t = 0; t < templateSubTypes.GetLength(); t++ ) - if( templateSubTypes[t].GetObjectType() ) - engine->GCEnumCallback(templateSubTypes[t].GetObjectType()); - - if( beh.getWeakRefFlag ) - engine->GCEnumCallback(engine->scriptFunctions[beh.getWeakRefFlag]); - - if( derivedFrom ) - engine->GCEnumCallback(derivedFrom); -} - END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_objecttype.h b/lib/angelscript/source/as_objecttype.h index 4538c951f..2a72e514f 100644 --- a/lib/angelscript/source/as_objecttype.h +++ b/lib/angelscript/source/as_objecttype.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -51,28 +51,6 @@ BEGIN_AS_NAMESPACE // TODO: memory: Need to minimize used memory here, because not all types use all properties of the class -// TODO: The type id should have flags for differenciating between value types and reference types. It should also have a flag for differenciating interface types. - -// Additional flag to the class object type -const asDWORD asOBJ_IMPLICIT_HANDLE = 0x00400000; -const asDWORD asOBJ_LIST_PATTERN = 0x08000000; -const asDWORD asOBJ_ENUM = 0x10000000; -const asDWORD asOBJ_TEMPLATE_SUBTYPE = 0x20000000; -const asDWORD asOBJ_TYPEDEF = 0x40000000; - - - -// asOBJ_GC is used to indicate that the type can potentially -// form circular references, thus is garbage collected. - -// The fact that an object is garbage collected doesn't imply that an other object -// that can reference it also must be garbage collected, only if the garbage collected -// object can reference the other object as well. - -// For registered types however, we set the flag asOBJ_GC if the GC -// behaviours are registered. For script types that contain any such type we -// automatically make garbage collected as well, because we cannot know what type -// of references that object can contain, and must assume the worst. struct asSTypeBehaviour { @@ -119,7 +97,6 @@ struct asSTypeBehaviour asCArray factories; asCArray constructors; - asCArray operators; }; struct asSEnumValue @@ -131,8 +108,6 @@ struct asSEnumValue class asCScriptEngine; struct asSNameSpace; -void RegisterObjectTypeGCBehaviours(asCScriptEngine *engine); - class asCObjectType : public asIObjectType { public: @@ -178,7 +153,7 @@ public: // Properties asUINT GetPropertyCount() const; - int GetProperty(asUINT index, const char **name, int *typeId, bool *isPrivate, int *offset, bool *isReference, asDWORD *accessMask) const; + int GetProperty(asUINT index, const char **name, int *typeId, bool *isPrivate, bool *isProtected, int *offset, bool *isReference, asDWORD *accessMask) const; const char *GetPropertyDeclaration(asUINT index, bool includeNamespace = false) const; // Behaviours @@ -195,25 +170,27 @@ public: public: asCObjectType(asCScriptEngine *engine); ~asCObjectType(); + void DestroyInternal(); - void Orphan(asCModule *module); - int GetRefCount(); - void SetGCFlag(); - bool GetGCFlag(); - void EnumReferences(asIScriptEngine *); - void ReleaseAllHandles(asIScriptEngine *); + // Keep an internal reference counter to separate references coming from + // application or script objects and references coming from the script code + int AddRefInternal(); + int ReleaseInternal(); void ReleaseAllFunctions(); bool IsInterface() const; bool IsShared() const; - asCObjectProperty *AddPropertyToClass(const asCString &name, const asCDataType &dt, bool isPrivate); + asCObjectProperty *AddPropertyToClass(const asCString &name, const asCDataType &dt, bool isPrivate, bool isProtected, bool isInherited); void ReleaseAllProperties(); asCString name; asSNameSpace *nameSpace; int size; +#ifdef WIP_16BYTE_ALIGN + int alignment; +#endif asCArray properties; asCArray methods; asCArray interfaces; @@ -232,16 +209,23 @@ public: bool acceptValueSubType; bool acceptRefSubType; + // Store the script section where the code was declared + int scriptSectionIdx; + // Store the location where the function was declared (row in the lower 20 bits, and column in the upper 12) + int declaredAt; + asCScriptEngine *engine; asCModule *module; asCArray userData; protected: friend class asCScriptEngine; + friend class asCConfigGroup; + friend class asCModule; asCObjectType(); - mutable asCAtomic refCount; - mutable bool gcFlag; + mutable asCAtomic externalRefCount; + asCAtomic internalRefCount; }; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_parser.cpp b/lib/angelscript/source/as_parser.cpp index e1b273b6f..eaa0768f8 100644 --- a/lib/angelscript/source/as_parser.cpp +++ b/lib/angelscript/source/as_parser.cpp @@ -34,6 +34,18 @@ // // This class parses the script code and builds a tree for compilation // +// +// I've documented the syntax in Extended BNF. You'll find it by doing a search in +// this file by "BNF:". The starting point for the script language is SCRIPT ::=. +// +// Ref: http://matt.might.net/articles/grammars-bnf-ebnf/ +// +// ( ) - used for grouping +// { } - 0 or more repetitions +// [ ] - optional +// | - or +// ' ' - token +// @@ -113,6 +125,7 @@ int asCParser::ParseFunctionDefinition(asCScriptCode *script, bool expectListPat if( t.type != ttEnd ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t); + Error(InsteadFound(t), &t); return -1; } } @@ -160,6 +173,7 @@ int asCParser::ParseDataType(asCScriptCode *script, bool isReturnType) if( t.type != ttEnd ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t); + Error(InsteadFound(t), &t); return -1; } @@ -187,6 +201,7 @@ int asCParser::ParseTemplateDecl(asCScriptCode *script) if( t.type != ttLessThan ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttLessThan)), &t); + Error(InsteadFound(t), &t); return -1; } @@ -216,6 +231,7 @@ int asCParser::ParseTemplateDecl(asCScriptCode *script) if( t.type != ttGreaterThan ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttGreaterThan)), &t); + Error(InsteadFound(t), &t); return -1; } @@ -223,6 +239,7 @@ int asCParser::ParseTemplateDecl(asCScriptCode *script) if( t.type != ttEnd ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t); + Error(InsteadFound(t), &t); return -1; } @@ -257,12 +274,14 @@ int asCParser::ParsePropertyDeclaration(asCScriptCode *script) if( t.type != ttEnd ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t); + Error(InsteadFound(t), &t); return -1; } return 0; } +// BNF: SCOPE ::= [[IDENTIFIER] '::' {IDENTIFIER '::'}] void asCParser::ParseOptionalScope(asCScriptNode *node) { sToken t1, t2; @@ -315,6 +334,7 @@ asCScriptNode *asCParser::ParseFunctionDefinition() return node; } +// BNF: TYPEMOD ::= ['&' ['in' | 'out' | 'inout']] asCScriptNode *asCParser::ParseTypeMod(bool isParam) { asCScriptNode *node = CreateNode(snDataType); @@ -355,7 +375,8 @@ asCScriptNode *asCParser::ParseTypeMod(bool isParam) return node; } -asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType) +// BNF: TYPE ::= ['const'] SCOPE DATATYPE ['<' TYPE {',' TYPE} '>'] { ('[' ']') | '@' } +asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType, bool allowAuto) { asCScriptNode *node = CreateNode(snDataType); if( node == 0 ) return 0; @@ -377,7 +398,8 @@ asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType) ParseOptionalScope(node); // Parse the actual type - node->AddChildLast(ParseDataType(allowVariableType)); + node->AddChildLast(ParseDataType(allowVariableType, allowAuto)); + if( isSyntaxError ) return node; // If the datatype is a template type, then parse the subtype within the < > asCScriptNode *type = node->lastChild; @@ -388,6 +410,7 @@ asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType) if( t.type != ttLessThan ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttLessThan)), &t); + Error(InsteadFound(t), &t); return node; } @@ -410,6 +433,7 @@ asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType) if( script->code[t.pos] != '>' ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttGreaterThan)), &t); + Error(InsteadFound(t), &t); return node; } else @@ -433,6 +457,7 @@ asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType) if( t.type != ttCloseBracket ) { Error(ExpectedToken("]"), &t); + Error(InsteadFound(t), &t); return node; } } @@ -460,6 +485,7 @@ asCScriptNode *asCParser::ParseToken(int token) if( t1.type != token ) { Error(ExpectedToken(asCTokenizer::GetDefinition(token)), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -486,6 +512,7 @@ asCScriptNode *asCParser::ParseOneOf(int *tokens, int count) if( n == count ) { Error(ExpectedOneOf(tokens, count), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -495,8 +522,8 @@ asCScriptNode *asCParser::ParseOneOf(int *tokens, int count) return node; } - -asCScriptNode *asCParser::ParseDataType(bool allowVariableType) +// BNF: DATATYPE ::= (IDENTIFIER | PRIMTYPE | '?' | 'auto') +asCScriptNode *asCParser::ParseDataType(bool allowVariableType, bool allowAuto) { asCScriptNode *node = CreateNode(snDataType); if( node == 0 ) return 0; @@ -504,7 +531,7 @@ asCScriptNode *asCParser::ParseDataType(bool allowVariableType) sToken t1; GetToken(&t1); - if( !IsDataType(t1) && !(allowVariableType && t1.type == ttQuestion) ) + if( !IsDataType(t1) && !(allowVariableType && t1.type == ttQuestion) && !(allowAuto && t1.type == ttAuto) ) { if( t1.type == ttIdentifier ) { @@ -513,8 +540,15 @@ asCScriptNode *asCParser::ParseDataType(bool allowVariableType) errMsg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, tempString.AddressOf()); Error(errMsg, &t1); } + else if( t1.type == ttAuto ) + { + Error(TXT_AUTO_NOT_ALLOWED, &t1); + } else + { Error(TXT_EXPECTED_DATA_TYPE, &t1); + Error(InsteadFound(t1), &t1); + } return node; } @@ -524,6 +558,7 @@ asCScriptNode *asCParser::ParseDataType(bool allowVariableType) return node; } +// BNF: PRIMTYPE ::= 'void' | 'int' | 'int8' | 'int16' | 'int32' | 'int64' | 'uint' | 'uint8' | 'uint16' | 'uint32' | 'uint64' | 'float' | 'double' | 'bool' asCScriptNode *asCParser::ParseRealType() { asCScriptNode *node = CreateNode(snDataType); @@ -535,6 +570,7 @@ asCScriptNode *asCParser::ParseRealType() if( !IsRealType(t1.type) ) { Error(TXT_EXPECTED_DATA_TYPE, &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -544,6 +580,7 @@ asCScriptNode *asCParser::ParseRealType() return node; } +// BNF: IDENTIFIER ::= single token: starts with letter or _, can include any letter and digit, same as in C++ asCScriptNode *asCParser::ParseIdentifier() { asCScriptNode *node = CreateNode(snIdentifier); @@ -555,6 +592,7 @@ asCScriptNode *asCParser::ParseIdentifier() if( t1.type != ttIdentifier ) { Error(TXT_EXPECTED_IDENTIFIER, &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -564,6 +602,7 @@ asCScriptNode *asCParser::ParseIdentifier() return node; } +// BNF: PARAMLIST ::= '(' ('void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]}) ')' asCScriptNode *asCParser::ParseParameterList() { asCScriptNode *node = CreateNode(snParameterList); @@ -574,6 +613,7 @@ asCScriptNode *asCParser::ParseParameterList() if( t1.type != ttOpenParanthesis ) { Error(ExpectedToken("("), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -647,6 +687,7 @@ asCScriptNode *asCParser::ParseParameterList() else { Error(ExpectedTokens(")", ","), &t1); + Error(InsteadFound(t1), &t1); return node; } } @@ -828,6 +869,15 @@ void asCParser::Error(const asCString &text, sToken *token) builder->WriteError(script->name, text, row, col); } +void asCParser::Warning(const asCString &text, sToken *token) +{ + int row, col; + script->ConvertPosToRowCol(token->pos, &row, &col); + + if( builder ) + builder->WriteWarning(script->name, text, row, col); +} + void asCParser::Info(const asCString &text, sToken *token) { RewindTo(token); @@ -931,6 +981,19 @@ asCString asCParser::ExpectedOneOf(const char **tokens, int count) return str; } +asCString asCParser::InsteadFound(sToken &t) +{ + asCString str; + if( t.type == ttIdentifier ) + { + asCString id(&script->code[t.pos], t.length); + str.Format(TXT_INSTEAD_FOUND_s, id.AddressOf()); + } + else + str.Format(TXT_INSTEAD_FOUND_s, asCTokenizer::GetDefinition(t.type)); + return str; +} + asCScriptNode *asCParser::ParseListPattern() { asCScriptNode *node = CreateNode(snListPattern); @@ -942,6 +1005,7 @@ asCScriptNode *asCParser::ParseListPattern() if( t1.type != ttStartStatementBlock ) { Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -957,27 +1021,30 @@ asCScriptNode *asCParser::ParseListPattern() if( t1.type == ttEndStatementBlock ) { if( !afterType ) + { Error(TXT_EXPECTED_DATA_TYPE, &t1); + Error(InsteadFound(t1), &t1); + } break; } else if( t1.type == ttStartStatementBlock ) { if( afterType ) { - asCString msg; - msg.Format(TXT_EXPECTED_s_OR_s, ",", "}"); - Error(msg.AddressOf(), &t1); + Error(ExpectedTokens(",","}"), &t1); + Error(InsteadFound(t1), &t1); } RewindTo(&t1); node->AddChildLast(ParseListPattern()); afterType = true; } - else if( t1.type == ttIdentifier && IdentifierIs(t1, "repeat") ) + else if( t1.type == ttIdentifier && (IdentifierIs(t1, "repeat") || IdentifierIs(t1, "repeat_same")) ) { if( !isBeginning ) { asCString msg; - msg.Format(TXT_UNEXPECTED_TOKEN_s, "repeat"); + asCString token(&script->code[t1.pos], t1.length); + msg.Format(TXT_UNEXPECTED_TOKEN_s, token.AddressOf()); Error(msg.AddressOf(), &t1); } RewindTo(&t1); @@ -992,16 +1059,18 @@ asCScriptNode *asCParser::ParseListPattern() else if( t1.type == ttListSeparator ) { if( !afterType ) + { Error(TXT_EXPECTED_DATA_TYPE, &t1); + Error(InsteadFound(t1), &t1); + } afterType = false; } else { if( afterType ) { - asCString msg; - msg.Format(TXT_EXPECTED_s_OR_s, ",", "}"); - Error(msg.AddressOf(), &t1); + Error(ExpectedTokens(",", "}"), &t1); + Error(InsteadFound(t1), &t1); } RewindTo(&t1); node->AddChildLast(ParseType(true, true)); @@ -1025,6 +1094,10 @@ bool asCParser::IdentifierIs(const sToken &t, const char *str) } #ifndef AS_NO_COMPILER + +// This function will return true if the current token is not a template, or if it is and +// the following has a valid syntax for a template type. The source position will be left +// at the first token after the type in case of success bool asCParser::CheckTemplateType(sToken &t) { // Is this a template type? @@ -1098,6 +1171,7 @@ bool asCParser::CheckTemplateType(sToken &t) return true; } +// BNF: CAST ::= 'cast' '<' TYPE '>' '(' ASSIGN ')' asCScriptNode *asCParser::ParseCast() { asCScriptNode *node = CreateNode(snCast); @@ -1108,6 +1182,7 @@ asCScriptNode *asCParser::ParseCast() if( t1.type != ttCast ) { Error(ExpectedToken("cast"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -1117,6 +1192,7 @@ asCScriptNode *asCParser::ParseCast() if( t1.type != ttLessThan ) { Error(ExpectedToken("<"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -1124,13 +1200,11 @@ asCScriptNode *asCParser::ParseCast() node->AddChildLast(ParseType(true)); if( isSyntaxError ) return node; - node->AddChildLast(ParseTypeMod(false)); - if( isSyntaxError ) return node; - GetToken(&t1); if( t1.type != ttGreaterThan ) { Error(ExpectedToken(">"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -1138,6 +1212,7 @@ asCScriptNode *asCParser::ParseCast() if( t1.type != ttOpenParanthesis ) { Error(ExpectedToken("("), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -1148,6 +1223,7 @@ asCScriptNode *asCParser::ParseCast() if( t1.type != ttCloseParanthesis ) { Error(ExpectedToken(")"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -1156,6 +1232,7 @@ asCScriptNode *asCParser::ParseCast() return node; } +// BNF: EXPRVALUE ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')' asCScriptNode *asCParser::ParseExprValue() { asCScriptNode *node = CreateNode(snExprValue); @@ -1187,14 +1264,25 @@ asCScriptNode *asCParser::ParseExprValue() else break; } + + bool isDataType = IsDataType(t2); + bool isTemplateType = false; + if( isDataType ) + { + // Is this a template type? + tempString.Assign(&script->code[t2.pos], t2.length); + if( engine->IsTemplateType(tempString.AddressOf()) ) + isTemplateType = true; + } // Rewind so the real parsing can be done, after deciding what to parse RewindTo(&t1); // Check if this is a construct call - if( IsDataType(t2) && (t.type == ttOpenParanthesis || - t.type == ttLessThan || - t.type == ttOpenBracket) ) + if( isDataType && (t.type == ttOpenParanthesis || // type() + t.type == ttOpenBracket) ) // type[]() + node->AddChildLast(ParseConstructCall()); + else if( isTemplateType && t.type == ttLessThan ) // type() node->AddChildLast(ParseConstructCall()); else if( IsFunctionCall() ) node->AddChildLast(ParseFunctionCall()); @@ -1215,16 +1303,26 @@ asCScriptNode *asCParser::ParseExprValue() GetToken(&t1); if( t1.type != ttCloseParanthesis ) + { Error(ExpectedToken(")"), &t1); + Error(InsteadFound(t1), &t1); + } node->UpdateSourcePos(t1.pos, t1.length); } else + { Error(TXT_EXPECTED_EXPRESSION_VALUE, &t1); + Error(InsteadFound(t1), &t1); + } return node; } +// BNF: LITERAL ::= NUMBER | STRING | BITS | 'true' | 'false' | 'null' +// BNF: NUMBER ::= single token: includes integers and real numbers, same as C++ +// BNF: STRING ::= single token: single quoted ', double quoted ", or heredoc multi-line string """ +// BNF: BITS ::= single token: binary 0b or 0B, octal 0o or 0O, decimal 0d or 0D, hexadecimal 0x or 0X asCScriptNode *asCParser::ParseConstant() { asCScriptNode *node = CreateNode(snConstant); @@ -1235,6 +1333,7 @@ asCScriptNode *asCParser::ParseConstant() if( !IsConstant(t.type) ) { Error(TXT_EXPECTED_CONSTANT, &t); + Error(InsteadFound(t), &t); return node; } @@ -1266,6 +1365,7 @@ asCScriptNode *asCParser::ParseStringConstant() if( t.type != ttStringConstant && t.type != ttMultilineStringConstant && t.type != ttHeredocStringConstant ) { Error(TXT_EXPECTED_STRING, &t); + Error(InsteadFound(t), &t); return node; } @@ -1275,6 +1375,7 @@ asCScriptNode *asCParser::ParseStringConstant() return node; } +// BNF: FUNCCALL ::= SCOPE IDENTIFIER ARGLIST asCScriptNode *asCParser::ParseFunctionCall() { asCScriptNode *node = CreateNode(snFunctionCall); @@ -1292,6 +1393,7 @@ asCScriptNode *asCParser::ParseFunctionCall() return node; } +// BNF: VARACCESS ::= SCOPE IDENTIFIER asCScriptNode *asCParser::ParseVariableAccess() { asCScriptNode *node = CreateNode(snVariableAccess); @@ -1306,6 +1408,7 @@ asCScriptNode *asCParser::ParseVariableAccess() return node; } +// BNF: CONSTRUCTCALL ::= TYPE ARGLIST asCScriptNode *asCParser::ParseConstructCall() { asCScriptNode *node = CreateNode(snConstructCall); @@ -1319,27 +1422,45 @@ asCScriptNode *asCParser::ParseConstructCall() return node; } -asCScriptNode *asCParser::ParseArgList() +// BNF: ARGLIST ::= '(' [IDENTIFIER ':'] ASSIGN {',' [IDENTIFIER ':'] ASSIGN} ')' +asCScriptNode *asCParser::ParseArgList(bool withParenthesis) { asCScriptNode *node = CreateNode(snArgList); if( node == 0 ) return 0; sToken t1; - GetToken(&t1); - if( t1.type != ttOpenParanthesis ) + if( withParenthesis ) { - Error(ExpectedToken("("), &t1); - return node; + GetToken(&t1); + if( t1.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t1); + Error(InsteadFound(t1), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); } - node->UpdateSourcePos(t1.pos, t1.length); - GetToken(&t1); - if( t1.type == ttCloseParanthesis ) + if( t1.type == ttCloseParanthesis || t1.type == ttCloseBracket ) { - node->UpdateSourcePos(t1.pos, t1.length); + if( withParenthesis ) + { + if( t1.type == ttCloseParanthesis ) + node->UpdateSourcePos(t1.pos, t1.length); + else + { + asCString str; + str.Format(TXT_UNEXPECTED_TOKEN_s, asCTokenizer::GetDefinition(ttCloseBracket)); - // Statement block is finished + Error(str.AddressOf(), &t1); + } + } + else + RewindTo(&t1); + + // Argument list has ended return node; } else @@ -1348,22 +1469,55 @@ asCScriptNode *asCParser::ParseArgList() for(;;) { - node->AddChildLast(ParseAssignment()); + // Determine if this is a named argument + sToken tl, t2; + GetToken(&tl); + GetToken(&t2); + RewindTo(&tl); + + // Named arguments uses the syntax: arg : expr + // This avoids confusion when the argument has the same name as a local variable, i.e. var = expr + // It also avoids conflict with expressions to that creates anonymous objects initialized with lists, i.e. type = {...} + // The alternate syntax: arg = expr, is supported to provide backwards compatibility with 2.29.0 + // TODO: 3.0.0: Remove the alternate syntax + if( tl.type == ttIdentifier && (t2.type == ttColon || (engine->ep.alterSyntaxNamedArgs && t2.type == ttAssignment)) ) + { + asCScriptNode *named = CreateNode(snNamedArgument); + if( named == 0 ) return 0; + node->AddChildLast(named); + + named->AddChildLast(ParseIdentifier()); + GetToken(&t2); + + if( engine->ep.alterSyntaxNamedArgs == 1 && t2.type == ttAssignment ) + Warning(TXT_NAMED_ARGS_WITH_OLD_SYNTAX, &t2); + + named->AddChildLast(ParseAssignment()); + } + else + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; // Check if list continues GetToken(&t1); - if( t1.type == ttCloseParanthesis ) - { - node->UpdateSourcePos(t1.pos, t1.length); - - return node; - } - else if( t1.type == ttListSeparator ) + if( t1.type == ttListSeparator ) continue; else { - Error(ExpectedTokens(")", ","), &t1); + if( withParenthesis ) + { + if( t1.type == ttCloseParanthesis ) + node->UpdateSourcePos(t1.pos, t1.length); + else + { + Error(ExpectedTokens(")", ","), &t1); + Error(InsteadFound(t1), &t1); + } + } + else + RewindTo(&t1); + return node; } } @@ -1406,6 +1560,7 @@ bool asCParser::IsFunctionCall() return false; } +// BNF: ASSIGN ::= CONDITION [ ASSIGNOP ASSIGN ] asCScriptNode *asCParser::ParseAssignment() { asCScriptNode *node = CreateNode(snAssignment); @@ -1430,6 +1585,7 @@ asCScriptNode *asCParser::ParseAssignment() return node; } +// BNF: CONDITION ::= EXPR ['?' ASSIGN ':' ASSIGN] asCScriptNode *asCParser::ParseCondition() { asCScriptNode *node = CreateNode(snCondition); @@ -1449,6 +1605,7 @@ asCScriptNode *asCParser::ParseCondition() if( t.type != ttColon ) { Error(ExpectedToken(":"), &t); + Error(InsteadFound(t), &t); return node; } @@ -1461,11 +1618,35 @@ asCScriptNode *asCParser::ParseCondition() return node; } +// BNF: EXPR ::= (TYPE '=' INILIST) | (EXPRTERM {EXPROP EXPRTERM}) asCScriptNode *asCParser::ParseExpression() { asCScriptNode *node = CreateNode(snExpression); if( node == 0 ) return 0; + // Check if the expression is a initialization of a temp object with init list, i.e. type = {...} + sToken t; + GetToken(&t); + sToken t2 = t, t3; + if( IsDataType(t2) && CheckTemplateType(t2) ) + { + // The next token must be a = followed by a { + GetToken(&t2); + GetToken(&t3); + if( t2.type == ttAssignment && t3.type == ttStartStatementBlock ) + { + // It is an initialization, now parse it for real + RewindTo(&t); + node->AddChildLast(ParseType(false)); + GetToken(&t2); + node->AddChildLast(ParseInitList()); + return node; + } + } + + // It wasn't an initialization, so it must be an ordinary expression + RewindTo(&t); + node->AddChildLast(ParseExprTerm()); if( isSyntaxError ) return node; @@ -1487,6 +1668,7 @@ asCScriptNode *asCParser::ParseExpression() UNREACHABLE_RETURN; } +// BNF: EXPRTERM ::= {EXPRPREOP} EXPRVALUE {EXPRPOSTOP} asCScriptNode *asCParser::ParseExprTerm() { asCScriptNode *node = CreateNode(snExprTerm); @@ -1522,6 +1704,7 @@ asCScriptNode *asCParser::ParseExprTerm() UNREACHABLE_RETURN; } +// BNF: EXPRPREOP ::= '-' | '+' | '!' | '++' | '--' | '~' | '@' asCScriptNode *asCParser::ParseExprPreOp() { asCScriptNode *node = CreateNode(snExprPreOp); @@ -1532,6 +1715,7 @@ asCScriptNode *asCParser::ParseExprPreOp() if( !IsPreOperator(t.type) ) { Error(TXT_EXPECTED_PRE_OPERATOR, &t); + Error(InsteadFound(t), &t); return node; } @@ -1541,6 +1725,7 @@ asCScriptNode *asCParser::ParseExprPreOp() return node; } +// BNF: EXPRPOSTOP ::= ('.' (FUNCCALL | IDENTIFIER)) | ('[' [IDENTIFIER ':'] ASSIGN {',' [IDENTIFIER ':' ASSIGN} ']') | ARGLIST | '++' | '--' asCScriptNode *asCParser::ParseExprPostOp() { asCScriptNode *node = CreateNode(snExprPostOp); @@ -1551,6 +1736,7 @@ asCScriptNode *asCParser::ParseExprPostOp() if( !IsPostOperator(t.type) ) { Error(TXT_EXPECTED_POST_OPERATOR, &t); + Error(InsteadFound(t), &t); return node; } @@ -1570,12 +1756,13 @@ asCScriptNode *asCParser::ParseExprPostOp() } else if( t.type == ttOpenBracket ) { - node->AddChildLast(ParseAssignment()); + node->AddChildLast(ParseArgList(false)); GetToken(&t); if( t.type != ttCloseBracket ) { Error(ExpectedToken("]"), &t); + Error(InsteadFound(t), &t); return node; } @@ -1590,6 +1777,11 @@ asCScriptNode *asCParser::ParseExprPostOp() return node; } +// BNF: EXPROP ::= MATHOP | COMPOP | LOGICOP | BITOP +// BNF: MATHOP ::= '+' | '-' | '*' | '/' | '%' | '**' +// BNF: COMPOP ::= '==' | '!=' | '<' | '<=' | '>' | '>=' | 'is' | '!is' +// BNF: LOGICOP ::= '&&' | '||' | '^^' | 'and' | 'or' | 'xor' +// BNF: BITOP ::= '&' | '|' | '^' | '<<' | '>>' | '>>>' asCScriptNode *asCParser::ParseExprOperator() { asCScriptNode *node = CreateNode(snExprOperator); @@ -1600,6 +1792,7 @@ asCScriptNode *asCParser::ParseExprOperator() if( !IsOperator(t.type) ) { Error(TXT_EXPECTED_OPERATOR, &t); + Error(InsteadFound(t), &t); return node; } @@ -1609,6 +1802,7 @@ asCScriptNode *asCParser::ParseExprOperator() return node; } +// BNF: ASSIGNOP ::= '=' | '+=' | '-=' | '*=' | '/=' | '|=' | '&=' | '^=' | '%=' | '**=' | '<<=' | '>>=' | '>>>=' asCScriptNode *asCParser::ParseAssignOperator() { asCScriptNode *node = CreateNode(snExprOperator); @@ -1619,6 +1813,7 @@ asCScriptNode *asCParser::ParseAssignOperator() if( !IsAssignOperator(t.type) ) { Error(TXT_EXPECTED_OPERATOR, &t); + Error(InsteadFound(t), &t); return node; } @@ -1730,13 +1925,17 @@ int asCParser::ParseScript(asCScriptCode *script) if( errorWhileParsing ) return -1; + // TODO: Should allow application to request this warning to be generated. + // It should be off by default, since pre-processor may remove all + // code from a section while still being meant as valid code +/* // Warn in case there isn't anything in the script if( scriptNode->firstChild == 0 ) { if( builder ) builder->WriteWarning(script->name, TXT_SECTION_IS_EMPTY, 1, 1); } - +*/ return 0; } @@ -1755,6 +1954,7 @@ int asCParser::ParseExpression(asCScriptCode *script) return 0; } +// BNF: IMPORT ::= 'import' TYPE ['&'] IDENTIFIER PARAMLIST 'from' STRING ';' asCScriptNode *asCParser::ParseImport() { asCScriptNode *node = CreateNode(snImport); @@ -1765,6 +1965,7 @@ asCScriptNode *asCParser::ParseImport() if( t.type != ttImport ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttImport)), &t); + Error(InsteadFound(t), &t); return node; } @@ -1778,6 +1979,7 @@ asCScriptNode *asCParser::ParseImport() if( t.type != ttIdentifier ) { Error(ExpectedToken(FROM_TOKEN), &t); + Error(InsteadFound(t), &t); return node; } @@ -1785,6 +1987,7 @@ asCScriptNode *asCParser::ParseImport() if( tempString != FROM_TOKEN ) { Error(ExpectedToken(FROM_TOKEN), &t); + Error(InsteadFound(t), &t); return node; } @@ -1794,6 +1997,7 @@ asCScriptNode *asCParser::ParseImport() if( t.type != ttStringConstant ) { Error(TXT_EXPECTED_STRING, &t); + Error(InsteadFound(t), &t); return node; } @@ -1809,6 +2013,7 @@ asCScriptNode *asCParser::ParseImport() if( t.type != ttEndStatement ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatement)), &t); + Error(InsteadFound(t), &t); return node; } @@ -1817,6 +2022,7 @@ asCScriptNode *asCParser::ParseImport() return node; } +// BNF: SCRIPT ::= {IMPORT | ENUM | TYPEDEF | CLASS | MIXIN | INTERFACE | FUNCDEF | VIRTPROP | VAR | FUNC | NAMESPACE | ';'} asCScriptNode *asCParser::ParseScript(bool inBlock) { asCScriptNode *node = CreateNode(snScript); @@ -1840,8 +2046,8 @@ asCScriptNode *asCParser::ParseScript(bool inBlock) else if( t1.type == ttTypedef ) node->AddChildLast(ParseTypedef()); // Handle primitive typedefs else if( t1.type == ttClass || - ((IdentifierIs(t1, SHARED_TOKEN) || IdentifierIs(t1, FINAL_TOKEN)) && t2.type == ttClass) || - (IdentifierIs(t1, SHARED_TOKEN) && IdentifierIs(t2, FINAL_TOKEN)) ) + ((IdentifierIs(t1, SHARED_TOKEN) || IdentifierIs(t1, FINAL_TOKEN) || IdentifierIs(t1, ABSTRACT_TOKEN)) && t2.type == ttClass) || + (IdentifierIs(t1, SHARED_TOKEN) && (IdentifierIs(t2, FINAL_TOKEN) || IdentifierIs(t2, ABSTRACT_TOKEN))) ) node->AddChildLast(ParseClass()); else if( t1.type == ttMixin ) node->AddChildLast(ParseMixin()); @@ -1849,7 +2055,7 @@ asCScriptNode *asCParser::ParseScript(bool inBlock) node->AddChildLast(ParseInterface()); else if( t1.type == ttFuncDef ) node->AddChildLast(ParseFuncDef()); - else if( t1.type == ttConst || t1.type == ttScope || IsDataType(t1) ) + else if( t1.type == ttConst || t1.type == ttScope || t1.type == ttAuto || IsDataType(t1) ) { if( IsVirtualPropertyDecl() ) node->AddChildLast(ParseVirtualPropertyDecl(false, false)); @@ -1908,6 +2114,7 @@ asCScriptNode *asCParser::ParseScript(bool inBlock) UNREACHABLE_RETURN; } +// BNF: NAMESPACE ::= 'namespace' IDENTIFIER '{' SCRIPT '}' asCScriptNode *asCParser::ParseNamespace() { asCScriptNode *node = CreateNode(snNamespace); @@ -1919,7 +2126,10 @@ asCScriptNode *asCParser::ParseNamespace() if( t1.type == ttNamespace ) node->UpdateSourcePos(t1.pos, t1.length); else + { Error(ExpectedToken(asCTokenizer::GetDefinition(ttNamespace)), &t1); + Error(InsteadFound(t1), &t1); + } // TODO: namespace: Allow declaration of multiple nested namespace with namespace A::B::C { } node->AddChildLast(ParseIdentifier()); @@ -1931,6 +2141,7 @@ asCScriptNode *asCParser::ParseNamespace() else { Error(ExpectedToken(asCTokenizer::GetDefinition(ttStartStatementBlock)), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -1948,7 +2159,10 @@ asCScriptNode *asCParser::ParseNamespace() if( t1.type == ttEnd ) Error(TXT_UNEXPECTED_END_OF_FILE, &t1); else + { Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatementBlock)), &t1); + Error(InsteadFound(t1), &t1); + } Info(TXT_WHILE_PARSING_NAMESPACE, &start); return node; } @@ -1977,6 +2191,7 @@ int asCParser::ParseStatementBlock(asCScriptCode *script, asCScriptNode *block) return 0; } +// BNF: ENUM ::= ['shared'] 'enum' IDENTIFIER '{' IDENTIFIER ['=' EXPR] {',' IDENTIFIER ['=' EXPR]} '}' asCScriptNode *asCParser::ParseEnumeration() { asCScriptNode *ident; @@ -2002,6 +2217,7 @@ asCScriptNode *asCParser::ParseEnumeration() if( token.type != ttEnum ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnum)), &token); + Error(InsteadFound(token), &token); return node; } @@ -2013,6 +2229,7 @@ asCScriptNode *asCParser::ParseEnumeration() if(ttIdentifier != token.type) { Error(TXT_EXPECTED_IDENTIFIER, &token); + Error(InsteadFound(token), &token); return node; } @@ -2034,6 +2251,7 @@ asCScriptNode *asCParser::ParseEnumeration() { RewindTo(&token); Error(ExpectedToken(asCTokenizer::GetDefinition(token.type)), &token); + Error(InsteadFound(token), &token); return node; } @@ -2050,6 +2268,7 @@ asCScriptNode *asCParser::ParseEnumeration() if(ttIdentifier != token.type) { Error(TXT_EXPECTED_IDENTIFIER, &token); + Error(InsteadFound(token), &token); return node; } @@ -2088,7 +2307,8 @@ asCScriptNode *asCParser::ParseEnumeration() if( token.type != ttEndStatementBlock ) { RewindTo(&token); - Error(ExpectedToken(asCTokenizer::GetDefinition(token.type)), &token); + Error(ExpectedToken("}"), &token); + Error(InsteadFound(token), &token); return node; } @@ -2103,10 +2323,10 @@ bool asCParser::IsVarDecl() GetToken(&t); RewindTo(&t); - // A class property decl can be preceded by 'private' + // A class property decl can be preceded by 'private' or 'protected' sToken t1; GetToken(&t1); - if( t1.type != ttPrivate ) + if( t1.type != ttPrivate && t1.type != ttProtected ) RewindTo(&t1); // A variable decl can start with a const @@ -2114,25 +2334,28 @@ bool asCParser::IsVarDecl() if( t1.type == ttConst ) GetToken(&t1); - // The type may be initiated with the scope operator - if( t1.type == ttScope ) - GetToken(&t1); - - // The type may be preceeded with a multilevel scope sToken t2; - GetToken(&t2); - while( t1.type == ttIdentifier && t2.type == ttScope ) + if( t1.type != ttAuto ) { - GetToken(&t1); + // The type may be initiated with the scope operator + if( t1.type == ttScope ) + GetToken(&t1); + + // The type may be preceeded with a multilevel scope GetToken(&t2); + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + GetToken(&t1); + GetToken(&t2); + } + RewindTo(&t2); } - RewindTo(&t2); // We don't validate if the identifier is an actual declared type at this moment // as it may wrongly identify the statement as a non-declaration if the user typed // the name incorrectly. The real type is validated in ParseDeclaration where a // proper error message can be given. - if( !IsRealType(t1.type) && t1.type != ttIdentifier ) + if( !IsRealType(t1.type) && t1.type != ttIdentifier && t1.type != ttAuto ) { RewindTo(&t); return false; @@ -2145,8 +2368,10 @@ bool asCParser::IsVarDecl() } // Object handles can be interleaved with the array brackets + // Even though declaring variables with & is invalid we'll accept + // it here to give an appropriate error message later GetToken(&t2); - while( t2.type == ttHandle || t2.type == ttOpenBracket ) + while( t2.type == ttHandle || t2.type == ttAmp || t2.type == ttOpenBracket ) { if( t2.type == ttOpenBracket ) { @@ -2193,7 +2418,7 @@ bool asCParser::IsVarDecl() GetToken(&t2); } - if( t2.type == ttEnd ) + if( t2.type == ttEnd ) return false; else { @@ -2219,10 +2444,10 @@ bool asCParser::IsVirtualPropertyDecl() GetToken(&t); RewindTo(&t); - // A class property decl can be preceded by 'private' + // A class property decl can be preceded by 'private' or 'protected' sToken t1; GetToken(&t1); - if( t1.type != ttPrivate ) + if( t1.type != ttPrivate && t1.type != ttProtected ) RewindTo(&t1); // A variable decl can start with a const @@ -2290,10 +2515,10 @@ bool asCParser::IsFuncDecl(bool isMethod) if( isMethod ) { - // A class method decl can be preceded by 'private' + // A class method decl can be preceded by 'private' or 'protected' sToken t1, t2; GetToken(&t1); - if( t1.type != ttPrivate ) + if( t1.type != ttPrivate && t1.type != ttProtected ) RewindTo(&t1); // A class constructor starts with identifier followed by parenthesis @@ -2393,7 +2618,7 @@ bool asCParser::IsFuncDecl(bool isMethod) GetToken(&t2); } - if( t2.type == ttEnd ) + if( t2.type == ttEnd ) return false; else { @@ -2430,6 +2655,7 @@ bool asCParser::IsFuncDecl(bool isMethod) return false; } +// BNF: FUNCDEF ::= 'funcdef' TYPE ['&'] IDENTIFIER PARAMLIST ';' asCScriptNode *asCParser::ParseFuncDef() { asCScriptNode *node = CreateNode(snFuncDef); @@ -2461,6 +2687,7 @@ asCScriptNode *asCParser::ParseFuncDef() if( t1.type != ttEndStatement ) { Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatement)), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -2469,6 +2696,7 @@ asCScriptNode *asCParser::ParseFuncDef() return node; } +// BNF: FUNC ::= ['private' | 'protected' | 'shared'] [((TYPE ['&']) | '~')] IDENTIFIER PARAMLIST ['const'] {'override' | 'final'} STATBLOCK asCScriptNode *asCParser::ParseFunction(bool isMethod) { asCScriptNode *node = CreateNode(snFunction); @@ -2479,12 +2707,12 @@ asCScriptNode *asCParser::ParseFunction(bool isMethod) GetToken(&t2); RewindTo(&t1); - // A class method can start with private + // A class method can start with 'private' or 'protected' if( isMethod && t1.type == ttPrivate ) - { node->AddChildLast(ParseToken(ttPrivate)); - if( isSyntaxError ) return node; - } + else if( isMethod && t1.type == ttProtected ) + node->AddChildLast(ParseToken(ttProtected)); + if( isSyntaxError ) return node; // A global function can be marked as shared if( !isMethod && IdentifierIs(t1, SHARED_TOKEN) ) @@ -2525,6 +2753,7 @@ asCScriptNode *asCParser::ParseFunction(bool isMethod) if( t1.type == ttConst ) node->AddChildLast(ParseToken(ttConst)); + // TODO: Should support abstract methods, in which case no statement block should be provided ParseMethodOverrideBehaviors(node); if( isSyntaxError ) return node; } @@ -2536,6 +2765,7 @@ asCScriptNode *asCParser::ParseFunction(bool isMethod) return node; } +// BNF: INTFMTHD ::= TYPE ['&'] IDENTIFIER PARAMLIST ['const'] ';' asCScriptNode *asCParser::ParseInterfaceMethod() { asCScriptNode *node = CreateNode(snFunction); @@ -2564,6 +2794,7 @@ asCScriptNode *asCParser::ParseInterfaceMethod() if( t1.type != ttEndStatement ) { Error(ExpectedToken(";"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -2572,6 +2803,7 @@ asCScriptNode *asCParser::ParseInterfaceMethod() return node; } +// BNF: VIRTPROP ::= ['private' | 'protected'] TYPE ['&'] IDENTIFIER '{' {('get' | 'set') ['const'] [('override' | 'final')] (STATBLOCK | ';')} '}' asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterface) { asCScriptNode *node = CreateNode(snVirtualProperty); @@ -2582,12 +2814,12 @@ asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterfa GetToken(&t2); RewindTo(&t1); - // A class method can start with private + // A class method can start with 'private' or 'protected' if( isMethod && t1.type == ttPrivate ) - { node->AddChildLast(ParseToken(ttPrivate)); - if( isSyntaxError ) return node; - } + else if( isMethod && t1.type == ttProtected ) + node->AddChildLast(ParseToken(ttProtected)); + if( isSyntaxError ) return node; node->AddChildLast(ParseType(true)); if( isSyntaxError ) return node; @@ -2602,6 +2834,7 @@ asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterfa if( t1.type != ttStartStatementBlock ) { Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -2646,6 +2879,7 @@ asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterfa else if( t1.type != ttEndStatement ) { Error(ExpectedTokens(";", "{"), &t1); + Error(InsteadFound(t1), &t1); return node; } } @@ -2655,6 +2889,7 @@ asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterfa if( t1.type != ttEndStatement ) { Error(ExpectedToken(";"), &t1); + Error(InsteadFound(t1), &t1); return node; } } @@ -2665,6 +2900,7 @@ asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterfa { const char *tokens[] = { GET_TOKEN, SET_TOKEN, asCTokenizer::GetDefinition(ttEndStatementBlock) }; Error(ExpectedOneOf(tokens, 3), &t1); + Error(InsteadFound(t1), &t1); return node; } } @@ -2672,6 +2908,7 @@ asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterfa return node; } +// BNF: INTERFACE ::= ['shared'] 'interface' IDENTIFIER [':' IDENTIFIER {',' IDENTIFIER}] '{' {VIRTPROP | INTFMTHD} '}' asCScriptNode *asCParser::ParseInterface() { asCScriptNode *node = CreateNode(snInterface); @@ -2687,6 +2924,7 @@ asCScriptNode *asCParser::ParseInterface() if( tempString != SHARED_TOKEN ) { Error(ExpectedToken(SHARED_TOKEN), &t); + Error(InsteadFound(t), &t); return node; } @@ -2698,6 +2936,7 @@ asCScriptNode *asCParser::ParseInterface() if( t.type != ttInterface ) { Error(ExpectedToken("interface"), &t); + Error(InsteadFound(t), &t); return node; } @@ -2729,6 +2968,7 @@ asCScriptNode *asCParser::ParseInterface() if( t.type != ttStartStatementBlock ) { Error(ExpectedToken("{"), &t); + Error(InsteadFound(t), &t); return node; } @@ -2756,6 +2996,7 @@ asCScriptNode *asCParser::ParseInterface() if( t.type != ttEndStatementBlock ) { Error(ExpectedToken("}"), &t); + Error(InsteadFound(t), &t); return node; } @@ -2764,6 +3005,7 @@ asCScriptNode *asCParser::ParseInterface() return node; } +// BNF: MIXIN ::= 'mixin' CLASS asCScriptNode *asCParser::ParseMixin() { asCScriptNode *node = CreateNode(snMixin); @@ -2775,6 +3017,7 @@ asCScriptNode *asCParser::ParseMixin() if( t.type != ttMixin ) { Error(ExpectedToken("mixin"), &t); + Error(InsteadFound(t), &t); return node; } @@ -2786,6 +3029,7 @@ asCScriptNode *asCParser::ParseMixin() return node; } +// BNF: CLASS ::= {'shared' | 'abstract' | 'final'} 'class' IDENTIFIER [':' IDENTIFIER {',' IDENTIFIER}] '{' {VIRTPROP | FUNC | VAR} '}' asCScriptNode *asCParser::ParseClass() { asCScriptNode *node = CreateNode(snClass); @@ -2794,15 +3038,10 @@ asCScriptNode *asCParser::ParseClass() sToken t; GetToken(&t); - // Allow the keyword 'shared' before 'class' - if( IdentifierIs(t, SHARED_TOKEN) ) - { - RewindTo(&t); - node->AddChildLast(ParseIdentifier()); - GetToken(&t); - } - - if( IdentifierIs(t, FINAL_TOKEN) ) + // Allow the keywords 'shared', 'abstract', and 'final' before 'class' + while( IdentifierIs(t, SHARED_TOKEN) || + IdentifierIs(t, ABSTRACT_TOKEN) || + IdentifierIs(t, FINAL_TOKEN) ) { RewindTo(&t); node->AddChildLast(ParseIdentifier()); @@ -2812,6 +3051,7 @@ asCScriptNode *asCParser::ParseClass() if( t.type != ttClass ) { Error(ExpectedToken("class"), &t); + Error(InsteadFound(t), &t); return node; } @@ -2855,6 +3095,7 @@ asCScriptNode *asCParser::ParseClass() if( t.type != ttStartStatementBlock ) { Error(ExpectedToken("{"), &t); + Error(InsteadFound(t), &t); return node; } @@ -2876,9 +3117,13 @@ asCScriptNode *asCParser::ParseClass() else { Error(TXT_EXPECTED_METHOD_OR_PROPERTY, &t); + Error(InsteadFound(t), &t); return node; } + if( isSyntaxError ) + return node; + GetToken(&t); RewindTo(&t); } @@ -2887,6 +3132,7 @@ asCScriptNode *asCParser::ParseClass() if( t.type != ttEndStatementBlock ) { Error(ExpectedToken("}"), &t); + Error(InsteadFound(t), &t); return node; } node->UpdateSourcePos(t.pos, t.length); @@ -2925,6 +3171,7 @@ int asCParser::ParseVarInit(asCScriptCode *script, asCScriptNode *init) { int tokens[] = {ttAssignment, ttOpenParanthesis}; Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); } // Don't allow any more tokens after the expression @@ -3040,6 +3287,7 @@ asCScriptNode *asCParser::SuperficiallyParseVarInit() { int tokens[] = {ttAssignment, ttOpenParanthesis}; Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); } return node; @@ -3057,6 +3305,7 @@ asCScriptNode *asCParser::SuperficiallyParseStatementBlock() if( t1.type != ttStartStatementBlock ) { Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -3090,6 +3339,7 @@ asCScriptNode *asCParser::SuperficiallyParseStatementBlock() return node; } +// BNF: STATBLOCK ::= '{' {VAR | STATEMENT} '}' asCScriptNode *asCParser::ParseStatementBlock() { asCScriptNode *node = CreateNode(snStatementBlock); @@ -3101,6 +3351,7 @@ asCScriptNode *asCParser::ParseStatementBlock() if( t1.type != ttStartStatementBlock ) { Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -3171,6 +3422,7 @@ asCScriptNode *asCParser::ParseStatementBlock() UNREACHABLE_RETURN; } +// BNF: INITLIST ::= '{' [ASSIGN | INITLIST] {',' [ASSIGN | INITLIST]} '}' asCScriptNode *asCParser::ParseInitList() { asCScriptNode *node = CreateNode(snInitList); @@ -3182,6 +3434,7 @@ asCScriptNode *asCParser::ParseInitList() if( t1.type != ttStartStatementBlock ) { Error(ExpectedToken("{"), &t1); + Error(InsteadFound(t1), &t1); return node; } @@ -3205,12 +3458,14 @@ asCScriptNode *asCParser::ParseInitList() { // No expression node->AddChildLast(CreateNode(snUndefined)); + node->lastChild->UpdateSourcePos(t1.pos, 1); GetToken(&t1); if( t1.type == ttEndStatementBlock ) { // No expression node->AddChildLast(CreateNode(snUndefined)); + node->lastChild->UpdateSourcePos(t1.pos, 1); node->UpdateSourcePos(t1.pos, t1.length); return node; } @@ -3220,7 +3475,7 @@ asCScriptNode *asCParser::ParseInitList() { // No expression node->AddChildLast(CreateNode(snUndefined)); - + node->lastChild->UpdateSourcePos(t1.pos, 1); node->UpdateSourcePos(t1.pos, t1.length); // Statement block is finished @@ -3245,6 +3500,7 @@ asCScriptNode *asCParser::ParseInitList() else { Error(ExpectedTokens("}", ","), &t1); + Error(InsteadFound(t1), &t1); return node; } } @@ -3268,6 +3524,7 @@ asCScriptNode *asCParser::ParseInitList() else { Error(ExpectedTokens("}", ","), &t1); + Error(InsteadFound(t1), &t1); return node; } } @@ -3276,6 +3533,7 @@ asCScriptNode *asCParser::ParseInitList() UNREACHABLE_RETURN; } +// BNF: VAR ::= ['private'|'protected'] TYPE IDENTIFIER [( '=' (INITLIST | EXPR)) | ARGLIST] {',' IDENTIFIER [( '=' (INITLIST | EXPR)) | ARGLIST]} ';' asCScriptNode *asCParser::ParseDeclaration(bool isClassProp, bool isGlobalVar) { asCScriptNode *node = CreateNode(snDeclaration); @@ -3288,9 +3546,11 @@ asCScriptNode *asCParser::ParseDeclaration(bool isClassProp, bool isGlobalVar) // A class property can be preceeded by private if( t.type == ttPrivate && isClassProp ) node->AddChildLast(ParseToken(ttPrivate)); + else if( t.type == ttProtected && isClassProp ) + node->AddChildLast(ParseToken(ttProtected)); // Parse data type - node->AddChildLast(ParseType(true)); + node->AddChildLast(ParseType(true, false, !isClassProp)); if( isSyntaxError ) return node; for(;;) @@ -3352,12 +3612,14 @@ asCScriptNode *asCParser::ParseDeclaration(bool isClassProp, bool isGlobalVar) else { Error(ExpectedTokens(",", ";"), &t); + Error(InsteadFound(t), &t); return node; } } UNREACHABLE_RETURN; } +// BNF: STATEMENT ::= (IF | FOR | WHILE | RETURN | STATBLOCK | BREAK | CONTINUE | DOWHILE | SWITCH | EXPRSTAT) asCScriptNode *asCParser::ParseStatement() { sToken t1; @@ -3384,9 +3646,17 @@ asCScriptNode *asCParser::ParseStatement() else if( t1.type == ttSwitch ) return ParseSwitch(); else + { + if( IsVarDecl() ) + { + Error(TXT_UNEXPECTED_VAR_DECL, &t1); + return 0; + } return ParseExpressionStatement(); + } } +// BNF: EXPRSTAT ::= [ASSIGN] ';' asCScriptNode *asCParser::ParseExpressionStatement() { asCScriptNode *node = CreateNode(snExpressionStatement); @@ -3410,6 +3680,7 @@ asCScriptNode *asCParser::ParseExpressionStatement() if( t.type != ttEndStatement ) { Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3418,6 +3689,7 @@ asCScriptNode *asCParser::ParseExpressionStatement() return node; } +// BNF: SWITCH ::= 'switch' '(' ASSIGN ')' '{' {CASE} '}' asCScriptNode *asCParser::ParseSwitch() { asCScriptNode *node = CreateNode(snSwitch); @@ -3428,6 +3700,7 @@ asCScriptNode *asCParser::ParseSwitch() if( t.type != ttSwitch ) { Error(ExpectedToken("switch"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3437,6 +3710,7 @@ asCScriptNode *asCParser::ParseSwitch() if( t.type != ttOpenParanthesis ) { Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); return node; } @@ -3447,6 +3721,7 @@ asCScriptNode *asCParser::ParseSwitch() if( t.type != ttCloseParanthesis ) { Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3454,6 +3729,7 @@ asCScriptNode *asCParser::ParseSwitch() if( t.type != ttStartStatementBlock ) { Error(ExpectedToken("{"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3470,6 +3746,7 @@ asCScriptNode *asCParser::ParseSwitch() { const char *tokens[] = {"case", "default"}; Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); return node; } @@ -3480,12 +3757,14 @@ asCScriptNode *asCParser::ParseSwitch() if( t.type != ttEndStatementBlock ) { Error(ExpectedToken("}"), &t); + Error(InsteadFound(t), &t); return node; } return node; } +// BNF: CASE ::= (('case' EXPR) | 'default') ':' {STATEMENT} asCScriptNode *asCParser::ParseCase() { asCScriptNode *node = CreateNode(snCase); @@ -3496,6 +3775,7 @@ asCScriptNode *asCParser::ParseCase() if( t.type != ttCase && t.type != ttDefault ) { Error(ExpectedTokens("case", "default"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3510,6 +3790,7 @@ asCScriptNode *asCParser::ParseCase() if( t.type != ttColon ) { Error(ExpectedToken(":"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3539,6 +3820,7 @@ asCScriptNode *asCParser::ParseCase() return node; } +// BNF: IF ::= 'if' '(' ASSIGN ')' STATEMENT ['else' STATEMENT] asCScriptNode *asCParser::ParseIf() { asCScriptNode *node = CreateNode(snIf); @@ -3549,6 +3831,7 @@ asCScriptNode *asCParser::ParseIf() if( t.type != ttIf ) { Error(ExpectedToken("if"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3558,6 +3841,7 @@ asCScriptNode *asCParser::ParseIf() if( t.type != ttOpenParanthesis ) { Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); return node; } @@ -3568,6 +3852,7 @@ asCScriptNode *asCParser::ParseIf() if( t.type != ttCloseParanthesis ) { Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3587,6 +3872,7 @@ asCScriptNode *asCParser::ParseIf() return node; } +// BNF: FOR ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN] ')' STATEMENT asCScriptNode *asCParser::ParseFor() { asCScriptNode *node = CreateNode(snFor); @@ -3597,6 +3883,7 @@ asCScriptNode *asCParser::ParseFor() if( t.type != ttFor ) { Error(ExpectedToken("for"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3606,6 +3893,7 @@ asCScriptNode *asCParser::ParseFor() if( t.type != ttOpenParanthesis ) { Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); return node; } @@ -3633,6 +3921,7 @@ asCScriptNode *asCParser::ParseFor() if( t.type != ttCloseParanthesis ) { Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); return node; } } @@ -3642,6 +3931,7 @@ asCScriptNode *asCParser::ParseFor() return node; } +// BNF: WHILE ::= 'while' '(' ASSIGN ')' STATEMENT asCScriptNode *asCParser::ParseWhile() { asCScriptNode *node = CreateNode(snWhile); @@ -3652,6 +3942,7 @@ asCScriptNode *asCParser::ParseWhile() if( t.type != ttWhile ) { Error(ExpectedToken("while"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3661,6 +3952,7 @@ asCScriptNode *asCParser::ParseWhile() if( t.type != ttOpenParanthesis ) { Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); return node; } @@ -3671,6 +3963,7 @@ asCScriptNode *asCParser::ParseWhile() if( t.type != ttCloseParanthesis ) { Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3679,6 +3972,7 @@ asCScriptNode *asCParser::ParseWhile() return node; } +// BNF: DOWHILE ::= 'do' STATEMENT 'while' '(' ASSIGN ')' ';' asCScriptNode *asCParser::ParseDoWhile() { asCScriptNode *node = CreateNode(snDoWhile); @@ -3689,6 +3983,7 @@ asCScriptNode *asCParser::ParseDoWhile() if( t.type != ttDo ) { Error(ExpectedToken("do"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3701,6 +3996,7 @@ asCScriptNode *asCParser::ParseDoWhile() if( t.type != ttWhile ) { Error(ExpectedToken("while"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3708,6 +4004,7 @@ asCScriptNode *asCParser::ParseDoWhile() if( t.type != ttOpenParanthesis ) { Error(ExpectedToken("("), &t); + Error(InsteadFound(t), &t); return node; } @@ -3718,6 +4015,7 @@ asCScriptNode *asCParser::ParseDoWhile() if( t.type != ttCloseParanthesis ) { Error(ExpectedToken(")"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3725,6 +4023,7 @@ asCScriptNode *asCParser::ParseDoWhile() if( t.type != ttEndStatement ) { Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); return node; } node->UpdateSourcePos(t.pos, t.length); @@ -3732,6 +4031,7 @@ asCScriptNode *asCParser::ParseDoWhile() return node; } +// BNF: RETURN ::= 'return' [ASSIGN] ';' asCScriptNode *asCParser::ParseReturn() { asCScriptNode *node = CreateNode(snReturn); @@ -3742,6 +4042,7 @@ asCScriptNode *asCParser::ParseReturn() if( t.type != ttReturn ) { Error(ExpectedToken("return"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3763,6 +4064,7 @@ asCScriptNode *asCParser::ParseReturn() if( t.type != ttEndStatement ) { Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3771,6 +4073,7 @@ asCScriptNode *asCParser::ParseReturn() return node; } +// BNF: BREAK ::= 'break' ';' asCScriptNode *asCParser::ParseBreak() { asCScriptNode *node = CreateNode(snBreak); @@ -3781,6 +4084,7 @@ asCScriptNode *asCParser::ParseBreak() if( t.type != ttBreak ) { Error(ExpectedToken("break"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3788,13 +4092,17 @@ asCScriptNode *asCParser::ParseBreak() GetToken(&t); if( t.type != ttEndStatement ) + { Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); + } node->UpdateSourcePos(t.pos, t.length); return node; } +// BNF: CONTINUE ::= 'continue' ';' asCScriptNode *asCParser::ParseContinue() { asCScriptNode *node = CreateNode(snContinue); @@ -3805,6 +4113,7 @@ asCScriptNode *asCParser::ParseContinue() if( t.type != ttContinue ) { Error(ExpectedToken("continue"), &t); + Error(InsteadFound(t), &t); return node; } @@ -3812,7 +4121,10 @@ asCScriptNode *asCParser::ParseContinue() GetToken(&t); if( t.type != ttEndStatement ) + { Error(ExpectedToken(";"), &t); + Error(InsteadFound(t), &t); + } node->UpdateSourcePos(t.pos, t.length); @@ -3820,6 +4132,7 @@ asCScriptNode *asCParser::ParseContinue() } // TODO: typedef: Typedefs should accept complex types as well +// BNF: TYPEDEF ::= 'typedef' PRIMTYPE IDENTIFIER ';' asCScriptNode *asCParser::ParseTypedef() { // Create the typedef node @@ -3831,7 +4144,8 @@ asCScriptNode *asCParser::ParseTypedef() GetToken(&token); if( token.type != ttTypedef) { - Error(ExpectedToken(asCTokenizer::GetDefinition(token.type)), &token); + Error(ExpectedToken(asCTokenizer::GetDefinition(ttTypedef)), &token); + Error(InsteadFound(token), &token); return node; } @@ -3860,6 +4174,7 @@ asCScriptNode *asCParser::ParseTypedef() { RewindTo(&token); Error(ExpectedToken(asCTokenizer::GetDefinition(token.type)), &token); + Error(InsteadFound(token), &token); } return node; @@ -3884,3 +4199,4 @@ void asCParser::ParseMethodOverrideBehaviors(asCScriptNode *funcNode) END_AS_NAMESPACE + diff --git a/lib/angelscript/source/as_parser.h b/lib/angelscript/source/as_parser.h index ed2f15cc9..76d74c0ba 100644 --- a/lib/angelscript/source/as_parser.h +++ b/lib/angelscript/source/as_parser.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -76,6 +76,7 @@ protected: void RewindTo(const sToken *token); void SetPos(size_t pos); void Error(const asCString &text, sToken *token); + void Warning(const asCString &text, sToken *token); void Info(const asCString &text, sToken *token); asCScriptNode *CreateNode(eScriptNode type); @@ -83,11 +84,11 @@ protected: asCScriptNode *ParseFunctionDefinition(); asCScriptNode *ParseParameterList(); asCScriptNode *SuperficiallyParseExpression(); - asCScriptNode *ParseType(bool allowConst, bool allowVariableType = false); + asCScriptNode *ParseType(bool allowConst, bool allowVariableType = false, bool allowAuto = false); asCScriptNode *ParseTypeMod(bool isParam); void ParseOptionalScope(asCScriptNode *node); asCScriptNode *ParseRealType(); - asCScriptNode *ParseDataType(bool allowVariableType = false); + asCScriptNode *ParseDataType(bool allowVariableType = false, bool allowAuto = false); asCScriptNode *ParseIdentifier(); asCScriptNode *ParseListPattern(); @@ -143,7 +144,7 @@ protected: asCScriptNode *ParseExprPreOp(); asCScriptNode *ParseExprPostOp(); asCScriptNode *ParseExprValue(); - asCScriptNode *ParseArgList(); + asCScriptNode *ParseArgList(bool withParenthesis = true); asCScriptNode *ParseFunctionCall(); asCScriptNode *ParseVariableAccess(); asCScriptNode *ParseConstructCall(); @@ -168,6 +169,7 @@ protected: asCString ExpectedTokens(const char *token1, const char *token2); asCString ExpectedOneOf(int *tokens, int count); asCString ExpectedOneOf(const char **tokens, int count); + asCString InsteadFound(sToken &t); bool errorWhileParsing; bool isSyntaxError; diff --git a/lib/angelscript/source/as_property.h b/lib/angelscript/source/as_property.h index cb489c4b7..983ee644f 100644 --- a/lib/angelscript/source/as_property.h +++ b/lib/angelscript/source/as_property.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -54,11 +54,14 @@ class asCObjectProperty { public: asCObjectProperty() {accessMask = 0xFFFFFFFF;} + asCObjectProperty(const asCObjectProperty &o) : name(o.name), type(o.type), byteOffset(o.byteOffset), accessMask(o.accessMask), isPrivate(o.isPrivate), isProtected(o.isProtected), isInherited(o.isInherited) {} asCString name; asCDataType type; int byteOffset; - bool isPrivate; asDWORD accessMask; + bool isPrivate; + bool isProtected; + bool isInherited; }; class asCGlobalProperty @@ -69,7 +72,7 @@ public: void AddRef(); void Release(); - int GetRefCount(); + void DestroyInternal(); void *GetAddressOfValue(); void AllocateMemory(); @@ -84,16 +87,7 @@ public: void SetInitFunc(asCScriptFunction *initFunc); asCScriptFunction *GetInitFunc(); - static void RegisterGCBehaviours(asCScriptEngine *engine); - //protected: - void SetGCFlag(); - bool GetGCFlag(); - void EnumReferences(asIScriptEngine *); - void ReleaseAllHandles(asIScriptEngine *); - - void Orphan(asCModule *module); - // This is only stored for registered properties, and keeps the pointer given by the application void *realAddress; @@ -108,7 +102,6 @@ public: // The global property structure is reference counted, so that the // engine can keep track of how many references to the property there are. asCAtomic refCount; - bool gcFlag; }; class asCCompGlobPropType : public asIFilter diff --git a/lib/angelscript/source/as_restore.cpp b/lib/angelscript/source/as_restore.cpp index fd0a1322d..19eacc933 100644 --- a/lib/angelscript/source/as_restore.cpp +++ b/lib/angelscript/source/as_restore.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -119,7 +119,7 @@ int asCReader::Error(const char *msg) error = true; } - return -1; + return asERROR; } int asCReader::ReadInner() @@ -153,9 +153,9 @@ int asCReader::ReadInner() bool sharedExists = false; if( ot->IsShared() ) { - for( asUINT n = 0; n < engine->classTypes.GetLength(); n++ ) + for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ ) { - asCObjectType *t = engine->classTypes[n]; + asCObjectType *t = engine->sharedScriptTypes[n]; if( t && t->IsShared() && t->name == ot->name && @@ -171,11 +171,22 @@ int asCReader::ReadInner() } if( sharedExists ) + { existingShared.Insert(ot, true); + ot->AddRefInternal(); + } else - engine->classTypes.PushLast(ot); + { + if( ot->IsShared() ) + { + engine->sharedScriptTypes.PushLast(ot); + ot->AddRefInternal(); + } + + // Set this module as the owner + ot->module = module; + } module->enumTypes.PushLast(ot); - ot->AddRef(); ReadObjectTypeDeclaration(ot, 2); } @@ -200,9 +211,9 @@ int asCReader::ReadInner() bool sharedExists = false; if( ot->IsShared() ) { - for( asUINT n = 0; n < engine->classTypes.GetLength(); n++ ) + for( asUINT n = 0; n < engine->sharedScriptTypes.GetLength(); n++ ) { - asCObjectType *t = engine->classTypes[n]; + asCObjectType *t = engine->sharedScriptTypes[n]; if( t && t->IsShared() && t->name == ot->name && @@ -218,16 +229,22 @@ int asCReader::ReadInner() } if( sharedExists ) + { existingShared.Insert(ot, true); + ot->AddRefInternal(); + } else { - engine->classTypes.PushLast(ot); + if( ot->IsShared() ) + { + engine->sharedScriptTypes.PushLast(ot); + ot->AddRefInternal(); + } // Set this module as the owner ot->module = module; } module->classTypes.PushLast(ot); - ot->AddRef(); } if( error ) return asERROR; @@ -258,16 +275,13 @@ int asCReader::ReadInner() { // Replace our funcdef for the existing one module->funcDefs[module->funcDefs.IndexOf(func)] = f2; - f2->AddRef(); + f2->AddRefInternal(); engine->funcDefs.RemoveValue(func); savedFunctions[savedFunctions.IndexOf(func)] = f2; - func->Release(); - - // Funcdefs aren't deleted when the ref count reaches zero so we must manually delete it here - asDELETE(func,asCScriptFunction); + func->ReleaseInternal(); break; } } @@ -312,9 +326,8 @@ int asCReader::ReadInner() } ReadObjectTypeDeclaration(ot, 1); - engine->classTypes.PushLast(ot); + ot->module = module; module->typeDefs.PushLast(ot); - ot->AddRef(); ReadObjectTypeDeclaration(ot, 2); } @@ -361,9 +374,9 @@ int asCReader::ReadInner() { // Replace the recently created function with the pre-existing function module->scriptFunctions[module->scriptFunctions.GetLength()-1] = realFunc; - realFunc->AddRef(); + realFunc->AddRefInternal(); savedFunctions[savedFunctions.GetLength()-1] = realFunc; - engine->FreeScriptFunctionId(func->id); + engine->RemoveScriptFunction(func); // Insert the function in the dontTranslate array dontTranslate.Insert(realFunc, true); @@ -371,7 +384,7 @@ int asCReader::ReadInner() // Release the function, but make sure nothing else is released func->id = 0; func->scriptData->byteCode.SetLength(0); - func->Release(); + func->ReleaseInternal(); break; } } @@ -386,8 +399,11 @@ int asCReader::ReadInner() func = ReadFunction(isNew, false, false); if( func ) { + // All the global functions were already loaded while loading the scriptFunctions, here + // we're just re-reading the references to know which goes into the globalFunctions array + asASSERT( !isNew ); + module->globalFunctions.Put(func); - func->AddRef(); } else Error(TXT_INVALID_BYTECODE_d); @@ -475,17 +491,23 @@ int asCReader::ReadInner() asCScriptFunction *callback = engine->scriptFunctions[usedTypes[i]->beh.templateCallback]; if( !engine->CallGlobalFunctionRetBool(usedTypes[i], &dontGarbageCollect, callback->sysFuncIntf, callback) ) { - asCString sub = usedTypes[i]->templateSubTypes[0].Format(); + asCString sub = usedTypes[i]->templateSubTypes[0].Format(usedTypes[i]->nameSpace); for( asUINT n = 1; n < usedTypes[i]->templateSubTypes.GetLength(); n++ ) { sub += ","; - sub += usedTypes[i]->templateSubTypes[n].Format(); + sub += usedTypes[i]->templateSubTypes[n].Format(usedTypes[i]->nameSpace); } asCString str; str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, usedTypes[i]->name.AddressOf(), sub.AddressOf()); engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); Error(TXT_INVALID_BYTECODE_d); } + else + { + // If the callback said this template instance won't be garbage collected then remove the flag + if( dontGarbageCollect ) + usedTypes[i]->flags &= ~asOBJ_GC; + } } } engine->deferValidationOfTemplateTypes = false; @@ -544,6 +566,12 @@ void asCReader::ReadUsedFunctions() asUINT count; count = ReadEncodedUInt(); usedFunctions.SetLength(count); + if( usedFunctions.GetLength() != count ) + { + // Out of memory + error = true; + return; + } memset(usedFunctions.AddressOf(), 0, sizeof(asCScriptFunction *)*count); for( asUINT n = 0; n < usedFunctions.GetLength(); n++ ) @@ -573,17 +601,35 @@ void asCReader::ReadUsedFunctions() // Find the correct function if( c == 'm' ) { - for( asUINT i = 0; i < module->scriptFunctions.GetLength(); i++ ) + if( func.funcType == asFUNC_IMPORTED ) { - asCScriptFunction *f = module->scriptFunctions[i]; - if( !func.IsSignatureEqual(f) || - func.objectType != f->objectType || - func.funcType != f->funcType || - func.nameSpace != f->nameSpace ) - continue; + for( asUINT i = 0; i < module->bindInformations.GetLength(); i++ ) + { + asCScriptFunction *f = module->bindInformations[i]->importedFunctionSignature; + if( !func.IsSignatureEqual(f) || + func.objectType != f->objectType || + func.funcType != f->funcType || + func.nameSpace != f->nameSpace ) + continue; - usedFunctions[n] = f; - break; + usedFunctions[n] = f; + break; + } + } + else + { + for( asUINT i = 0; i < module->scriptFunctions.GetLength(); i++ ) + { + asCScriptFunction *f = module->scriptFunctions[i]; + if( !func.IsSignatureEqual(f) || + func.objectType != f->objectType || + func.funcType != f->funcType || + func.nameSpace != f->nameSpace ) + continue; + + usedFunctions[n] = f; + break; + } } } else @@ -652,6 +698,12 @@ void asCReader::ReadFunctionSignature(asCScriptFunction *func) } func->inOutFlags.SetLength(func->parameterTypes.GetLength()); + if( func->inOutFlags.GetLength() != func->parameterTypes.GetLength() ) + { + // Out of memory + error = true; + return; + } memset(func->inOutFlags.AddressOf(), 0, sizeof(asETypeModifiers)*func->inOutFlags.GetLength()); count = ReadEncodedUInt(); if( count > func->parameterTypes.GetLength() ) @@ -679,6 +731,12 @@ void asCReader::ReadFunctionSignature(asCScriptFunction *func) if( count ) { func->defaultArgs.SetLength(func->parameterTypes.GetLength()); + if( func->defaultArgs.GetLength() != func->parameterTypes.GetLength() ) + { + // Out of memory + error = true; + return; + } memset(func->defaultArgs.AddressOf(), 0, sizeof(asCString*)*func->defaultArgs.GetLength()); for( i = 0; i < count; i++ ) { @@ -697,10 +755,13 @@ void asCReader::ReadFunctionSignature(asCScriptFunction *func) func->objectType = ReadObjectType(); if( func->objectType ) { + func->objectType->AddRefInternal(); + asBYTE b; ReadData(&b, 1); func->isReadOnly = (b & 1) ? true : false; func->isPrivate = (b & 2) ? true : false; + func->isProtected = (b & 4) ? true : false; func->nameSpace = engine->nameSpaces[0]; } else @@ -740,7 +801,7 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a // Load the new function isNew = true; - asCScriptFunction *func = asNEW(asCScriptFunction)(engine,module,asFUNC_DUMMY); + asCScriptFunction *func = asNEW(asCScriptFunction)(engine,0,asFUNC_DUMMY); if( func == 0 ) { // Out of memory @@ -756,13 +817,20 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a ReadFunctionSignature(func); if( error ) { - asDELETE(func, asCScriptFunction); + func->DestroyHalfCreated(); return 0; } if( func->funcType == asFUNC_SCRIPT ) { func->AllocateScriptFunctionData(); + if( func->scriptData == 0 ) + { + // Out of memory + error = true; + func->DestroyHalfCreated(); + return 0; + } if( addToGC && !addToModule ) engine->gc.AddScriptObjectToGC(func, &engine->functionBehaviours); @@ -782,6 +850,13 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a func->scriptData->funcVariableTypes.PushLast((asCScriptFunction*)(asPWORD)idx); num = ReadEncodedUInt(); func->scriptData->objVariablePos.PushLast(num); + + if( error ) + { + // No need to continue (the error has already been reported before) + func->DestroyHalfCreated(); + return 0; + } } if( count > 0 ) func->scriptData->objVariablesOnHeap = ReadEncodedUInt(); @@ -801,12 +876,26 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a { length = ReadEncodedUInt(); func->scriptData->lineNumbers.SetLength(length); + if( int(func->scriptData->lineNumbers.GetLength()) != length ) + { + // Out of memory + error = true; + func->DestroyHalfCreated(); + return 0; + } for( i = 0; i < length; ++i ) func->scriptData->lineNumbers[i] = ReadEncodedUInt(); // Read the array of script sections length = ReadEncodedUInt(); func->scriptData->sectionIdxs.SetLength(length); + if( int(func->scriptData->sectionIdxs.GetLength()) != length ) + { + // Out of memory + error = true; + func->DestroyHalfCreated(); + return 0; + } for( i = 0; i < length; ++i ) { if( (i & 1) == 0 ) @@ -820,8 +909,6 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a } } - ReadData(&func->isShared, 1); - // Read the variable information if( !noDebugInfo ) { @@ -834,7 +921,7 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a { // Out of memory error = true; - asDELETE(func, asCScriptFunction); + func->DestroyHalfCreated(); return 0; } func->scriptData->variables.PushLast(var); @@ -843,10 +930,20 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a var->stackOffset = ReadEncodedUInt(); ReadString(&var->name); ReadDataType(&var->type); + + if( error ) + { + // No need to continue (the error has already been reported before) + func->DestroyHalfCreated(); + return 0; + } } } - ReadData(&func->dontCleanUpOnException, 1); + char bits; + ReadData(&bits, 1); + func->isShared = bits & 1 ? true : false; + func->dontCleanUpOnException = bits & 2 ? true : false; // Read script section name if( !noDebugInfo ) @@ -856,6 +953,21 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a func->scriptData->scriptSectionIdx = engine->GetScriptSectionNameIndex(name.AddressOf()); func->scriptData->declaredAt = ReadEncodedUInt(); } + + // Read parameter names + if( !noDebugInfo ) + { + asUINT count = asUINT(ReadEncodedUInt64()); + if( count > func->parameterTypes.GetLength() ) + { + error = true; + func->DestroyHalfCreated(); + return 0; + } + func->parameterNames.SetLength(count); + for( asUINT n = 0; n < count; n++ ) + ReadString(&func->parameterNames[n]); + } } else if( func->funcType == asFUNC_VIRTUAL || func->funcType == asFUNC_INTERFACE ) { @@ -866,11 +978,12 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a { // The refCount is already 1 module->scriptFunctions.PushLast(func); + func->module = module; } if( addToEngine ) { func->id = engine->GetNextScriptFunctionId(); - engine->SetScriptFunction(func); + engine->AddScriptFunction(func); } if( func->objectType ) func->ComputeSignatureId(); @@ -900,18 +1013,16 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) ot->beh.factory = 0; ot->beh.constructors.PopLast(); // These will be read from the file ot->beh.factories.PopLast(); // These will be read from the file - engine->scriptFunctions[ot->beh.addref]->AddRef(); - engine->scriptFunctions[ot->beh.release]->AddRef(); - engine->scriptFunctions[ot->beh.gcEnumReferences]->AddRef(); - engine->scriptFunctions[ot->beh.gcGetFlag]->AddRef(); - engine->scriptFunctions[ot->beh.gcGetRefCount]->AddRef(); - engine->scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRef(); - engine->scriptFunctions[ot->beh.gcSetFlag]->AddRef(); - engine->scriptFunctions[ot->beh.copy]->AddRef(); + engine->scriptFunctions[ot->beh.addref]->AddRefInternal(); + engine->scriptFunctions[ot->beh.release]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcEnumReferences]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcGetFlag]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcGetRefCount]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRefInternal(); + engine->scriptFunctions[ot->beh.gcSetFlag]->AddRefInternal(); + engine->scriptFunctions[ot->beh.copy]->AddRefInternal(); // TODO: weak: Should not do this if the class has been declared with 'noweak' - engine->scriptFunctions[ot->beh.getWeakRefFlag]->AddRef(); - for( asUINT i = 1; i < ot->beh.operators.GetLength(); i += 2 ) - engine->scriptFunctions[ot->beh.operators[i]]->AddRef(); + engine->scriptFunctions[ot->beh.getWeakRefFlag]->AddRefInternal(); } else if( phase == 2 ) { @@ -990,7 +1101,7 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) { ot->derivedFrom = ReadObjectType(); if( ot->derivedFrom ) - ot->derivedFrom->AddRef(); + ot->derivedFrom->AddRefInternal(); } // interfaces[] / interfaceVFTOffsets[] @@ -1054,10 +1165,10 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) // Destroy the function without releasing any references func->id = 0; func->scriptData->byteCode.SetLength(0); - func->Release(); + func->ReleaseInternal(); } module->scriptFunctions.PushLast(realFunc); - realFunc->AddRef(); + realFunc->AddRefInternal(); dontTranslate.Insert(realFunc, true); } } @@ -1066,7 +1177,7 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) if( func ) { ot->beh.destruct = func->id; - func->AddRef(); + func->AddRefInternal(); } else ot->beh.destruct = 0; @@ -1093,7 +1204,7 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) savedFunctions[savedFunctions.GetLength()-1] = realFunc; found = true; module->scriptFunctions.PushLast(realFunc); - realFunc->AddRef(); + realFunc->AddRefInternal(); dontTranslate.Insert(realFunc, true); break; } @@ -1110,13 +1221,13 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) // Destroy the function without releasing any references func->id = 0; func->scriptData->byteCode.SetLength(0); - func->Release(); + func->ReleaseInternal(); } } else { ot->beh.constructors.PushLast(func->id); - func->AddRef(); + func->AddRefInternal(); if( func->parameterTypes.GetLength() == 0 ) ot->beh.construct = func->id; @@ -1144,7 +1255,7 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) savedFunctions[savedFunctions.GetLength()-1] = realFunc; found = true; module->scriptFunctions.PushLast(realFunc); - realFunc->AddRef(); + realFunc->AddRefInternal(); dontTranslate.Insert(realFunc, true); break; } @@ -1161,13 +1272,13 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) // Destroy the function without releasing any references func->id = 0; func->scriptData->byteCode.SetLength(0); - func->Release(); + func->ReleaseInternal(); } } else { ot->beh.factories.PushLast(func->id); - func->AddRef(); + func->AddRefInternal(); if( func->parameterTypes.GetLength() == 0 ) ot->beh.factory = func->id; @@ -1203,7 +1314,7 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) savedFunctions[savedFunctions.GetLength()-1] = realFunc; found = true; module->scriptFunctions.PushLast(realFunc); - realFunc->AddRef(); + realFunc->AddRefInternal(); dontTranslate.Insert(realFunc, true); break; } @@ -1221,7 +1332,7 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) func->id = 0; if( func->scriptData ) func->scriptData->byteCode.SetLength(0); - func->Release(); + func->ReleaseInternal(); } } else @@ -1231,13 +1342,13 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) func->parameterTypes[0].GetObjectType() == func->objectType && (func->inOutFlags[0] & asTM_INREF) ) { - engine->scriptFunctions[ot->beh.copy]->Release(); + engine->scriptFunctions[ot->beh.copy]->ReleaseInternal(); ot->beh.copy = func->id; - func->AddRef(); + func->AddRefInternal(); } ot->methods.PushLast(func->id); - func->AddRef(); + func->AddRefInternal(); } } else @@ -1268,7 +1379,7 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) savedFunctions[savedFunctions.GetLength()-1] = realFunc; found = true; module->scriptFunctions.PushLast(realFunc); - realFunc->AddRef(); + realFunc->AddRefInternal(); dontTranslate.Insert(realFunc, true); break; } @@ -1286,13 +1397,13 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase) func->id = 0; if( func->scriptData ) func->scriptData->byteCode.SetLength(0); - func->Release(); + func->ReleaseInternal(); } } else { ot->virtualFunctionTable.PushLast(func); - func->AddRef(); + func->AddRefInternal(); } } else @@ -1449,21 +1560,17 @@ void asCReader::ReadGlobalProperty() asCGlobalProperty *prop = module->AllocateGlobalProperty(name.AddressOf(), type, nameSpace); // Read the initialization function - bool f; - ReadData(&f, 1); - if( f ) + bool isNew; + // Do not add the function to the GC at this time. It will + // only be added to the GC when the module releases the property + asCScriptFunction *func = ReadFunction(isNew, false, true, false); + if( func ) { - bool isNew; - // Do not add the function to the GC at this time. It will - // only be added to the GC when the module releases the property - asCScriptFunction *func = ReadFunction(isNew, false, true, false); - if( func ) - { - prop->SetInitFunc(func); - func->Release(); - } - else - Error(TXT_INVALID_BYTECODE_d); + // Make sure the function knows it is owned by the module + func->module = module; + + prop->SetInitFunc(func); + func->ReleaseInternal(); } } @@ -1473,47 +1580,49 @@ void asCReader::ReadObjectProperty(asCObjectType *ot) ReadString(&name); asCDataType dt; ReadDataType(&dt); - bool isPrivate; - ReadData(&isPrivate, 1); + int flags = ReadEncodedUInt(); + bool isPrivate = (flags & 1) ? true : false; + bool isProtected = (flags & 2) ? true : false; + bool isInherited = (flags & 4) ? true : false; // TODO: shared: If the type is shared and pre-existing, we should just // validate that the loaded methods match the original if( !existingShared.MoveTo(0, ot) ) - ot->AddPropertyToClass(name, dt, isPrivate); + ot->AddPropertyToClass(name, dt, isPrivate, isProtected, isInherited); } void asCReader::ReadDataType(asCDataType *dt) { - eTokenType tokenType; - - tokenType = (eTokenType)ReadEncodedUInt(); - if( tokenType == 0 ) + // Check if this is a previously used type + asUINT n = ReadEncodedUInt(); + if( n != 0 ) { // Get the datatype from the cache - asUINT n = ReadEncodedUInt(); - *dt = savedDataTypes[n]; + *dt = savedDataTypes[n-1]; return; } + // Read the type definition + eTokenType tokenType = (eTokenType)ReadEncodedUInt(); + // Reserve a spot in the savedDataTypes - size_t saveSlot = savedDataTypes.GetLength(); + asUINT saveSlot = savedDataTypes.GetLength(); savedDataTypes.PushLast(asCDataType()); // Read the datatype for the first time asCObjectType *objType = 0; - bool isObjectHandle = false; - bool isReadOnly = false; - bool isHandleToConst = false; - bool isReference = false; - if( tokenType == ttIdentifier ) - { objType = ReadObjectType(); - ReadData(&isObjectHandle, 1); - ReadData(&isHandleToConst, 1); - } - ReadData(&isReference, 1); - ReadData(&isReadOnly, 1); + + struct + { + char isObjectHandle :1; + char isHandleToConst:1; + char isReference :1; + char isReadOnly :1; + } bits = {0}; + asASSERT( sizeof(bits) == 1 ); + ReadData(&bits, 1); asCScriptFunction *funcDef = 0; if( tokenType == ttIdentifier && objType && objType->name == "_builtin_function_" ) @@ -1555,16 +1664,16 @@ void asCReader::ReadDataType(asCDataType *dt) *dt = asCDataType::CreateObject(objType, false); else *dt = asCDataType::CreatePrimitive(tokenType, false); - if( isObjectHandle ) + if( bits.isObjectHandle ) { - dt->MakeReadOnly(isHandleToConst); + dt->MakeReadOnly(bits.isHandleToConst ? true : false); // Here we must allow a scoped type to be a handle // e.g. if the datatype is for a system function dt->MakeHandle(true, true); } - dt->MakeReadOnly(isReadOnly); - dt->MakeReference(isReference); + dt->MakeReadOnly(bits.isReadOnly ? true : false); + dt->MakeReference(bits.isReference ? true : false); // Update the previously saved slot savedDataTypes[saveSlot] = *dt; @@ -1578,9 +1687,12 @@ asCObjectType* asCReader::ReadObjectType() if( ch == 'a' ) { // Read the name of the template type - asCString typeName; + asCString typeName, ns; ReadString(&typeName); - asCObjectType *tmpl = engine->GetRegisteredObjectType(typeName.AddressOf(), engine->nameSpaces[0]); + ReadString(&ns); + asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf()); + + asCObjectType *tmpl = engine->GetRegisteredObjectType(typeName.AddressOf(), nameSpace); if( tmpl == 0 ) { asCString str; @@ -1615,17 +1727,17 @@ asCObjectType* asCReader::ReadObjectType() else { // Get the template instance type based on the loaded subtypes - ot = engine->GetTemplateInstanceType(tmpl, subTypes); + ot = engine->GetTemplateInstanceType(tmpl, subTypes, module); } if( ot == 0 ) { // Show all subtypes in error message - asCString sub = subTypes[0].Format(); + asCString sub = subTypes[0].Format(nameSpace); for( asUINT n = 1; n < subTypes.GetLength(); n++ ) { sub += ","; - sub += subTypes[n].Format(); + sub += subTypes[n].Format(nameSpace); } asCString str; str.Format(TXT_INSTANCING_INVLD_TMPL_TYPE_s_s, typeName.AddressOf(), sub.AddressOf()); @@ -1708,7 +1820,7 @@ asCObjectType* asCReader::ReadObjectType() else { // No object type - asASSERT( ch == '\0' ); + asASSERT( ch == '\0' || error ); ot = 0; } @@ -2080,9 +2192,10 @@ void asCReader::TranslateFunction(asCScriptFunction *func) // Pre-compute the size of each instruction in order to translate jump offsets asUINT n; asDWORD *bc = func->scriptData->byteCode.AddressOf(); - asCArray bcSizes(func->scriptData->byteCode.GetLength()); - asCArray instructionNbrToPos(func->scriptData->byteCode.GetLength()); - for( n = 0; n < func->scriptData->byteCode.GetLength(); ) + asUINT bcLength = (asUINT)func->scriptData->byteCode.GetLength(); + asCArray bcSizes(bcLength); + asCArray instructionNbrToPos(bcLength); + for( n = 0; n < bcLength; ) { int c = *(asBYTE*)&bc[n]; asUINT size = asBCTypeSize[asBCInfo[c].type]; @@ -2097,7 +2210,7 @@ void asCReader::TranslateFunction(asCScriptFunction *func) } asUINT bcNum = 0; - for( n = 0; n < func->scriptData->byteCode.GetLength(); bcNum++ ) + for( n = 0; n < bcLength; bcNum++ ) { int c = *(asBYTE*)&bc[n]; if( c == asBC_REFCPY || @@ -2137,7 +2250,7 @@ void asCReader::TranslateFunction(asCScriptFunction *func) { // List patterns have a different way of adjusting the offsets SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; - *(((short*)&bc[n])+2) = (short)listAdj->AdjustOffset(*(((short*)&bc[n])+2), ot); + *(((short*)&bc[n])+2) = (short)listAdj->AdjustOffset(*(((short*)&bc[n])+2)); } else { @@ -2308,7 +2421,7 @@ void asCReader::TranslateFunction(asCScriptFunction *func) // The adjuster also needs to know the list type so it can know the type of the elements asCObjectType *ot = func->GetObjectTypeOfLocalVar(asBC_SWORDARG0(&bc[n])); - listAdjusters.PushLast(asNEW(SListAdjuster)(&bc[n], ot)); + listAdjusters.PushLast(asNEW(SListAdjuster)(this, &bc[n], ot)); } else if( c == asBC_FREE ) { @@ -2319,6 +2432,12 @@ void asCReader::TranslateFunction(asCScriptFunction *func) asCObjectType *ot = *(asCObjectType**)pot; if( ot && (ot->flags & asOBJ_LIST_PATTERN) ) { + if( listAdjusters.GetLength() == 0 ) + { + Error(TXT_INVALID_BYTECODE_d); + return; + } + // Finalize the adjustment of the list buffer that was initiated with asBC_AllocMem SListAdjuster *list = listAdjusters.PopLast(); list->AdjustAllocMem(); @@ -2329,7 +2448,7 @@ void asCReader::TranslateFunction(asCScriptFunction *func) { // Adjust the offset in the list where the size is informed SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; - bc[n+1] = listAdj->AdjustOffset(bc[n+1], listAdj->patternType); + bc[n+1] = listAdj->AdjustOffset(bc[n+1]); // Inform the list adjuster how many values will be repeated listAdj->SetRepeatCount(bc[n+2]); @@ -2338,13 +2457,13 @@ void asCReader::TranslateFunction(asCScriptFunction *func) { // Adjust the offset in the list where the size is informed SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; - bc[n+1] = listAdj->AdjustOffset(bc[n+1], listAdj->patternType); + bc[n+1] = listAdj->AdjustOffset(bc[n+1]); } else if( c == asBC_SetListType ) { // Adjust the offset in the list where the typeid is informed SListAdjuster *listAdj = listAdjusters[listAdjusters.GetLength()-1]; - bc[n+1] = listAdj->AdjustOffset(bc[n+1], listAdj->patternType); + bc[n+1] = listAdj->AdjustOffset(bc[n+1]); // Translate the type id bc[n+2] = FindTypeId(bc[n+2]); @@ -2361,7 +2480,7 @@ void asCReader::TranslateFunction(asCScriptFunction *func) // Adjust all variable positions in the bytecode bc = func->scriptData->byteCode.AddressOf(); - for( n = 0; n < func->scriptData->byteCode.GetLength(); ) + for( n = 0; n < bcLength; ) { int c = *(asBYTE*)&bc[n]; switch( asBCInfo[c].type ) @@ -2430,7 +2549,7 @@ void asCReader::TranslateFunction(asCScriptFunction *func) // This will also make the AdjustGetOffset() function quicker as it can // receive the called function directly instead of having to search for it. bc = func->scriptData->byteCode.AddressOf(); - for( n = 0; n < func->scriptData->byteCode.GetLength(); ) + for( n = 0; n < bcLength; ) { int c = *(asBYTE*)&bc[n]; @@ -2461,8 +2580,8 @@ void asCReader::TranslateFunction(asCScriptFunction *func) CalculateStackNeeded(func); } -asCReader::SListAdjuster::SListAdjuster(asDWORD *bc, asCObjectType *listType) : - allocMemBC(bc), maxOffset(0), patternType(listType), repeatCount(0), lastOffset(-1), nextTypeId(-1) +asCReader::SListAdjuster::SListAdjuster(asCReader *rd, asDWORD *bc, asCObjectType *listType) : + reader(rd), allocMemBC(bc), maxOffset(0), patternType(listType), repeatCount(0), lastOffset(-1), nextOffset(0), nextTypeId(-1) { asASSERT( patternType && (patternType->flags & asOBJ_LIST_PATTERN) ); @@ -2472,13 +2591,13 @@ asCReader::SListAdjuster::SListAdjuster(asDWORD *bc, asCObjectType *listType) : patternNode = node->next; } -int asCReader::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatternType) +int asCReader::SListAdjuster::AdjustOffset(int offset) { - // TODO: cleanup: The listPatternType parameter is not needed - asASSERT( listPatternType == patternType ); - UNUSED_VAR( listPatternType ); - - asASSERT( offset >= lastOffset ); + if( offset < lastOffset ) + { + reader->Error(TXT_INVALID_BYTECODE_d); + return 0; + } // If it is the same offset being accessed again, just return the same adjusted value if( lastOffset == offset ) @@ -2488,7 +2607,7 @@ int asCReader::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter lastAdjustedOffset = maxOffset; // What is being expected at this position? - if( patternNode->type == asLPT_REPEAT ) + if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ) { // Align the offset to 4 bytes boundary if( maxOffset & 0x3 ) @@ -2499,6 +2618,7 @@ int asCReader::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter // Don't move the patternNode yet because the caller must make a call to SetRepeatCount too maxOffset += 4; + nextOffset = offset+1; return lastAdjustedOffset; } else if( patternNode->type == asLPT_TYPE ) @@ -2513,7 +2633,7 @@ int asCReader::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter asCDataType dt = patternType->engine->GetDataTypeFromTypeId(nextTypeId); asUINT size; - if( dt.IsObjectHandle() ) + if( dt.IsObjectHandle() || (dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_REF)) ) size = AS_PTR_SIZE*4; else size = dt.GetSizeInMemoryBytes(); @@ -2532,6 +2652,7 @@ int asCReader::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter nextTypeId = -1; maxOffset += size; + nextOffset = offset+1; return lastAdjustedOffset; } else @@ -2545,36 +2666,40 @@ int asCReader::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter // The first adjustment is for the typeId maxOffset += 4; - + nextOffset = offset+1; return lastAdjustedOffset; } } else { - if( repeatCount > 0 ) - repeatCount--; - // Determine the size of the element asUINT size; asCDataType dt = reinterpret_cast(patternNode)->dataType; - if( dt.IsObjectHandle() ) + if( dt.IsObjectHandle() || (dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_REF)) ) size = AS_PTR_SIZE*4; else size = dt.GetSizeInMemoryBytes(); - // Align the offset to 4 bytes boundary - if( size >= 4 && (maxOffset & 0x3) ) + // If values are skipped, the offset needs to be incremented + while( nextOffset <= offset ) { - maxOffset += 4 - (maxOffset & 0x3); - lastAdjustedOffset = maxOffset; - } + if( repeatCount > 0 ) + repeatCount--; - maxOffset += size; + // Align the offset to 4 bytes boundary + if( size >= 4 && (maxOffset & 0x3) ) + maxOffset += 4 - (maxOffset & 0x3); + + lastAdjustedOffset = maxOffset; + nextOffset += 1; + maxOffset += size; + } // Only move the patternNode if we're not expecting any more repeated entries if( repeatCount == 0 ) patternNode = patternNode->next; + nextOffset = offset+1; return lastAdjustedOffset; } } @@ -2589,10 +2714,16 @@ int asCReader::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter patternNode = patternNode->next; lastOffset--; - return AdjustOffset(offset, listPatternType); + return AdjustOffset(offset); } else if( patternNode->type == asLPT_END ) { + if( stack.GetLength() == 0 ) + { + reader->Error(TXT_INVALID_BYTECODE_d); + return 0; + } + SInfo info = stack.PopLast(); repeatCount = info.repeatCount; if( repeatCount ) @@ -2601,21 +2732,22 @@ int asCReader::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter patternNode = patternNode->next; lastOffset--; - return AdjustOffset(offset, listPatternType); + return AdjustOffset(offset); } else { // Something is wrong with the pattern list declaration - asASSERT( false ); + reader->Error(TXT_INVALID_BYTECODE_d); + return 0; } - return 0; + UNREACHABLE_RETURN; } void asCReader::SListAdjuster::SetRepeatCount(asUINT rc) { // Make sure the list is expecting a repeat at this location - asASSERT( patternNode->type == asLPT_REPEAT ); + asASSERT( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ); // Now move to the next patternNode patternNode = patternNode->next; @@ -3285,6 +3417,7 @@ void asCWriter::WriteFunctionSignature(asCScriptFunction *func) asBYTE b = 0; b += func->isReadOnly ? 1 : 0; b += func->isPrivate ? 2 : 0; + b += func->isProtected ? 4 : 0; WriteData(&b, 1); } else @@ -3392,8 +3525,6 @@ void asCWriter::WriteFunction(asCScriptFunction* func) } } - WriteData(&func->isShared, 1); - // Write the variable information if( !stripDebugInfo ) { @@ -3409,7 +3540,10 @@ void asCWriter::WriteFunction(asCScriptFunction* func) } } - WriteData(&func->dontCleanUpOnException, 1); + char bits = 0; + bits += func->isShared ? 1 : 0; + bits += func->dontCleanUpOnException ? 2 : 0; + WriteData(&bits,1); // Store script section name if( !stripDebugInfo ) @@ -3423,6 +3557,15 @@ void asCWriter::WriteFunction(asCScriptFunction* func) } WriteEncodedInt64(func->scriptData->declaredAt); } + + // Store the parameter names + if( !stripDebugInfo ) + { + asUINT count = asUINT(func->parameterNames.GetLength()); + WriteEncodedInt64(count); + for( asUINT n = 0; n < count; n++ ) + WriteString(&func->parameterNames[n]); + } } else if( func->funcType == asFUNC_VIRTUAL || func->funcType == asFUNC_INTERFACE ) { @@ -3487,6 +3630,7 @@ void asCWriter::WriteObjectTypeDeclaration(asCObjectType *ot, int phase) int size = (asUINT)ot->interfaces.GetLength(); WriteEncodedInt64(size); asUINT n; + asASSERT( ot->interfaces.GetLength() == ot->interfaceVFTOffsets.GetLength() ); for( n = 0; n < ot->interfaces.GetLength(); n++ ) { WriteObjectType(ot->interfaces[n]); @@ -3655,25 +3799,18 @@ void asCWriter::WriteGlobalProperty(asCGlobalProperty* prop) WriteDataType(&prop->type); // Store the initialization function - if( prop->GetInitFunc() ) - { - bool f = true; - WriteData(&f, 1); - - WriteFunction(prop->GetInitFunc()); - } - else - { - bool f = false; - WriteData(&f, 1); - } + WriteFunction(prop->GetInitFunc()); } void asCWriter::WriteObjectProperty(asCObjectProperty* prop) { WriteString(&prop->name); WriteDataType(&prop->type); - WriteData(&prop->isPrivate, 1); + int flags = 0; + if( prop->isPrivate ) flags |= 1; + if( prop->isProtected ) flags |= 2; + if( prop->isInherited ) flags |= 4; + WriteEncodedInt64(flags); } void asCWriter::WriteDataType(const asCDataType *dt) @@ -3683,31 +3820,36 @@ void asCWriter::WriteDataType(const asCDataType *dt) { if( *dt == savedDataTypes[n] ) { - asUINT c = 0; - WriteEncodedInt64(c); - WriteEncodedInt64(n); + WriteEncodedInt64(n+1); return; } } + // Indicate a new type with a null byte + asUINT c = 0; + WriteEncodedInt64(c); + // Save the new datatype savedDataTypes.PushLast(*dt); - bool b; int t = dt->GetTokenType(); WriteEncodedInt64(t); if( t == ttIdentifier ) - { WriteObjectType(dt->GetObjectType()); - b = dt->IsObjectHandle(); - WriteData(&b, 1); - b = dt->IsHandleToConst(); - WriteData(&b, 1); - } - b = dt->IsReference(); - WriteData(&b, 1); - b = dt->IsReadOnly(); - WriteData(&b, 1); + + struct + { + char isObjectHandle :1; + char isHandleToConst:1; + char isReference :1; + char isReadOnly :1; + } bits = {0}; + + bits.isObjectHandle = dt->IsObjectHandle(); + bits.isHandleToConst = dt->IsHandleToConst(); + bits.isReference = dt->IsReference(); + bits.isReadOnly = dt->IsReadOnly(); + WriteData(&bits, 1); if( t == ttIdentifier && dt->GetObjectType()->name == "_builtin_function_" ) { @@ -3736,6 +3878,7 @@ void asCWriter::WriteObjectType(asCObjectType* ot) ch = 'a'; WriteData(&ch, 1); WriteString(&ot->name); + WriteString(&ot->nameSpace->name); WriteEncodedInt64(ot->templateSubTypes.GetLength()); for( asUINT n = 0; n < ot->templateSubTypes.GetLength(); n++ ) @@ -3865,7 +4008,7 @@ void asCWriter::CalculateAdjustmentByPos(asCScriptFunction *func) } // Compute the sequence number of each bytecode instruction in order to update the jump offsets - size_t length = func->scriptData->byteCode.GetLength(); + asUINT length = func->scriptData->byteCode.GetLength(); asDWORD *bc = func->scriptData->byteCode.AddressOf(); bytecodeNbrByPos.SetLength(length); asUINT num; @@ -4174,9 +4317,9 @@ void asCWriter::WriteByteCode(asCScriptFunction *func) int offset = *(int*)(tmp+1); // Determine instruction number for next instruction and destination - int bcSeqNum = bytecodeNbrByPos[bc - startBC] + 1; + int bcSeqNum = bytecodeNbrByPos[asUINT(bc - startBC)] + 1; asDWORD *targetBC = bc + 2 + offset; - int targetBcSeqNum = bytecodeNbrByPos[targetBC - startBC]; + int targetBcSeqNum = bytecodeNbrByPos[asUINT(targetBC - startBC)]; // Set the offset in number of instructions *(int*)(tmp+1) = targetBcSeqNum - bcSeqNum; @@ -4476,7 +4619,7 @@ void asCWriter::WriteByteCode(asCScriptFunction *func) } } -asCWriter::SListAdjuster::SListAdjuster(asCObjectType *ot) : patternType(ot), repeatCount(0), entries(0), lastOffset(-1), nextTypeId(-1) +asCWriter::SListAdjuster::SListAdjuster(asCObjectType *ot) : patternType(ot), repeatCount(0), entries(0), lastOffset(-1), nextOffset(0), nextTypeId(-1) { asASSERT( ot && (ot->flags & asOBJ_LIST_PATTERN) ); @@ -4498,12 +4641,16 @@ int asCWriter::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter if( offset == lastOffset ) return entries-1; + asASSERT( offset >= nextOffset ); + + // Update last offset for next call lastOffset = offset; // What is being expected at this position? - if( patternNode->type == asLPT_REPEAT ) + if( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ) { // Don't move the patternNode yet because the caller must make a call to SetRepeatCount too + nextOffset = offset + 4; return entries++; } else if( patternNode->type == asLPT_TYPE ) @@ -4516,6 +4663,8 @@ int asCWriter::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter // we can move to the next node if( nextTypeId != -1 ) { + nextOffset = offset + 4; + if( repeatCount > 0 ) repeatCount--; @@ -4529,7 +4678,35 @@ int asCWriter::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter else { if( repeatCount > 0 ) + { + // Was any value skipped? + asUINT size; + if( dt.IsObjectHandle() || (dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_REF)) ) + size = AS_PTR_SIZE*4; + else + size = dt.GetSizeInMemoryBytes(); + + int count = 0; + while( nextOffset <= offset ) + { + count++; + nextOffset += size; + + // Align the offset on 4 byte boundaries + if( size >= 4 && (nextOffset & 0x3) ) + nextOffset += 4 - (nextOffset & 0x3); + } + + if( --count > 0 ) + { + // Skip these values + repeatCount -= count; + entries += count; + } + + nextOffset = offset + size; repeatCount--; + } // Only move the patternNode if we're not expecting any more repeated entries if( repeatCount == 0 ) @@ -4575,7 +4752,7 @@ int asCWriter::SListAdjuster::AdjustOffset(int offset, asCObjectType *listPatter void asCWriter::SListAdjuster::SetRepeatCount(asUINT rc) { // Make sure the list is expecting a repeat at this location - asASSERT( patternNode->type == asLPT_REPEAT ); + asASSERT( patternNode->type == asLPT_REPEAT || patternNode->type == asLPT_REPEAT_SAME ); // Now move to the next patternNode patternNode = patternNode->next; diff --git a/lib/angelscript/source/as_restore.h b/lib/angelscript/source/as_restore.h index 3fc3f8a40..bfd2725bc 100644 --- a/lib/angelscript/source/as_restore.h +++ b/lib/angelscript/source/as_restore.h @@ -124,9 +124,9 @@ protected: // Helper class for adjusting offsets within initialization list buffers struct SListAdjuster { - SListAdjuster(asDWORD *bc, asCObjectType *ot); + SListAdjuster(asCReader *rd, asDWORD *bc, asCObjectType *ot); void AdjustAllocMem(); - int AdjustOffset(int offset, asCObjectType *listPatternType); + int AdjustOffset(int offset); void SetRepeatCount(asUINT rc); void SetNextType(int typeId); @@ -137,11 +137,13 @@ protected: }; asCArray stack; + asCReader *reader; asDWORD *allocMemBC; asUINT maxOffset; asCObjectType *patternType; asUINT repeatCount; int lastOffset; + int nextOffset; asUINT lastAdjustedOffset; asSListPatternNode *patternNode; int nextTypeId; @@ -239,7 +241,8 @@ protected: asUINT repeatCount; asSListPatternNode *patternNode; asUINT entries; - int lastOffset; + int lastOffset; // Last offset adjusted + int nextOffset; // next expected offset to be adjusted int nextTypeId; }; asCArray listAdjusters; diff --git a/lib/angelscript/source/as_scriptengine.cpp b/lib/angelscript/source/as_scriptengine.cpp index a06571a7b..85cf0cd75 100644 --- a/lib/angelscript/source/as_scriptengine.cpp +++ b/lib/angelscript/source/as_scriptengine.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -58,7 +58,7 @@ BEGIN_AS_NAMESPACE #ifdef AS_PROFILE -// Instanciate the profiler once +// Instantiate the profiler once CProfiler g_profiler; #endif @@ -109,6 +109,15 @@ AS_API const char * asGetLibraryOptions() #ifdef AS_NO_MEMBER_INIT "AS_NO_MEMBER_INIT " #endif +#ifdef AS_NO_THISCALL_FUNCTOR_METHOD + "AS_NO_THISCALL_FUNCTOR_METHOD " +#endif +#ifdef AS_NO_EXCEPTIONS + "AS_NO_EXCEPTIONS " +#endif +#ifdef WIP_16BYTE_ALIGN + "WIP_16BYTE_ALIGN " +#endif // Target system #ifdef AS_WIN @@ -120,6 +129,9 @@ AS_API const char * asGetLibraryOptions() #ifdef AS_MAC "AS_MAC " #endif +#ifdef AS_SUN + "AS_SUN " +#endif #ifdef AS_BSD "AS_BSD " #endif @@ -138,6 +150,9 @@ AS_API const char * asGetLibraryOptions() #ifdef AS_PS3 "AS_PS3 " #endif +#ifdef AS_PSVITA + "AS_PSVITA " +#endif #ifdef AS_DC "AS_DC " #endif @@ -197,6 +212,9 @@ AS_API const char * asGetLibraryOptions() #endif #ifdef AS_X64_MSVC "AS_X64_MSVC " +#endif +#ifdef AS_SPARC + "AS_SPARC " #endif ; @@ -228,15 +246,19 @@ AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version) // Verify endianess #ifdef AS_BIG_ENDIAN - asASSERT( *(asDWORD*)"\x00\x01\x02\x03" == 0x00010203 ); - asASSERT( *(asQWORD*)"\x00\x01\x02\x03\x04\x05\x06\x07" == ((asQWORD(0x00010203)<<32)|asQWORD(0x04050607)) ); + asDWORD dw = 0x00010203; + asQWORD qw = ((asQWORD(0x00010203)<<32)|asQWORD(0x04050607)); #else - asASSERT( *(asDWORD*)"\x00\x01\x02\x03" == 0x03020100 ); + asDWORD dw = 0x03020100; // C++ didn't have a standard way of declaring 64bit literal constants until C++11, so // I'm forced to do it like this to avoid compilers warnings when compiling with the full // C++ compliance. - asASSERT( *(asQWORD*)"\x00\x01\x02\x03\x04\x05\x06\x07" == ((asQWORD(0x07060504)<<32)|asQWORD(0x03020100)) ); + asQWORD qw = ((asQWORD(0x07060504)<<32)|asQWORD(0x03020100)); #endif + asASSERT( memcmp("\x00\x01\x02\x03", &dw, 4) == 0 ); + asASSERT( memcmp("\x00\x01\x02\x03\x04\x05\x06\x07", &qw, 8) == 0 ); + UNUSED_VAR(dw); + UNUSED_VAR(qw); return asNEW(asCScriptEngine)(); } @@ -357,6 +379,25 @@ int asCScriptEngine::SetEngineProperty(asEEngineProp property, asPWORD value) ep.disallowValueAssignForRefType = value ? true : false; break; + case asEP_ALTER_SYNTAX_NAMED_ARGS: + if( value <= 2 ) + ep.alterSyntaxNamedArgs = (int)value; + else + return asINVALID_ARG; + break; + + case asEP_DISABLE_INTEGER_DIVISION: + ep.disableIntegerDivision = value ? true : false; + break; + + case asEP_DISALLOW_EMPTY_LIST_ELEMENTS: + ep.disallowEmptyListElements = value ? true : false; + break; + + case asEP_PRIVATE_PROP_AS_PROTECTED: + ep.privatePropAsProtected = value ? true : false; + break; + default: return asINVALID_ARG; } @@ -429,11 +470,23 @@ asPWORD asCScriptEngine::GetEngineProperty(asEEngineProp property) const case asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE: return ep.disallowValueAssignForRefType; + case asEP_ALTER_SYNTAX_NAMED_ARGS: + return ep.alterSyntaxNamedArgs; + + case asEP_DISABLE_INTEGER_DIVISION: + return ep.disableIntegerDivision; + + case asEP_DISALLOW_EMPTY_LIST_ELEMENTS: + return ep.disallowEmptyListElements; + + case asEP_PRIVATE_PROP_AS_PROTECTED: + return ep.privatePropAsProtected; + default: return 0; } - return 0; + UNREACHABLE_RETURN; } // interface @@ -460,6 +513,7 @@ asCScriptEngine::asCScriptEngine() asCThreadManager::Prepare(0); shuttingDown = false; + inDestructor = false; // Engine properties { @@ -471,7 +525,7 @@ asCScriptEngine::asCScriptEngine() ep.allowMultilineStrings = false; ep.allowImplicitHandleTypes = false; // TODO: optimize: Maybe this should be turned off by default? If a debugger is not used - // then this is just slowing down the execution. + // then this is just slowing down the execution. ep.buildWithoutLineCues = false; ep.initGlobalVarsAfterBuild = true; ep.requireEnumScope = false; @@ -486,6 +540,10 @@ asCScriptEngine::asCScriptEngine() ep.compilerWarnings = 1; // 0 = no warnings, 1 = warning, 2 = treat as error // TODO: 3.0.0: disallowValueAssignForRefType should be true by default ep.disallowValueAssignForRefType = false; + ep.alterSyntaxNamedArgs = 0; // 0 = no alternate syntax, 1 = accept alternate syntax but warn, 2 = accept without warning + ep.disableIntegerDivision = false; + ep.disallowEmptyListElements = false; + ep.privatePropAsProtected = false; } gc.engine = this; @@ -499,11 +557,6 @@ asCScriptEngine::asCScriptEngine() deferValidationOfTemplateTypes = false; lastModule = 0; - // User data - cleanModuleFunc = 0; - cleanContextFunc = 0; - cleanFunctionFunc = 0; - initialContextStackSize = 1024; // 4 KB (1024 * sizeof(asDWORD) @@ -513,18 +566,20 @@ asCScriptEngine::asCScriptEngine() defaultAccessMask = 1; msgCallback = 0; - jitCompiler = 0; + jitCompiler = 0; // Create the global namespace defaultNamespace = AddNameSpace(""); + requestCtxFunc = 0; + returnCtxFunc = 0; + ctxCallbackParam = 0; + // We must set the namespace in the built-in types explicitly as // this wasn't done by the default constructor. If we do not do // this we will get null pointer access in other parts of the code scriptTypeBehaviours.nameSpace = defaultNamespace; functionBehaviours.nameSpace = defaultNamespace; - objectTypeBehaviours.nameSpace = defaultNamespace; - globalPropertyBehaviours.nameSpace = defaultNamespace; // Reserve function id 0 for no function scriptFunctions.PushLast(0); @@ -549,109 +604,91 @@ asCScriptEngine::asCScriptEngine() RegisterScriptObject(this); RegisterScriptFunction(this); - RegisterObjectTypeGCBehaviours(this); - asCGlobalProperty::RegisterGCBehaviours(this); +} + +void asCScriptEngine::DeleteDiscardedModules() +{ + // TODO: 2.30.0: redesign: Prevent more than one thread from entering this function at the same time. + // If a thread is already doing the work for the clean-up the other thread should + // simply return, as the first thread will continue. + + ACQUIRESHARED(engineRWLock); + asUINT maxCount = discardedModules.GetLength(); + RELEASESHARED(engineRWLock); + + for( asUINT n = 0; n < maxCount; n++ ) + { + ACQUIRESHARED(engineRWLock); + asCModule *mod = discardedModules[n]; + RELEASESHARED(engineRWLock); + + if( !mod->HasExternalReferences(shuttingDown) ) + { + asDELETE(mod, asCModule); + n--; + } + + ACQUIRESHARED(engineRWLock); + // Determine the max count again, since another module may have been discarded during the processing + maxCount = discardedModules.GetLength(); + RELEASESHARED(engineRWLock); + } + + // Go over the list of global properties, to see if it is possible to clean + // up some variables that are no longer referred to by any functions + for( asUINT n = 0; n < globalProperties.GetLength(); n++ ) + { + asCGlobalProperty *prop = globalProperties[n]; + if( prop && prop->refCount.get() == 1 ) + RemoveGlobalProperty(prop); + } } asCScriptEngine::~asCScriptEngine() { - shuttingDown = true; + // TODO: 2.30.0: redesign: Clean up redundant code + + asUINT n = 0; + inDestructor = true; asASSERT(refCount.get() == 0); - asUINT n; - // The modules must be deleted first, as they may use - // object types from the config groups - for( n = (asUINT)scriptModules.GetLength(); n-- > 0; ) - if( scriptModules[n] ) - scriptModules[n]->Discard(); - scriptModules.SetLength(0); + // If ShutDown hasn't been called yet do it now + if( !shuttingDown ) + { + AddRef(); + ShutDownAndRelease(); + } - GarbageCollect(); + // Unravel the registered interface + if( defaultArrayObjectType ) + { + defaultArrayObjectType->ReleaseInternal(); + defaultArrayObjectType = 0; + } - // Delete the functions for template types that may references object types + // Delete the functions for generated template types that may references object types for( n = 0; n < templateInstanceTypes.GetLength(); n++ ) { + asCObjectType *templateType = templateInstanceTypes[n]; if( templateInstanceTypes[n] ) - { - asUINT f; - asCObjectType *templateType = templateInstanceTypes[n]; - - // Delete the factory stubs first - for( f = 0; f < templateType->beh.factories.GetLength(); f++ ) - scriptFunctions[templateType->beh.factories[f]]->Release(); - templateType->beh.factories.Allocate(0, false); - - // The list factory is not stored in the list with the rest of the factories - if( templateType->beh.listFactory ) - { - scriptFunctions[templateType->beh.listFactory]->Release(); - templateType->beh.listFactory = 0; - } - - // Delete the specialized functions - for( f = 1; f < templateType->beh.operators.GetLength(); f += 2 ) - { - if( scriptFunctions[templateType->beh.operators[f]]->objectType == templateType ) - { - scriptFunctions[templateType->beh.operators[f]]->Release(); - templateType->beh.operators[f] = 0; - } - } - for( f = 0; f < templateType->methods.GetLength(); f++ ) - { - if( scriptFunctions[templateType->methods[f]]->objectType == templateType ) - { - scriptFunctions[templateType->methods[f]]->Release(); - templateType->methods[f] = 0; - } - } - } + templateType->DestroyInternal(); } - - // Do one more garbage collect to free gc objects that were global variables - GarbageCollect(); - FreeUnusedGlobalProperties(); - ClearUnusedTypes(); - - // Break all relationship between remaining class types and functions - for( n = 0; n < classTypes.GetLength(); n++ ) + for( n = 0; n < listPatternTypes.GetLength(); n++ ) { - if( classTypes[n] ) - classTypes[n]->ReleaseAllFunctions(); - - if( classTypes[n]->derivedFrom ) - { - classTypes[n]->derivedFrom->Release(); - classTypes[n]->derivedFrom = 0; - } + asCObjectType *type = listPatternTypes[n]; + if( type ) + type->ReleaseInternal(); } + listPatternTypes.SetLength(0); - GarbageCollect(asGC_FULL_CYCLE); - FreeUnusedGlobalProperties(); - ClearUnusedTypes(); + // No script types must have survived + asASSERT( sharedScriptTypes.GetLength() == 0 ); - // Destroy internals of script functions that may still be kept alive outside of engine - for( n = 0; n < scriptFunctions.GetLength(); n++ ) - if( scriptFunctions[n] && scriptFunctions[n]->funcType == asFUNC_SCRIPT ) - scriptFunctions[n]->DestroyInternal(); - - // There may be instances where one more gc cycle must be run - GarbageCollect(asGC_FULL_CYCLE); - ClearUnusedTypes(); - - // If the application hasn't registered GC behaviours for all types - // that can form circular references with script types, then there - // may still be objects in the GC. - if( gc.ReportAndReleaseUndestroyedObjects() > 0 ) - { - // Some items cannot be destroyed because the application is still holding on to them - - // Make sure the script functions won't attempt to access the engine if they are destroyed later on - for( n = 0; n < scriptFunctions.GetLength(); n++ ) - if( scriptFunctions[n] && scriptFunctions[n]->funcType == asFUNC_SCRIPT ) - scriptFunctions[n]->engine = 0; - } + // It is allowed to create new references to the engine temporarily while destroying objects + // but these references must be release immediately or else something is can go wrong later on + if( refCount.get() > 0 ) + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ENGINE_REF_COUNT_ERROR_DURING_SHUTDOWN); asSMapNode *cursor = 0; while( mapTypeIdToDataType.MoveFirst(&cursor) ) @@ -675,38 +712,30 @@ asCScriptEngine::~asCScriptEngine() // Remove what is remaining defaultGroup.RemoveConfiguration(this); - asCSymbolTable::iterator it = registeredGlobalProps.List(); - for( ; it; it++ ) - (*it)->Release(); - registeredGlobalProps.Clear(); - FreeUnusedGlobalProperties(); - + // Any remaining objects in templateInstanceTypes is from generated template instances for( n = 0; n < templateInstanceTypes.GetLength(); n++ ) { + asCObjectType *templateType = templateInstanceTypes[n]; if( templateInstanceTypes[n] ) - { - // Clear the sub types before deleting the template type so that the sub types aren't freed to soon - templateInstanceTypes[n]->templateSubTypes.SetLength(0); - asDELETE(templateInstanceTypes[n],asCObjectType); - } + templateType->ReleaseInternal(); } templateInstanceTypes.SetLength(0); - asSMapNode *cursor2; - allRegisteredTypes.MoveFirst(&cursor2); - while( cursor2 ) + asCSymbolTable::iterator it = registeredGlobalProps.List(); + for( ; it; it++ ) { - // Clear the sub types before deleting the template type so that the sub types aren't freed to soon - cursor2->value->templateSubTypes.SetLength(0); - asDELETE(cursor2->value, asCObjectType); - - allRegisteredTypes.MoveNext(&cursor2, cursor2); + RemoveGlobalProperty(*it); + (*it)->Release(); } - allRegisteredTypes.EraseAll(); + registeredGlobalProps.Clear(); + for( n = 0; n < templateSubTypes.GetLength(); n++ ) { if( templateSubTypes[n] ) - asDELETE(templateSubTypes[n], asCObjectType); + { + templateSubTypes[n]->DestroyInternal(); + templateSubTypes[n]->ReleaseInternal(); + } } templateSubTypes.SetLength(0); registeredTypeDefs.SetLength(0); @@ -715,29 +744,47 @@ asCScriptEngine::~asCScriptEngine() asCSymbolTable::iterator funcIt = registeredGlobalFuncs.List(); for( ; funcIt; funcIt++ ) - (*funcIt)->Release(); + (*funcIt)->ReleaseInternal(); registeredGlobalFuncs.Clear(); scriptTypeBehaviours.ReleaseAllFunctions(); functionBehaviours.ReleaseAllFunctions(); - objectTypeBehaviours.ReleaseAllFunctions(); - globalPropertyBehaviours.ReleaseAllFunctions(); + + for( n = 0; n < scriptFunctions.GetLength(); n++ ) + if( scriptFunctions[n] ) + { + scriptFunctions[n]->DestroyInternal(); + + // Set the engine pointer to null to signal that the function is no longer part of the engine + scriptFunctions[n]->engine = 0; + } + scriptFunctions.SetLength(0); + + // Increase the internal ref count for these builtin object types, so the destructor is not called incorrectly + scriptTypeBehaviours.AddRefInternal(); + functionBehaviours.AddRefInternal(); // Destroy the funcdefs // As funcdefs are shared between modules it shouldn't be a problem to keep the objects until the engine is released - // TODO: refactor: This really should be done by ClearUnusedTypes() as soon as the funcdef is no longer is use. - // Perhaps to make it easier to manage the memory for funcdefs each function definition should - // have it's own object type. That would make the funcdef much more similar to the other types - // and could then be handled in much the same way. When this is done the funcdef should also be - // changed so that it doesn't take up a function id, i.e. don't keep a reference to it in scriptFunctions. for( n = 0; n < funcDefs.GetLength(); n++ ) if( funcDefs[n] ) { - asASSERT( funcDefs[n]->GetRefCount() == 0 ); - asDELETE(funcDefs[n], asCScriptFunction); + funcDefs[n]->DestroyInternal(); + funcDefs[n]->ReleaseInternal(); } funcDefs.SetLength(0); + // Free the global properties + for( asUINT n = 0; n < globalProperties.GetLength(); n++ ) + { + asCGlobalProperty *prop = globalProperties[n]; + if( prop ) + { + asASSERT( prop->refCount.get() == 1 ); + RemoveGlobalProperty(prop); + } + } + // Free string constants for( n = 0; n < stringConstants.GetLength(); n++ ) asDELETE(stringConstants[n],asCString); @@ -768,22 +815,114 @@ asCScriptEngine::~asCScriptEngine() asCThreadManager::Unprepare(); } -// internal -void asCScriptEngine::CleanupAfterDiscardModule() +// interface +int asCScriptEngine::SetContextCallbacks(asREQUESTCONTEXTFUNC_t requestCtx, asRETURNCONTEXTFUNC_t returnCtx, void *param) { - // Skip this when shutting down as it will be done anyway by the engine destructor - if( shuttingDown ) return; + // Both callbacks or neither must be set + if( (requestCtx == 0 && returnCtx != 0) || (requestCtx != 0 && returnCtx == 0) ) + return asINVALID_ARG; - if( ep.autoGarbageCollect ) - GarbageCollect(); + requestCtxFunc = requestCtx; + returnCtxFunc = returnCtx; + ctxCallbackParam = param; - FreeUnusedGlobalProperties(); - ClearUnusedTypes(); + return 0; +} + +// interface +asIScriptContext *asCScriptEngine::RequestContext() +{ + if( requestCtxFunc ) + { + // The return callback must also exist + asASSERT( returnCtxFunc ); + + asIScriptContext *ctx = requestCtxFunc(this, ctxCallbackParam); + return ctx; + } + + // As fallback we create a new context + return CreateContext(); +} + +// internal +asCModule *asCScriptEngine::FindNewOwnerForSharedType(asCObjectType *type, asCModule *mod) +{ + asASSERT( type->IsShared() ); + + if( type->module != mod ) + return type->module; + + for( asUINT n = 0; n < scriptModules.GetLength(); n++ ) + { + // TODO: optimize: If the modules already stored the shared types separately, this would be quicker + int foundIdx = -1; + asCModule *mod = scriptModules[n]; + if( mod == type->module ) continue; + if( type->flags & asOBJ_ENUM ) + foundIdx = mod->enumTypes.IndexOf(type); + else if( type->flags & asOBJ_TYPEDEF ) + foundIdx = mod->typeDefs.IndexOf(type); + else + foundIdx = mod->classTypes.IndexOf(type); + + if( foundIdx >= 0 ) + { + type->module = mod; + break; + } + } + + return type->module; +} + +// internal +asCModule *asCScriptEngine::FindNewOwnerForSharedFunc(asCScriptFunction *func, asCModule *mod) +{ + asASSERT( func->IsShared() ); + + if( func->module != mod ) + return func->module; + + for( asUINT n = 0; n < scriptModules.GetLength(); n++ ) + { + // TODO: optimize: If the modules already stored the shared types separately, this would be quicker + int foundIdx = -1; + asCModule *mod = scriptModules[n]; + if( mod == func->module ) continue; + if( func->funcType == asFUNC_FUNCDEF ) + foundIdx = mod->funcDefs.IndexOf(func); + else + foundIdx = mod->scriptFunctions.IndexOf(func); + + if( foundIdx >= 0 ) + { + func->module = mod; + break; + } + } + + return func->module; +} + +// interface +void asCScriptEngine::ReturnContext(asIScriptContext *ctx) +{ + if( returnCtxFunc ) + { + returnCtxFunc(this, ctx, ctxCallbackParam); + return; + } + + // As fallback we just release the context + if( ctx ) + ctx->Release(); } // interface int asCScriptEngine::AddRef() const { + asASSERT( refCount.get() > 0 || inDestructor ); return refCount.atomicInc(); } @@ -794,13 +933,56 @@ int asCScriptEngine::Release() const if( r == 0 ) { - asDELETE(const_cast(this),asCScriptEngine); + // It is possible that some function will temporarily increment the engine ref count + // during clean-up for example while destroying the objects in the garbage collector. + if( !inDestructor ) + asDELETE(const_cast(this),asCScriptEngine); return 0; } return r; } +// interface +int asCScriptEngine::ShutDownAndRelease() +{ + // Do a full garbage collection cycle to clean up any object that may still hold on to the engine + GarbageCollect(); + + // Set the flag that the engine is being shutdown now. This will speed up + // the process, and will also allow the engine to warn about invalid calls + shuttingDown = true; + + // Clear the context callbacks. If new context's are needed for the clean-up the engine will take care of this itself. + // Context callbacks are normally used for pooling contexts, and if we allow new contexts to be created without being + // immediately destroyed afterwards it means the engine's refcount will increase. This is turn may cause memory access + // violations later on when the pool releases its contexts. + SetContextCallbacks(0, 0, 0); + + // The modules must be deleted first, as they may use + // object types from the config groups + for( asUINT n = (asUINT)scriptModules.GetLength(); n-- > 0; ) + if( scriptModules[n] ) + scriptModules[n]->Discard(); + scriptModules.SetLength(0); + + // Do another full garbage collection to destroy the object types/functions + // that may have been placed in the gc when destroying the modules + GarbageCollect(); + + // Do another sweep to delete discarded modules, that may not have + // been deleted earlier due to still having external references + DeleteDiscardedModules(); + + // If the application hasn't registered GC behaviours for all types + // that can form circular references with script types, then there + // may still be objects in the GC. + gc.ReportAndReleaseUndestroyedObjects(); + + // Release the engine reference + return Release(); +} + // internal asSNameSpace *asCScriptEngine::AddNameSpace(const char *name) { @@ -822,7 +1004,7 @@ asSNameSpace *asCScriptEngine::AddNameSpace(const char *name) } // internal -asSNameSpace *asCScriptEngine::FindNameSpace(const char *name) +asSNameSpace *asCScriptEngine::FindNameSpace(const char *name) const { // TODO: optimize: Improve linear search for( asUINT n = 0; n < nameSpaces.GetLength(); n++ ) @@ -930,7 +1112,7 @@ int asCScriptEngine::SetMessageCallback(const asSFuncPtr &callback, void *obj, a msgCallback = true; msgCallbackObj = obj; bool isObj = false; - if( (unsigned)callConv == asCALL_GENERIC ) + if( (unsigned)callConv == asCALL_GENERIC || (unsigned)callConv == asCALL_THISCALL_OBJFIRST || (unsigned)callConv == asCALL_THISCALL_OBJLAST ) { msgCallback = false; return asNOT_SUPPORTED; @@ -968,6 +1150,25 @@ int asCScriptEngine::WriteMessage(const char *section, int row, int col, asEMsgT if( !msgCallback ) return 0; + // If a pre-message has been set, then write that first + if( preMessage.isSet ) + { + asSMessageInfo msg; + msg.section = preMessage.scriptname.AddressOf(); + msg.row = preMessage.r; + msg.col = preMessage.c; + msg.type = asMSGTYPE_INFORMATION; + msg.message = preMessage.message.AddressOf(); + + if( msgCallbackFunc.callConv < ICC_THISCALL ) + CallGlobalFunction(&msg, msgCallbackObj, &msgCallbackFunc, 0); + else + CallObjectMethod(msgCallbackObj, &msg, &msgCallbackFunc, 0); + + preMessage.isSet = false; + } + + // Write the message to the callback asSMessageInfo msg; msg.section = section; msg.row = row; @@ -985,17 +1186,17 @@ int asCScriptEngine::WriteMessage(const char *section, int row, int col, asEMsgT int asCScriptEngine::SetJITCompiler(asIJITCompiler *compiler) { - jitCompiler = compiler; - return asSUCCESS; + jitCompiler = compiler; + return asSUCCESS; } asIJITCompiler *asCScriptEngine::GetJITCompiler() const { - return jitCompiler; + return jitCompiler; } // interface -asETokenClass asCScriptEngine::ParseToken(const char *string, size_t stringLength, int *tokenLength) const +asETokenClass asCScriptEngine::ParseToken(const char *string, size_t stringLength, asUINT *tokenLength) const { if( stringLength == 0 ) stringLength = strlen(string); @@ -1005,7 +1206,7 @@ asETokenClass asCScriptEngine::ParseToken(const char *string, size_t stringLengt tok.GetToken(string, stringLength, &len, &tc); if( tokenLength ) - *tokenLength = (int)len; + *tokenLength = (asUINT)len; return tc; } @@ -1043,151 +1244,32 @@ int asCScriptEngine::DiscardModule(const char *module) // interface asUINT asCScriptEngine::GetModuleCount() const { - return asUINT(scriptModules.GetLength()); + ACQUIRESHARED(engineRWLock); + asUINT length = asUINT(scriptModules.GetLength()); + RELEASESHARED(engineRWLock); + return length; } // interface asIScriptModule *asCScriptEngine::GetModuleByIndex(asUINT index) const { - if( index >= scriptModules.GetLength() ) - return 0; - - return scriptModules[index]; + asIScriptModule *mod = 0; + ACQUIRESHARED(engineRWLock); + if( index < scriptModules.GetLength() ) + mod = scriptModules[index]; + RELEASESHARED(engineRWLock); + return mod; } -// internal -int asCScriptEngine::ClearUnusedTypes() -{ - int clearCount = 0; - - // Build a list of all types to check for - asCArray types; - types = classTypes; - types.Concatenate(generatedTemplateTypes); - - // Go through all modules - asUINT n; - for( n = 0; n < scriptModules.GetLength() && types.GetLength(); n++ ) - { - asCModule *mod = scriptModules[n]; - if( mod ) - { - // Functions/Methods/Globals are handled after this - - // Go through all type declarations - asUINT m; - for( m = 0; m < mod->classTypes.GetLength() && types.GetLength(); m++ ) - RemoveTypeAndRelatedFromList(types, mod->classTypes[m]); - for( m = 0; m < mod->enumTypes.GetLength() && types.GetLength(); m++ ) - RemoveTypeAndRelatedFromList(types, mod->enumTypes[m]); - for( m = 0; m < mod->typeDefs.GetLength() && types.GetLength(); m++ ) - RemoveTypeAndRelatedFromList(types, mod->typeDefs[m]); - } - } - - // Go through all function parameters and remove used types - for( n = 0; n < scriptFunctions.GetLength() && types.GetLength(); n++ ) - { - asCScriptFunction *func = scriptFunctions[n]; - if( func ) - { - // Ignore factory stubs - if( func->name == "factstub" ) - continue; - - // Ignore funcdefs because these will only be destroyed when the engine is released - if( func->funcType == asFUNC_FUNCDEF ) - continue; - - asCObjectType *ot = func->returnType.GetObjectType(); - if( ot != 0 && ot != func->objectType ) - if( func->name != ot->name ) - RemoveTypeAndRelatedFromList(types, ot); - - for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) - { - ot = func->parameterTypes[p].GetObjectType(); - if( ot != 0 && ot != func->objectType ) - if( func->name != ot->name ) - RemoveTypeAndRelatedFromList(types, ot); - } - } - } - - // Go through all global properties - for( n = 0; n < globalProperties.GetLength() && types.GetLength(); n++ ) - { - if( globalProperties[n] && globalProperties[n]->type.GetObjectType() ) - RemoveTypeAndRelatedFromList(types, globalProperties[n]->type.GetObjectType()); - } - - // All that remains in the list after this can be discarded, since they are no longer used - for(;;) - { - bool didClearTemplateInstanceType = false; - - for( n = 0; n < types.GetLength(); n++ ) - { - int refCount = 0; - asCObjectType *type = types[n]; - - // Template types and script classes will have two references for each factory stub - if( (type->flags & asOBJ_TEMPLATE) ) - { - refCount = 2*(int)type->beh.factories.GetLength(); - if( type->beh.listFactory ) - refCount += 2; - } - - if( type->GetRefCount() == refCount ) - { - if( type->flags & asOBJ_TEMPLATE ) - { - didClearTemplateInstanceType = true; - RemoveTemplateInstanceType(type); - clearCount++; - } - else - { - RemoveFromTypeIdMap(type); - asDELETE(type,asCObjectType); - clearCount++; - - classTypes.RemoveIndexUnordered(classTypes.IndexOf(type)); - } - - // Remove the type from the array - types.RemoveIndexUnordered(n); - n--; - } - } - - if( didClearTemplateInstanceType == false ) - break; - } - - // Clear the list pattern types that are no longer used - for( n = 0; n < listPatternTypes.GetLength(); n++ ) - { - if( listPatternTypes[n]->refCount.get() == 0 ) - { - asDELETE(listPatternTypes[n], asCObjectType); - listPatternTypes.RemoveIndexUnordered(n); - n--; - } - } - - return clearCount; -} - -// internal -void asCScriptEngine::RemoveTypeAndRelatedFromList(asCArray &types, asCObjectType *ot) +// Internal +void asCScriptEngine::RemoveTypeAndRelatedFromList(asCMap &types, asCObjectType *ot) { // Remove the type from the list - int i = types.IndexOf(ot); - if( i == -1 ) return; + asSMapNode* node; + if( !types.MoveTo(&node, ot) ) + return; - types.RemoveIndexUnordered(i); + types.Erase(node); // If the type is an template type then remove all sub types as well for( asUINT n = 0; n < ot->templateSubTypes.GetLength(); n++ ) @@ -1204,14 +1286,13 @@ void asCScriptEngine::RemoveTypeAndRelatedFromList(asCArray &typ } } - // internal int asCScriptEngine::GetFactoryIdByDecl(const asCObjectType *ot, const char *decl) { asCModule *mod = 0; // Is this a script class? - if( ot->flags & asOBJ_SCRIPT_OBJECT && ot->size > 0 ) + if( (ot->flags & asOBJ_SCRIPT_OBJECT) && ot->size > 0 ) mod = scriptFunctions[ot->beh.factories[0]]->module; asCBuilder bld(this, mod); @@ -1226,7 +1307,7 @@ int asCScriptEngine::GetFactoryIdByDecl(const asCObjectType *ot, const char *dec // Search for matching factory function int id = -1; - for( size_t n = 0; n < ot->beh.factories.GetLength(); n++ ) + for( asUINT n = 0; n < ot->beh.factories.GetLength(); n++ ) { asCScriptFunction *f = scriptFunctions[ot->beh.factories[n]]; if( f->IsSignatureEqual(&func) ) @@ -1255,6 +1336,7 @@ int asCScriptEngine::GetMethodIdByDecl(const asCObjectType *ot, const char *decl // Set the object type so that the signature can be properly compared // This cast is OK, it will only be used for comparison func.objectType = const_cast(ot); + func.objectType->AddRefInternal(); int r = bld.ParseFunctionDeclaration(func.objectType, decl, &func, false); if( r < 0 ) @@ -1262,7 +1344,7 @@ int asCScriptEngine::GetMethodIdByDecl(const asCObjectType *ot, const char *decl // Search script functions for matching interface int id = -1; - for( size_t n = 0; n < ot->methods.GetLength(); ++n ) + for( asUINT n = 0; n < ot->methods.GetLength(); ++n ) { if( func.IsSignatureEqual(scriptFunctions[ot->methods[n]]) ) { @@ -1332,6 +1414,10 @@ int asCScriptEngine::RegisterObjectProperty(const char *obj, const char *declara if( r < 0 ) return ConfigError(r, "RegisterObjectProperty", obj, declaration); + // Don't allow modifying generated template instances + if( dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_TEMPLATE) && generatedTemplateTypes.Exists(dt.GetObjectType()) ) + return ConfigError(asINVALID_TYPE, "RegisterObjectProperty", obj, declaration); + // Verify that the correct config group is used if( currentGroup->FindType(dt.GetObjectType()->name.AddressOf()) == 0 ) return ConfigError(asWRONG_CONFIG_GROUP, "RegisterObjectProperty", obj, declaration); @@ -1348,8 +1434,8 @@ int asCScriptEngine::RegisterObjectProperty(const char *obj, const char *declara // The VM currently only supports 16bit offsets // TODO: The VM needs to have support for 32bit offsets. Probably with a second ADDSi instruction - // However, when implementing this it is necessary for the bytecode serialization to support - // the switch between the instructions upon loading bytecode as the offset may not be the + // However, when implementing this it is necessary for the bytecode serialization to support + // the switch between the instructions upon loading bytecode as the offset may not be the // same on all platforms if( byteOffset > 32767 || byteOffset < -32768 ) return ConfigError(asINVALID_ARG, "RegisterObjectProperty", obj, declaration); @@ -1358,25 +1444,26 @@ int asCScriptEngine::RegisterObjectProperty(const char *obj, const char *declara if( prop == 0 ) return ConfigError(asOUT_OF_MEMORY, "RegisterObjectProperty", obj, declaration); - prop->name = name; - prop->type = type; - prop->byteOffset = byteOffset; - prop->isPrivate = false; - prop->accessMask = defaultAccessMask; + prop->name = name; + prop->type = type; + prop->byteOffset = byteOffset; + prop->isPrivate = false; + prop->isProtected = false; + prop->accessMask = defaultAccessMask; dt.GetObjectType()->properties.PushLast(prop); - // Add references to template instances so they are not released too early - if( type.GetObjectType() && (type.GetObjectType()->flags & asOBJ_TEMPLATE) ) + // Add references to types so they are not released too early + if( type.GetObjectType() ) { - if( !currentGroup->objTypes.Exists(type.GetObjectType()) ) - { - type.GetObjectType()->AddRef(); + type.GetObjectType()->AddRefInternal(); + + // Add template instances to the config group + if( (type.GetObjectType()->flags & asOBJ_TEMPLATE) && !currentGroup->objTypes.Exists(type.GetObjectType()) ) currentGroup->objTypes.PushLast(type.GetObjectType()); - } } - currentGroup->RefConfigGroup(FindConfigGroupForObjectType(type.GetObjectType())); + currentGroup->AddReferencesForType(this, type.GetObjectType()); return asSUCCESS; } @@ -1387,7 +1474,6 @@ int asCScriptEngine::RegisterInterface(const char *name) if( name == 0 ) return ConfigError(asINVALID_NAME, "RegisterInterface", 0, 0); // Verify if the name has been registered as a type already - // TODO: Must check against registered funcdefs too if( GetRegisteredObjectType(name, defaultNamespace) ) return asALREADY_REGISTERED; @@ -1397,7 +1483,13 @@ int asCScriptEngine::RegisterInterface(const char *name) bool oldMsgCallback = msgCallback; msgCallback = false; int r = bld.ParseDataType(name, &dt, defaultNamespace); msgCallback = oldMsgCallback; - if( r >= 0 ) return ConfigError(asERROR, "RegisterInterface", name, 0); + if( r >= 0 ) + { + // If it is not in the defaultNamespace then the type was successfully parsed because + // it is declared in a parent namespace which shouldn't be treated as an error + if( dt.GetObjectType() && dt.GetObjectType()->nameSpace == defaultNamespace ) + return ConfigError(asERROR, "RegisterInterface", name, 0); + } // Make sure the name is not a reserved keyword size_t tokenLen; @@ -1418,16 +1510,16 @@ int asCScriptEngine::RegisterInterface(const char *name) return ConfigError(asOUT_OF_MEMORY, "RegisterInterface", name, 0); st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT | asOBJ_SHARED; - st->size = 0; // Cannot be instanciated + st->size = 0; // Cannot be instantiated st->name = name; st->nameSpace = defaultNamespace; // Use the default script class behaviours st->beh.factory = 0; st->beh.addref = scriptTypeBehaviours.beh.addref; - scriptFunctions[st->beh.addref]->AddRef(); + scriptFunctions[st->beh.addref]->AddRefInternal(); st->beh.release = scriptTypeBehaviours.beh.release; - scriptFunctions[st->beh.release]->AddRef(); + scriptFunctions[st->beh.release]->AddRefInternal(); st->beh.copy = 0; allRegisteredTypes.Insert(asSNameSpaceNamePair(st->nameSpace, st->name), st); @@ -1456,10 +1548,12 @@ int asCScriptEngine::RegisterInterfaceMethod(const char *intf, const char *decla return ConfigError(asOUT_OF_MEMORY, "RegisterInterfaceMethod", intf, declaration); func->objectType = dt.GetObjectType(); + func->objectType->AddRefInternal(); r = bld.ParseFunctionDeclaration(func->objectType, declaration, func, false); if( r < 0 ) { + func->funcType = asFUNC_DUMMY; asDELETE(func,asCScriptFunction); return ConfigError(asINVALID_DECLARATION, "RegisterInterfaceMethod", intf, declaration); } @@ -1468,12 +1562,13 @@ int asCScriptEngine::RegisterInterfaceMethod(const char *intf, const char *decla r = bld.CheckNameConflictMember(dt.GetObjectType(), func->name.AddressOf(), 0, 0, false); if( r < 0 ) { + func->funcType = asFUNC_DUMMY; asDELETE(func,asCScriptFunction); return ConfigError(asNAME_TAKEN, "RegisterInterfaceMethod", intf, declaration); } func->id = GetNextScriptFunctionId(); - SetScriptFunction(func); + AddScriptFunction(func); // The index into the interface's vftable chunk should be // its index in the methods array. @@ -1483,21 +1578,7 @@ int asCScriptEngine::RegisterInterfaceMethod(const char *intf, const char *decla func->ComputeSignatureId(); - // If parameter type from other groups are used, add references - // TODO: The code for adding references to config groups is repeated in a lot of places - if( func->returnType.GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType()); - currentGroup->RefConfigGroup(group); - } - for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) - { - if( func->parameterTypes[n].GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType()); - currentGroup->RefConfigGroup(group); - } - } + currentGroup->AddReferencesForFunc(this, func); // Return function id as success return func->id; @@ -1514,79 +1595,85 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD if( flags & asOBJ_REF ) { // Can optionally have the asOBJ_GC, asOBJ_NOHANDLE, asOBJ_SCOPED, or asOBJ_TEMPLATE flag set, but nothing else - if( flags & ~(asOBJ_REF | asOBJ_GC | asOBJ_NOHANDLE | asOBJ_SCOPED | asOBJ_TEMPLATE | asOBJ_NOCOUNT) ) + if( flags & ~(asOBJ_REF | asOBJ_GC | asOBJ_NOHANDLE | asOBJ_SCOPED | asOBJ_TEMPLATE | asOBJ_NOCOUNT | asOBJ_IMPLICIT_HANDLE) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); // flags are exclusive if( (flags & asOBJ_GC) && (flags & (asOBJ_NOHANDLE|asOBJ_SCOPED|asOBJ_NOCOUNT)) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); - if( (flags & asOBJ_NOHANDLE) && (flags & (asOBJ_GC|asOBJ_SCOPED|asOBJ_NOCOUNT)) ) + if( (flags & asOBJ_NOHANDLE) && (flags & (asOBJ_GC|asOBJ_SCOPED|asOBJ_NOCOUNT|asOBJ_IMPLICIT_HANDLE)) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); - if( (flags & asOBJ_SCOPED) && (flags & (asOBJ_GC|asOBJ_NOHANDLE|asOBJ_NOCOUNT)) ) + if( (flags & asOBJ_SCOPED) && (flags & (asOBJ_GC|asOBJ_NOHANDLE|asOBJ_NOCOUNT|asOBJ_IMPLICIT_HANDLE)) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); if( (flags & asOBJ_NOCOUNT) && (flags & (asOBJ_GC|asOBJ_NOHANDLE|asOBJ_SCOPED)) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + + // Implicit handle is only allowed if the engine property for this is turned on + if( !ep.allowImplicitHandleTypes && (flags & asOBJ_IMPLICIT_HANDLE) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); } else if( flags & asOBJ_VALUE ) { // Cannot use reference flags - // TODO: template: Should be possible to register a value type as template type - if( flags & (asOBJ_REF | asOBJ_GC | asOBJ_NOHANDLE | asOBJ_SCOPED | asOBJ_TEMPLATE | asOBJ_NOCOUNT) ) + if( flags & (asOBJ_REF | asOBJ_GC | asOBJ_NOHANDLE | asOBJ_SCOPED | asOBJ_NOCOUNT | asOBJ_IMPLICIT_HANDLE) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); - // flags are exclusive - if( (flags & asOBJ_POD) && (flags & asOBJ_ASHANDLE) ) + // Flags are exclusive + if( (flags & asOBJ_POD) && (flags & (asOBJ_ASHANDLE | asOBJ_TEMPLATE)) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); // If the app type is given, we must validate the flags if( flags & asOBJ_APP_CLASS ) { // Must not set the primitive or float flag - if( flags & (asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT) ) + if( flags & (asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_ARRAY) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); } - else if( flags & asOBJ_APP_PRIMITIVE ) + else { - // Must not set the class flags nor the float flag - if( flags & (asOBJ_APP_CLASS | - asOBJ_APP_CLASS_CONSTRUCTOR | + // Must not set the class properties, without the class flag + if( flags & (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_CLASS_COPY_CONSTRUCTOR | - asOBJ_APP_FLOAT | asOBJ_APP_CLASS_ALLINTS | asOBJ_APP_CLASS_ALLFLOATS) ) + { + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + } + } + + if( flags & asOBJ_APP_PRIMITIVE ) + { + if( flags & (asOBJ_APP_CLASS | + asOBJ_APP_FLOAT | + asOBJ_APP_ARRAY) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); } else if( flags & asOBJ_APP_FLOAT ) { - // Must not set the class flags nor the primitive flag - if( flags & (asOBJ_APP_CLASS | - asOBJ_APP_CLASS_CONSTRUCTOR | - asOBJ_APP_CLASS_DESTRUCTOR | - asOBJ_APP_CLASS_ASSIGNMENT | - asOBJ_APP_CLASS_COPY_CONSTRUCTOR | - asOBJ_APP_PRIMITIVE | - asOBJ_APP_CLASS_ALLINTS | - asOBJ_APP_CLASS_ALLFLOATS) ) + if( flags & (asOBJ_APP_CLASS | + asOBJ_APP_PRIMITIVE | + asOBJ_APP_ARRAY) ) return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); } - else if( flags & (asOBJ_APP_CLASS_CONSTRUCTOR | - asOBJ_APP_CLASS_DESTRUCTOR | - asOBJ_APP_CLASS_ASSIGNMENT | - asOBJ_APP_CLASS_COPY_CONSTRUCTOR | - asOBJ_APP_CLASS_ALLINTS | - asOBJ_APP_CLASS_ALLFLOATS) ) + else if( flags & asOBJ_APP_ARRAY ) { - // Must not set the class properties, without the class flag - return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); + if( flags & (asOBJ_APP_CLASS | + asOBJ_APP_PRIMITIVE | + asOBJ_APP_FLOAT) ) + return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); } } else return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); // Don't allow anything else than the defined flags +#ifndef WIP_16BYTE_ALIGN if( flags - (flags & asOBJ_MASK_VALID_FLAGS) ) +#else + if( flags - (flags & (asOBJ_MASK_VALID_FLAGS | asOBJ_APP_ALIGN16)) ) +#endif return ConfigError(asINVALID_ARG, "RegisterObjectType", name, 0); // Value types must have a defined size @@ -1610,7 +1697,6 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD return ConfigError(r, "RegisterObjectType", name, 0); // Verify that the template name hasn't been registered as a type already - // TODO: Must check against registered funcdefs too if( GetRegisteredObjectType(typeName, defaultNamespace) ) // This is not an irrepairable error, as it may just be that the same type is registered twice return asALREADY_REGISTERED; @@ -1622,6 +1708,10 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD type->name = typeName; type->nameSpace = defaultNamespace; type->size = byteSize; +#ifdef WIP_16BYTE_ALIGN + // TODO: Types smaller than 4 don't need to be aligned to 4 byte boundaries + type->alignment = (flags & asOBJ_APP_ALIGN16) ? 16 : 4; +#endif type->flags = flags; type->accessMask = defaultAccessMask; @@ -1652,12 +1742,14 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD subtype->name = subtypeNames[subTypeIdx]; subtype->size = 0; +#ifdef WIP_16BYTE_ALIGN + type->alignment = 0; // template subtypes cannot be instantiated and don't need alignment +#endif subtype->flags = asOBJ_TEMPLATE_SUBTYPE; templateSubTypes.PushLast(subtype); - subtype->AddRef(); } type->templateSubTypes.PushLast(asCDataType::CreateObject(subtype, false)); - subtype->AddRef(); + subtype->AddRefInternal(); } } else @@ -1665,7 +1757,6 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD typeName = name; // Verify if the name has been registered as a type already - // TODO: Must check against registered funcdefs too if( GetRegisteredObjectType(typeName, defaultNamespace) ) // This is not an irrepairable error, as it may just be that the same type is registered twice return asALREADY_REGISTERED; @@ -1682,8 +1773,9 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD // Keep the most recent template generated instance type, so we know what it was before parsing the datatype asCObjectType *mostRecentTemplateInstanceType = 0; - if( generatedTemplateTypes.GetLength() ) - mostRecentTemplateInstanceType = generatedTemplateTypes[generatedTemplateTypes.GetLength()-1]; + asUINT originalSizeOfGeneratedTemplateTypes = (asUINT)generatedTemplateTypes.GetLength(); + if( originalSizeOfGeneratedTemplateTypes ) + mostRecentTemplateInstanceType = generatedTemplateTypes[originalSizeOfGeneratedTemplateTypes-1]; // Use builder to parse the datatype asCDataType dt; @@ -1716,6 +1808,10 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD type->name = typeName; type->nameSpace = defaultNamespace; type->size = byteSize; +#ifdef WIP_16BYTE_ALIGN + // TODO: Types smaller than 4 don't need to be aligned to 4 byte boundaries + type->alignment = (flags & asOBJ_APP_ALIGN16) ? 16 : 4; +#endif type->flags = flags; type->accessMask = defaultAccessMask; @@ -1739,14 +1835,17 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD dt.IsReference() ) return ConfigError(asINVALID_TYPE, "RegisterObjectType", name, 0); - // Was the template instance type created before? - if( (generatedTemplateTypes.GetLength() && - generatedTemplateTypes[generatedTemplateTypes.GetLength()-1] == mostRecentTemplateInstanceType) || - mostRecentTemplateInstanceType == dt.GetObjectType() ) - // TODO: Should have a better error message + // Was the template instance type generated before? + if( generatedTemplateTypes.Exists(dt.GetObjectType()) && + generatedTemplateTypes[generatedTemplateTypes.GetLength()-1] == mostRecentTemplateInstanceType ) + { + asCString str; + str.Format(TXT_TEMPLATE_s_ALREADY_GENERATED_CANT_REGISTER, typeName.AddressOf()); + WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); return ConfigError(asNOT_SUPPORTED, "RegisterObjectType", name, 0); + } - // If this is not a template instance type, then it means it is an + // If this is not a generated template instance type, then it means it is an // already registered template specialization if( !generatedTemplateTypes.Exists(dt.GetObjectType()) ) return ConfigError(asALREADY_REGISTERED, "RegisterObjectType", name, 0); @@ -1764,10 +1863,15 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD type->name = dt.GetObjectType()->name; // The namespace will be the same as the original template type type->nameSpace = dt.GetObjectType()->nameSpace; - // TODO: template: Support multiple subtypes type->templateSubTypes.PushLast(dt.GetSubType()); - if( type->templateSubTypes[0].GetObjectType() ) type->templateSubTypes[0].GetObjectType()->AddRef(); + for( asUINT s = 0; s < type->templateSubTypes.GetLength(); s++ ) + if( type->templateSubTypes[s].GetObjectType() ) + type->templateSubTypes[s].GetObjectType()->AddRefInternal(); type->size = byteSize; +#ifdef WIP_16BYTE_ALIGN + // TODO: Types smaller than 4 don't need to be aligned to 4 byte boundaries + type->alignment = (flags & asOBJ_APP_ALIGN16) ? 16 : 4; +#endif type->flags = flags; type->accessMask = defaultAccessMask; @@ -1776,7 +1880,10 @@ int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD currentGroup->objTypes.PushLast(type); // Remove the template instance type, which will no longer be used. - RemoveTemplateInstanceType(dt.GetObjectType()); + // It is possible that multiple template instances are generated if + // they have any relationship, so all of them must be removed + while( generatedTemplateTypes.GetLength() > originalSizeOfGeneratedTemplateTypes ) + RemoveTemplateInstanceType(generatedTemplateTypes[generatedTemplateTypes.GetLength()-1]); } } @@ -1804,49 +1911,52 @@ int asCScriptEngine::RegisterObjectBehaviour(const char *datatype, asEBehaviours // Don't allow application to modify built-in types if( type.GetObjectType() == &functionBehaviours || - type.GetObjectType() == &objectTypeBehaviours || - type.GetObjectType() == &globalPropertyBehaviours || type.GetObjectType() == &scriptTypeBehaviours ) return ConfigError(asINVALID_TYPE, "RegisterObjectBehaviour", datatype, decl); if( type.IsReadOnly() || type.IsReference() ) return ConfigError(asINVALID_TYPE, "RegisterObjectBehaviour", datatype, decl); + // Don't allow modifying generated template instances + if( type.GetObjectType() && (type.GetObjectType()->flags & asOBJ_TEMPLATE) && generatedTemplateTypes.Exists(type.GetObjectType()) ) + return ConfigError(asINVALID_TYPE, "RegisterObjectBehaviour", datatype, decl); + return RegisterBehaviourToObjectType(type.GetObjectType(), behaviour, decl, funcPointer, callConv, objForThiscall); } // internal int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall) { - asSSystemFunctionInterface internal; - if( behaviour == asBEHAVE_FACTORY || - behaviour == asBEHAVE_LIST_FACTORY || - behaviour == asBEHAVE_TEMPLATE_CALLBACK ) - { #ifdef AS_MAX_PORTABILITY - if( callConv != asCALL_GENERIC ) - return ConfigError(asNOT_SUPPORTED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); -#endif - int r = DetectCallingConvention(false, funcPointer, callConv, objForThiscall, &internal); - if( r < 0 ) - return ConfigError(r, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - } - else - { -#ifdef AS_MAX_PORTABILITY - if( callConv != asCALL_GENERIC ) - return ConfigError(asNOT_SUPPORTED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); -#else - if( callConv != asCALL_THISCALL && - callConv != asCALL_CDECL_OBJLAST && - callConv != asCALL_CDECL_OBJFIRST && - callConv != asCALL_GENERIC ) - return ConfigError(asNOT_SUPPORTED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); #endif - int r = DetectCallingConvention(true, funcPointer, callConv, objForThiscall, &internal); - if( r < 0 ) - return ConfigError(r, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + asSSystemFunctionInterface internal; + bool isMethod = !(behaviour == asBEHAVE_FACTORY || + behaviour == asBEHAVE_LIST_FACTORY || + behaviour == asBEHAVE_TEMPLATE_CALLBACK); + int r = DetectCallingConvention(isMethod, funcPointer, callConv, objForThiscall, &internal); + if( r < 0 ) + return ConfigError(r, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + + // TODO: cleanup: This is identical to what is in RegisterMethodToObjectType + // If the object type is a template, make sure there are no generated instances already + if( objectType->flags & asOBJ_TEMPLATE ) + { + for( asUINT n = 0; n < generatedTemplateTypes.GetLength(); n++ ) + { + asCObjectType *tmpl = generatedTemplateTypes[n]; + if( tmpl->name == objectType->name && + tmpl->nameSpace == objectType->nameSpace && + !(tmpl->templateSubTypes[0].GetObjectType() && (tmpl->templateSubTypes[0].GetObjectType()->flags & asOBJ_TEMPLATE_SUBTYPE)) ) + { + asCString msg; + msg.Format(TXT_TEMPLATE_s_ALREADY_GENERATED_CANT_REGISTER, asCDataType::CreateObject(tmpl, false).Format(tmpl->nameSpace).AddressOf()); + WriteMessage("",0,0, asMSGTYPE_ERROR, msg.AddressOf()); + return ConfigError(asERROR, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + } } isPrepared = false; @@ -1859,7 +1969,7 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as bool expectListPattern = behaviour == asBEHAVE_LIST_FACTORY || behaviour == asBEHAVE_LIST_CONSTRUCT; asCScriptNode *listPattern = 0; asCBuilder bld(this, 0); - int r = bld.ParseFunctionDeclaration(objectType, decl, &func, true, &internal.paramAutoHandles, &internal.returnAutoHandle, 0, expectListPattern ? &listPattern : 0); + r = bld.ParseFunctionDeclaration(objectType, decl, &func, true, &internal.paramAutoHandles, &internal.returnAutoHandle, 0, expectListPattern ? &listPattern : 0); if( r < 0 ) { if( listPattern ) @@ -1869,8 +1979,12 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as func.name.Format("_beh_%d_", behaviour); if( behaviour != asBEHAVE_FACTORY && behaviour != asBEHAVE_LIST_FACTORY ) + { func.objectType = objectType; + func.objectType->AddRefInternal(); + } + // TODO: cleanup: This is identical to what is in RegisterMethodToObjectType // Check if the method restricts that use of the template to value types or reference types if( objectType->flags & asOBJ_TEMPLATE ) { @@ -1882,6 +1996,10 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as objectType->acceptValueSubType = false; else if( !func.returnType.IsReference() ) objectType->acceptRefSubType = false; + + // Can't support template subtypes by value, since each type is treated differently in the ABI + if( !func.returnType.IsObjectHandle() && !func.returnType.IsReference() ) + return ConfigError(asNOT_SUPPORTED, "RegisterObjectMethod", objectType->name.AddressOf(), decl); } for( asUINT n = 0; n < func.parameterTypes.GetLength(); n++ ) @@ -1893,6 +2011,10 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as objectType->acceptValueSubType = false; else if( !func.parameterTypes[n].IsReference() ) objectType->acceptRefSubType = false; + + // Can't support template subtypes by value, since each type is treated differently in the ABI + if( !func.parameterTypes[n].IsObjectHandle() && !func.parameterTypes[n].IsReference() ) + return ConfigError(asNOT_SUPPORTED, "RegisterObjectMethod", objectType->name.AddressOf(), decl); } } } @@ -1911,7 +2033,7 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as beh->construct = AddBehaviourFunction(func, internal); beh->factory = beh->construct; - scriptFunctions[beh->factory]->AddRef(); + scriptFunctions[beh->factory]->AddRefInternal(); beh->constructors.PushLast(beh->construct); beh->factories.PushLast(beh->factory); func.id = beh->construct; @@ -1925,12 +2047,22 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); } + // The templates take a hidden parameter with the object type + if( (objectType->flags & asOBJ_TEMPLATE) && + (func.parameterTypes.GetLength() == 0 || + !func.parameterTypes[0].IsReference()) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_FIRST_PARAM_MUST_BE_REF_FOR_TEMPLATE_FACTORY); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + // TODO: Verify that the same constructor hasn't been registered already // Store all constructors in a list func.id = AddBehaviourFunction(func, internal); beh->constructors.PushLast(func.id); - if( func.parameterTypes.GetLength() == 0 ) + if( func.parameterTypes.GetLength() == 0 || + (func.parameterTypes.GetLength() == 1 && (objectType->flags & asOBJ_TEMPLATE)) ) { beh->construct = func.id; } @@ -2055,6 +2187,33 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); } + if( behaviour == asBEHAVE_LIST_FACTORY ) + { + // Make sure the factory takes a reference as its last parameter + if( objectType->flags & asOBJ_TEMPLATE ) + { + if( func.parameterTypes.GetLength() != 2 || !func.parameterTypes[1].IsReference() ) + { + if( listPattern ) + listPattern->Destroy(this); + + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_TEMPLATE_LIST_FACTORY_EXPECTS_2_REF_PARAMS); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + } + else + { + if( func.parameterTypes.GetLength() != 1 || !func.parameterTypes[0].IsReference() ) + { + if( listPattern ) + listPattern->Destroy(this); + + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_LIST_FACTORY_EXPECTS_1_REF_PARAM); + return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); + } + } + } + // TODO: Verify that the same factory function hasn't been registered already // Don't accept duplicates @@ -2085,30 +2244,6 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as { beh->listFactory = func.id; - // Make sure the factory takes a reference as its last parameter - if( objectType->flags & asOBJ_TEMPLATE ) - { - if( func.parameterTypes.GetLength() != 2 || !func.parameterTypes[1].IsReference() ) - { - if( listPattern ) - listPattern->Destroy(this); - - WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_TEMPLATE_LIST_FACTORY_EXPECTS_2_REF_PARAMS); - return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - } - } - else - { - if( func.parameterTypes.GetLength() != 1 || !func.parameterTypes[0].IsReference() ) - { - if( listPattern ) - listPattern->Destroy(this); - - WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_LIST_FACTORY_EXPECTS_1_REF_PARAM); - return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - } - } - // Store the list pattern for this function int r = scriptFunctions[func.id]->RegisterListPattern(decl, listPattern); @@ -2254,26 +2389,32 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as else if( behaviour == asBEHAVE_RELEASEREFS ) func.id = beh->gcReleaseAllReferences = AddBehaviourFunction(func, internal); } +#ifdef AS_DEPRECATED + // Deprecated since 2.30.0. 2014-10-24 else if( behaviour == asBEHAVE_IMPLICIT_VALUE_CAST || behaviour == asBEHAVE_VALUE_CAST ) { - // Verify parameter count - if( func.parameterTypes.GetLength() != 0 ) + // There are two allowed signatures + // 1. type f() + // 2. void f(?&out) + + if( !(func.parameterTypes.GetLength() == 1 && func.parameterTypes[0].GetTokenType() == ttQuestion && func.inOutFlags[0] == asTM_OUTREF && func.returnType.GetTokenType() == ttVoid) && + !(func.parameterTypes.GetLength() == 0 && func.returnType.GetTokenType() != ttVoid) ) return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - // Verify return type + // It is not allowed to implement a value cast to bool if( func.returnType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, false)) ) return ConfigError(asNOT_SUPPORTED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - if( func.returnType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttVoid, false)) ) - return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - - // TODO: verify that the same cast is not registered already (const or non-const is treated the same for the return type) - - beh->operators.PushLast(behaviour); - func.id = AddBehaviourFunction(func, internal); - beh->operators.PushLast(func.id); + asCString decl; + decl += func.returnType.Format(defaultNamespace); + decl += behaviour == asBEHAVE_VALUE_CAST ? " opConv(" : " opImplConv("; + if( func.parameterTypes.GetLength() ) + decl += "?&out"; + decl += ")"; + func.id = RegisterMethodToObjectType(objectType, decl.AddressOf(), funcPointer, callConv, objForThiscall); } + // Deprecated since 2.30.0, 2014-12-30 else if( behaviour == asBEHAVE_REF_CAST || behaviour == asBEHAVE_IMPLICIT_REF_CAST ) { @@ -2289,41 +2430,17 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as if( func.IsReadOnly() ) return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - // Verify that the same cast is not registered already - // (const or non-const is treated the same for the return type) - if( func.parameterTypes.GetLength() == 1 ) - { - // Check for existing behaviour with ?&out - for( asUINT n = 0; n < beh->operators.GetLength(); n+= 2 ) - { - if( beh->operators[n] == asBEHAVE_REF_CAST || - beh->operators[n] == asBEHAVE_IMPLICIT_REF_CAST ) - { - asCScriptFunction *f = scriptFunctions[beh->operators[n+1]]; - if( f->parameterTypes.GetLength() == 1 ) - return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - } - } - } - else - { - // Check for existing behaviour with same return type - for( asUINT n = 0; n < beh->operators.GetLength(); n+= 2 ) - { - if( beh->operators[n] == asBEHAVE_REF_CAST || - beh->operators[n] == asBEHAVE_IMPLICIT_REF_CAST ) - { - asCScriptFunction *f = scriptFunctions[beh->operators[n+1]]; - if( f->returnType.GetObjectType() == func.returnType.GetObjectType() ) - return ConfigError(asALREADY_REGISTERED, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); - } - } - } - - beh->operators.PushLast(behaviour); - func.id = AddBehaviourFunction(func, internal); - beh->operators.PushLast(func.id); + asCString decl; + decl += func.returnType.Format(defaultNamespace); + if( internal.returnAutoHandle ) + decl += "+"; + decl += behaviour == asBEHAVE_REF_CAST ? " opCast(" : " opImplCast("; + if( func.parameterTypes.GetLength() ) + decl += "?&out"; + decl += ")"; + func.id = RegisterMethodToObjectType(objectType, decl.AddressOf(), funcPointer, callConv, objForThiscall); } +#endif else if ( behaviour == asBEHAVE_GET_WEAKREF_FLAG ) { // This behaviour is only allowed for reference types @@ -2403,6 +2520,8 @@ int asCScriptEngine::AddBehaviourFunction(asCScriptFunction &func, asSSystemFunc f->sysFuncIntf = newInterface; f->returnType = func.returnType; f->objectType = func.objectType; + if( f->objectType ) + f->objectType->AddRefInternal(); f->id = id; f->isReadOnly = func.isReadOnly; f->accessMask = defaultAccessMask; @@ -2414,22 +2533,10 @@ int asCScriptEngine::AddBehaviourFunction(asCScriptFunction &func, asSSystemFunc else f->defaultArgs.PushLast(0); - SetScriptFunction(f); + AddScriptFunction(f); // If parameter type from other groups are used, add references - if( f->returnType.GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(f->returnType.GetObjectType()); - currentGroup->RefConfigGroup(group); - } - for( n = 0; n < f->parameterTypes.GetLength(); n++ ) - { - if( f->parameterTypes[n].GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(f->parameterTypes[n].GetObjectType()); - currentGroup->RefConfigGroup(group); - } - } + currentGroup->AddReferencesForFunc(this, f); return id; } @@ -2464,14 +2571,10 @@ int asCScriptEngine::RegisterGlobalProperty(const char *declaration, void *point varAddressMap.Insert(prop->GetAddressOfValue(), prop); registeredGlobalProps.Put(prop); + prop->AddRef(); currentGroup->globalProps.PushLast(prop); - // If from another group add reference - if( type.GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(type.GetObjectType()); - currentGroup->RefConfigGroup(group); - } + currentGroup->AddReferencesForType(this, type.GetObjectType()); return asSUCCESS; } @@ -2500,23 +2603,21 @@ asCGlobalProperty *asCScriptEngine::AllocateGlobalProperty() } // internal -void asCScriptEngine::FreeUnusedGlobalProperties() +void asCScriptEngine::RemoveGlobalProperty(asCGlobalProperty *prop) { - for( asUINT n = 0; n < globalProperties.GetLength(); n++ ) + int index = globalProperties.IndexOf(prop); + if( index >= 0 ) { - if( globalProperties[n] && globalProperties[n]->GetRefCount() == 0 ) - { - freeGlobalPropertyIds.PushLast(n); + freeGlobalPropertyIds.PushLast(index); + globalProperties[index] = 0; - asSMapNode *node; - varAddressMap.MoveTo(&node, globalProperties[n]->GetAddressOfValue()); - asASSERT(node); - if( node ) - varAddressMap.Erase(node); + asSMapNode *node; + varAddressMap.MoveTo(&node, prop->GetAddressOfValue()); + asASSERT(node); + if( node ) + varAddressMap.Erase(node); - asDELETE(globalProperties[n], asCGlobalProperty); - globalProperties[n] = 0; - } + prop->Release(); } } @@ -2556,11 +2657,20 @@ int asCScriptEngine::GetGlobalPropertyByIndex(asUINT index, const char **name, c // interface int asCScriptEngine::GetGlobalPropertyIndexByName(const char *name) const { - // Find the global var id - int id = registeredGlobalProps.GetFirstIndex(defaultNamespace, name); - if( id == -1 ) return asNO_GLOBAL_VAR; + asSNameSpace *ns = defaultNamespace; - return id; + // Find the global var id + while( ns ) + { + int id = registeredGlobalProps.GetFirstIndex(ns, name); + if( id >= 0 ) + return id; + + // Recursively search parent namespace + ns = GetParentNameSpace(ns); + } + + return asNO_GLOBAL_VAR; } // interface @@ -2580,15 +2690,20 @@ int asCScriptEngine::GetGlobalPropertyIndexByDecl(const char *decl) const return r; // Search for a match - int id = registeredGlobalProps.GetFirstIndex(ns, name, asCCompGlobPropType(dt)); - if (id < 0) - return asNO_GLOBAL_VAR; + while( ns ) + { + int id = registeredGlobalProps.GetFirstIndex(ns, name, asCCompGlobPropType(dt)); + if( id >= 0 ) + return id; - return id; + ns = GetParentNameSpace(ns); + } + + return asNO_GLOBAL_VAR; } // interface -int asCScriptEngine::RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) +int asCScriptEngine::RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall) { if( obj == 0 ) return ConfigError(asINVALID_ARG, "RegisterObjectMethod", obj, declaration); @@ -2605,34 +2720,48 @@ int asCScriptEngine::RegisterObjectMethod(const char *obj, const char *declarati // Don't allow application to modify built-in types if( dt.GetObjectType() == &functionBehaviours || - dt.GetObjectType() == &objectTypeBehaviours || - dt.GetObjectType() == &globalPropertyBehaviours || dt.GetObjectType() == &scriptTypeBehaviours ) return ConfigError(asINVALID_ARG, "RegisterObjectMethod", obj, declaration); - return RegisterMethodToObjectType(dt.GetObjectType(), declaration, funcPointer, callConv); + // Don't allow modifying generated template instances + if( dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_TEMPLATE) && generatedTemplateTypes.Exists(dt.GetObjectType()) ) + return ConfigError(asINVALID_TYPE, "RegisterObjectMethod", obj, declaration); + + return RegisterMethodToObjectType(dt.GetObjectType(), declaration, funcPointer, callConv, objForThiscall); } // internal -int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) +int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall) { - asSSystemFunctionInterface internal; - int r = DetectCallingConvention(true, funcPointer, callConv, 0, &internal); - if( r < 0 ) - return ConfigError(r, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); - - // We only support these calling conventions for object methods #ifdef AS_MAX_PORTABILITY if( callConv != asCALL_GENERIC ) return ConfigError(asNOT_SUPPORTED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); -#else - if( callConv != asCALL_THISCALL && - callConv != asCALL_CDECL_OBJLAST && - callConv != asCALL_CDECL_OBJFIRST && - callConv != asCALL_GENERIC ) - return ConfigError(asNOT_SUPPORTED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); #endif + asSSystemFunctionInterface internal; + int r = DetectCallingConvention(true, funcPointer, callConv, objForThiscall, &internal); + if( r < 0 ) + return ConfigError(r, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + + // TODO: cleanup: This is identical to what is in RegisterMethodToObjectType + // If the object type is a template, make sure there are no generated instances already + if( objectType->flags & asOBJ_TEMPLATE ) + { + for( asUINT n = 0; n < generatedTemplateTypes.GetLength(); n++ ) + { + asCObjectType *tmpl = generatedTemplateTypes[n]; + if( tmpl->name == objectType->name && + tmpl->nameSpace == objectType->nameSpace && + !(tmpl->templateSubTypes[0].GetObjectType() && (tmpl->templateSubTypes[0].GetObjectType()->flags & asOBJ_TEMPLATE_SUBTYPE)) ) + { + asCString msg; + msg.Format(TXT_TEMPLATE_s_ALREADY_GENERATED_CANT_REGISTER, asCDataType::CreateObject(tmpl, false).Format(tmpl->nameSpace).AddressOf()); + WriteMessage("",0,0, asMSGTYPE_ERROR, msg.AddressOf()); + return ConfigError(asERROR, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } + } + } + isPrepared = false; // Put the system function in the list of system functions @@ -2649,6 +2778,7 @@ int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const func->sysFuncIntf = newInterface; func->objectType = objectType; + func->objectType->AddRefInternal(); asCBuilder bld(this, 0); r = bld.ParseFunctionDeclaration(func->objectType, declaration, func, true, &newInterface->paramAutoHandles, &newInterface->returnAutoHandle); @@ -2671,38 +2801,43 @@ int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const // Check against duplicate methods asUINT n; - for( n = 0; n < func->objectType->methods.GetLength(); n++ ) + if( func->name == "opConv" || func->name == "opImplConv" || func->name == "opCast" || func->name == "opImplCast" ) { - asCScriptFunction *f = scriptFunctions[func->objectType->methods[n]]; - if( f->name == func->name && - f->IsSignatureExceptNameAndReturnTypeEqual(func) ) + // opConv and opCast are special methods that the compiler differentiates between by the return type + for( n = 0; n < func->objectType->methods.GetLength(); n++ ) { - func->funcType = asFUNC_DUMMY; - asDELETE(func,asCScriptFunction); - return ConfigError(asALREADY_REGISTERED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + asCScriptFunction *f = scriptFunctions[func->objectType->methods[n]]; + if( f->name == func->name && + f->IsSignatureExceptNameEqual(func) ) + { + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asALREADY_REGISTERED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } + } + } + else + { + for( n = 0; n < func->objectType->methods.GetLength(); n++ ) + { + asCScriptFunction *f = scriptFunctions[func->objectType->methods[n]]; + if( f->name == func->name && + f->IsSignatureExceptNameAndReturnTypeEqual(func) ) + { + func->funcType = asFUNC_DUMMY; + asDELETE(func,asCScriptFunction); + return ConfigError(asALREADY_REGISTERED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); + } } } func->id = GetNextScriptFunctionId(); func->objectType->methods.PushLast(func->id); func->accessMask = defaultAccessMask; - SetScriptFunction(func); + AddScriptFunction(func); - // TODO: This code is repeated in many places // If parameter type from other groups are used, add references - if( func->returnType.GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType()); - currentGroup->RefConfigGroup(group); - } - for( n = 0; n < func->parameterTypes.GetLength(); n++ ) - { - if( func->parameterTypes[n].GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType()); - currentGroup->RefConfigGroup(group); - } - } + currentGroup->AddReferencesForFunc(this, func); // Check if the method restricts that use of the template to value types or reference types if( func->objectType->flags & asOBJ_TEMPLATE ) @@ -2715,6 +2850,10 @@ int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const func->objectType->acceptValueSubType = false; else if( !func->returnType.IsReference() ) func->objectType->acceptRefSubType = false; + + // Can't support template subtypes by value, since each type is treated differently in the ABI + if( !func->returnType.IsObjectHandle() && !func->returnType.IsReference() ) + return ConfigError(asNOT_SUPPORTED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); } for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) @@ -2726,6 +2865,10 @@ int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const func->objectType->acceptValueSubType = false; else if( !func->parameterTypes[n].IsReference() ) func->objectType->acceptRefSubType = false; + + // Can't support template subtypes by value, since each type is treated differently in the ABI + if( !func->parameterTypes[n].IsObjectHandle() && !func->parameterTypes[n].IsReference() ) + return ConfigError(asNOT_SUPPORTED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); } } } @@ -2734,13 +2877,13 @@ int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const // TODO: beh.copy member will be removed, so this is not necessary // Is this the default copy behaviour? if( func->name == "opAssign" && func->parameterTypes.GetLength() == 1 && func->isReadOnly == false && - (objectType->flags & asOBJ_SCRIPT_OBJECT || func->parameterTypes[0].IsEqualExceptRefAndConst(asCDataType::CreateObject(func->objectType, false))) ) + ((objectType->flags & asOBJ_SCRIPT_OBJECT) || func->parameterTypes[0].IsEqualExceptRefAndConst(asCDataType::CreateObject(func->objectType, false))) ) { if( func->objectType->beh.copy != 0 ) return ConfigError(asALREADY_REGISTERED, "RegisterObjectMethod", objectType->name.AddressOf(), declaration); func->objectType->beh.copy = func->id; - func->AddRef(); + func->AddRefInternal(); } // Return the function id as success @@ -2750,22 +2893,16 @@ int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const // interface int asCScriptEngine::RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall) { +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED, "RegisterGlobalFunction", declaration, 0); +#endif + asSSystemFunctionInterface internal; int r = DetectCallingConvention(false, funcPointer, callConv, objForThiscall, &internal); if( r < 0 ) return ConfigError(r, "RegisterGlobalFunction", declaration, 0); -#ifdef AS_MAX_PORTABILITY - if( callConv != asCALL_GENERIC ) - return ConfigError(asNOT_SUPPORTED, "RegisterGlobalFunction", declaration, 0); -#else - if( callConv != asCALL_CDECL && - callConv != asCALL_STDCALL && - callConv != asCALL_THISCALL_ASGLOBAL && - callConv != asCALL_GENERIC ) - return ConfigError(asNOT_SUPPORTED, "RegisterGlobalFunction", declaration, 0); -#endif - isPrepared = false; // Put the system function in the list of system functions @@ -2820,26 +2957,14 @@ int asCScriptEngine::RegisterGlobalFunction(const char *declaration, const asSFu } func->id = GetNextScriptFunctionId(); - SetScriptFunction(func); + AddScriptFunction(func); currentGroup->scriptFunctions.PushLast(func); func->accessMask = defaultAccessMask; registeredGlobalFuncs.Put(func); // If parameter type from other groups are used, add references - if( func->returnType.GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType()); - currentGroup->RefConfigGroup(group); - } - for( n = 0; n < func->parameterTypes.GetLength(); n++ ) - { - if( func->parameterTypes[n].GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType()); - currentGroup->RefConfigGroup(group); - } - } + currentGroup->AddReferencesForFunc(this, func); // Return the function id as success return func->id; @@ -2877,39 +3002,49 @@ asIScriptFunction *asCScriptEngine::GetGlobalFunctionByDecl(const char *decl) co if( r < 0 ) return 0; + asSNameSpace *ns = defaultNamespace; // Search script functions for matching interface - asIScriptFunction *f = 0; - const asCArray &idxs = registeredGlobalFuncs.GetIndexes(defaultNamespace, func.name); - for( unsigned int n = 0; n < idxs.GetLength(); n++ ) + while( ns ) { - const asCScriptFunction *funcPtr = registeredGlobalFuncs.Get(idxs[n]); - if( funcPtr->objectType == 0 && - func.returnType == funcPtr->returnType && - func.parameterTypes.GetLength() == funcPtr->parameterTypes.GetLength() - ) + asIScriptFunction *f = 0; + const asCArray &idxs = registeredGlobalFuncs.GetIndexes(ns, func.name); + for( unsigned int n = 0; n < idxs.GetLength(); n++ ) { - bool match = true; - for( size_t p = 0; p < func.parameterTypes.GetLength(); ++p ) + const asCScriptFunction *funcPtr = registeredGlobalFuncs.Get(idxs[n]); + if( funcPtr->objectType == 0 && + func.returnType == funcPtr->returnType && + func.parameterTypes.GetLength() == funcPtr->parameterTypes.GetLength() + ) { - if( func.parameterTypes[p] != funcPtr->parameterTypes[p] ) + bool match = true; + for( asUINT p = 0; p < func.parameterTypes.GetLength(); ++p ) { - match = false; - break; + if( func.parameterTypes[p] != funcPtr->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( f == 0 ) + f = const_cast(funcPtr); + else + // Multiple functions + return 0; } } - - if( match ) - { - if( f == 0 ) - f = const_cast(funcPtr); - else - // Multiple functions - return 0; - } } + + if( f ) + return f; + + // Recursively search parent namespaces + ns = GetParentNameSpace(ns); } - return f; + return 0; } @@ -3049,7 +3184,7 @@ int asCScriptEngine::RegisterDefaultArrayType(const char *type) return asINVALID_TYPE; defaultArrayObjectType = dt.GetObjectType(); - defaultArrayObjectType->AddRef(); + defaultArrayObjectType->AddRefInternal(); return 0; } @@ -3117,7 +3252,7 @@ int asCScriptEngine::RegisterStringFactory(const char *datatype, const asSFuncPt func->parameterTypes.PushLast(parm1); func->inOutFlags.PushLast(asTM_INREF); func->id = GetNextScriptFunctionId(); - SetScriptFunction(func); + AddScriptFunction(func); stringFactory = func; @@ -3133,49 +3268,62 @@ int asCScriptEngine::RegisterStringFactory(const char *datatype, const asSFuncPt } // interface -int asCScriptEngine::GetStringFactoryReturnTypeId() const +int asCScriptEngine::GetStringFactoryReturnTypeId(asDWORD *flags) const { if( stringFactory == 0 ) return asNO_FUNCTION; - return GetTypeIdFromDataType(stringFactory->returnType); + return stringFactory->GetReturnTypeId(flags); } -// interface -asCModule *asCScriptEngine::GetModule(const char *_name, bool create) +// internal +asCModule *asCScriptEngine::GetModule(const char *name, bool create) { // Accept null as well as zero-length string - const char *name = ""; - if( _name != 0 ) name = _name; + if( name == 0 ) name = ""; + asCModule *retModule = 0; + + ACQUIRESHARED(engineRWLock); if( lastModule && lastModule->name == name ) - return lastModule; + retModule = lastModule; + else + { + // TODO: optimize: Improve linear search + for( asUINT n = 0; n < scriptModules.GetLength(); ++n ) + if( scriptModules[n] && scriptModules[n]->name == name ) + { + retModule = scriptModules[n]; + break; + } + } + RELEASESHARED(engineRWLock); - // TODO: optimize: Improve linear search - for( asUINT n = 0; n < scriptModules.GetLength(); ++n ) - if( scriptModules[n] && scriptModules[n]->name == name ) - { - lastModule = scriptModules[n]; - return lastModule; - } + if( retModule ) + { + ACQUIREEXCLUSIVE(engineRWLock); + lastModule = retModule; + RELEASEEXCLUSIVE(engineRWLock); + + return retModule; + } if( create ) { - asCModule *module = asNEW(asCModule)(name, this); - if( module == 0 ) + retModule = asNEW(asCModule)(name, this); + if( retModule == 0 ) { // Out of memory return 0; } - scriptModules.PushLast(module); - - lastModule = module; - - return lastModule; + ACQUIREEXCLUSIVE(engineRWLock); + scriptModules.PushLast(retModule); + lastModule = retModule; + RELEASEEXCLUSIVE(engineRWLock); } - return 0; + return retModule; } asCModule *asCScriptEngine::GetModuleFromFuncId(int id) @@ -3213,110 +3361,60 @@ void asCScriptEngine::BuildCompleted() void asCScriptEngine::RemoveTemplateInstanceType(asCObjectType *t) { - int n; + // If there is a module that still owns the generated type, then don't remove it + if( t->module ) + return; - // Destroy the factory stubs - for( n = 0; n < (int)t->beh.factories.GetLength(); n++ ) - { - // Make sure the factory stub isn't referencing this object anymore - scriptFunctions[t->beh.factories[n]]->ReleaseAllHandles(this); - scriptFunctions[t->beh.factories[n]]->Release(); - } - t->beh.factories.SetLength(0); + // Don't remove it if there are external refernces + if( t->externalRefCount.get() ) + return; - // Destroy the stub for the list factory too - if( t->beh.listFactory ) - { - scriptFunctions[t->beh.listFactory]->ReleaseAllHandles(this); - scriptFunctions[t->beh.listFactory]->Release(); - t->beh.listFactory = 0; - } + // Only remove the template instance type if no config group is using it + if( defaultGroup.generatedTemplateInstances.Exists(t) ) + return; + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + if( configGroups[n]->generatedTemplateInstances.Exists(t) ) + return; - // Destroy the specialized functions - for( n = 1; n < (int)t->beh.operators.GetLength(); n += 2 ) - { - if( t->beh.operators[n] && scriptFunctions[t->beh.operators[n]]->objectType == t ) - { - scriptFunctions[t->beh.operators[n]]->Release(); - } - } - t->beh.operators.SetLength(0); - - // Start searching from the end of the list, as most of - // the time it will be the last two types - for( n = (int)templateInstanceTypes.GetLength()-1; n >= 0; n-- ) - { - if( templateInstanceTypes[n] == t ) - { - if( n == (signed)templateInstanceTypes.GetLength()-1 ) - templateInstanceTypes.PopLast(); - else - templateInstanceTypes[n] = templateInstanceTypes.PopLast(); - } - } - - for( n = (int)generatedTemplateTypes.GetLength()-1; n >= 0; n-- ) - { - if( generatedTemplateTypes[n] == t ) - { - if( n == (signed)generatedTemplateTypes.GetLength()-1 ) - generatedTemplateTypes.PopLast(); - else - generatedTemplateTypes[n] = generatedTemplateTypes.PopLast(); - } - } - - asDELETE(t,asCObjectType); + t->DestroyInternal(); + templateInstanceTypes.RemoveValue(t); + generatedTemplateTypes.RemoveValue(t); + t->ReleaseInternal(); } // internal -void asCScriptEngine::OrphanTemplateInstances(asCObjectType *subType) -{ - for( asUINT n = 0; n < templateInstanceTypes.GetLength(); n++ ) - { - asCObjectType *type = templateInstanceTypes[n]; - - if( type == 0 ) - continue; - - // If the template type isn't owned by any module it can't be orphaned - if( type->module == 0 ) - continue; - - for( asUINT subTypeIdx = 0; subTypeIdx < type->templateSubTypes.GetLength(); subTypeIdx++ ) - { - if( type->templateSubTypes[subTypeIdx].GetObjectType() == subType ) - { - // Tell the GC that the template type exists so it can resolve potential circular references - gc.AddScriptObjectToGC(type, &objectTypeBehaviours); - - // Clear the module - type->module = 0; - type->Release(); - - // Do a recursive check for other template instances - OrphanTemplateInstances(type); - - // Break out so we don't add the same template to - // the gc again if another subtype matches this one - break; - } - } - } -} - -// internal -asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateType, asCArray &subTypes) +asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateType, asCArray &subTypes, asCModule *requestingModule) { asUINT n; // Is there any template instance type or template specialization already with this subtype? for( n = 0; n < templateInstanceTypes.GetLength(); n++ ) { - if( templateInstanceTypes[n] && - templateInstanceTypes[n]->name == templateType->name && - templateInstanceTypes[n]->templateSubTypes == subTypes ) + asCObjectType *type = templateInstanceTypes[n]; + if( type && + type->name == templateType->name && + type->templateSubTypes == subTypes ) + { + // If the template instance is generated, then the module should hold a reference + // to it so the config group can determine see that the template type is in use. + // Template specializations will be treated as normal types + if( requestingModule && generatedTemplateTypes.Exists(type) ) + { + if( type->module == 0 ) + { + // Set the ownership of this template type + // It may be without ownership if it was previously created from application with for example GetObjectTypeByDecl + type->module = requestingModule; + } + if( !requestingModule->templateInstances.Exists(type) ) + { + requestingModule->templateInstances.PushLast(type); + type->AddRefInternal(); + } + } + return templateInstanceTypes[n]; + } } // No previous template instance exists @@ -3343,18 +3441,33 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT ot->flags = templateType->flags; ot->size = templateType->size; ot->name = templateType->name; + ot->nameSpace = templateType->nameSpace; - // The template instance type will inherit the same module as the subType - // This will allow the module to orphan the template instance types afterwards - for( n = 0; n < subTypes.GetLength(); n++ ) + // If the template is being requested from a module, then the module should hold a reference to the type + if( requestingModule ) { - if( subTypes[n].GetObjectType() ) + // Set the ownership of this template type + ot->module = requestingModule; + requestingModule->templateInstances.PushLast(ot); + ot->AddRefInternal(); + } + else + { + // If the template type is not requested directly from a module, then set the ownership + // of it to the same module as one of the subtypes. If none of the subtypes are owned by] + // any module, the template instance will be without ownership and can be removed from the + // engine at any time (unless the application holds an external reference). + for( n = 0; n < subTypes.GetLength(); n++ ) { - ot->module = subTypes[n].GetObjectType()->module; - if( ot->module ) + if( subTypes[n].GetObjectType() ) { - ot->AddRef(); - break; + ot->module = subTypes[n].GetObjectType()->module; + if( ot->module ) + { + ot->module->templateInstances.PushLast(ot); + ot->AddRefInternal(); + break; + } } } } @@ -3362,42 +3475,67 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT // Before filling in the methods, call the template instance callback behaviour to validate the type if( templateType->beh.templateCallback ) { - bool dontGarbageCollect = false; - - asCScriptFunction *callback = scriptFunctions[templateType->beh.templateCallback]; - if( !CallGlobalFunctionRetBool(ot, &dontGarbageCollect, callback->sysFuncIntf, callback) ) + // If the validation is deferred then the validation will be done later, + // so it is necessary to continue the preparation of the template instance type + if( !deferValidationOfTemplateTypes ) { - // If the validation is deferred then the validation will be done later, - // so it is necessary to continue the preparation of the template instance type - if( !deferValidationOfTemplateTypes ) + asCScriptFunction *callback = scriptFunctions[templateType->beh.templateCallback]; + + bool dontGarbageCollect = false; + if( !CallGlobalFunctionRetBool(ot, &dontGarbageCollect, callback->sysFuncIntf, callback) ) { - // The type cannot be instanciated + // The type cannot be instantiated ot->templateSubTypes.SetLength(0); - asDELETE(ot, asCObjectType); + if( ot->module ) + { + ot->module->templateInstances.RemoveValue(ot); + ot->ReleaseInternal(); + } + ot->ReleaseInternal(); return 0; } + + // If the callback said this template instance won't be garbage collected then remove the flag + if( dontGarbageCollect ) + ot->flags &= ~asOBJ_GC; } - // If the callback said this template instance won't be garbage collected then remove the flag - if( dontGarbageCollect ) - ot->flags &= ~asOBJ_GC; - ot->beh.templateCallback = templateType->beh.templateCallback; - scriptFunctions[ot->beh.templateCallback]->AddRef(); + scriptFunctions[ot->beh.templateCallback]->AddRefInternal(); } ot->methods = templateType->methods; for( n = 0; n < ot->methods.GetLength(); n++ ) - scriptFunctions[ot->methods[n]]->AddRef(); + scriptFunctions[ot->methods[n]]->AddRefInternal(); - // Store the real factory in the constructor. This is used by the CreateScriptObject function. - // Otherwise it wouldn't be necessary to store the real factory ids. - ot->beh.construct = templateType->beh.factory; - ot->beh.constructors = templateType->beh.factories; + if( templateType->flags & asOBJ_REF ) + { + // Store the real factory in the constructor. This is used by the CreateScriptObject function. + // Otherwise it wouldn't be necessary to store the real factory ids. + ot->beh.construct = templateType->beh.factory; + ot->beh.constructors = templateType->beh.factories; + } + else + { + ot->beh.construct = templateType->beh.construct; + ot->beh.constructors = templateType->beh.constructors; + } for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) - scriptFunctions[ot->beh.constructors[n]]->AddRef(); + scriptFunctions[ot->beh.constructors[n]]->AddRefInternal(); - // As the new template type is instanciated the engine should + + // Before proceeding with the generation of the template functions for the template instance it is necessary + // to include the new template instance type in the list of known types, otherwise it is possible that we get + // a infinite recursive loop as the template instance type is requested again during the generation of the + // template functions. + templateInstanceTypes.PushLast(ot); + + // Store the template instance types that have been created automatically by the engine from a template type + // The object types in templateInstanceTypes that are not also in generatedTemplateTypes are registered template specializations + generatedTemplateTypes.PushLast(ot); + + + // As the new template type is instantiated the engine should // generate new functions to substitute the ones with the template subtype. for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) { @@ -3407,7 +3545,7 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT if( GenerateNewTemplateFunction(templateType, ot, func, &func) ) { // Release the old function, the new one already has its ref count set to 1 - scriptFunctions[funcId]->Release(); + scriptFunctions[funcId]->ReleaseInternal(); ot->beh.constructors[n] = func->id; if( ot->beh.construct == funcId ) @@ -3417,17 +3555,35 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT ot->beh.factory = 0; - // Generate factory stubs for each of the factories - for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) + if( templateType->flags & asOBJ_REF ) { - asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, ot->beh.constructors[n]); + // Generate factory stubs for each of the factories + for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) + { + asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, ot->beh.constructors[n]); - // The function's refCount was already initialized to 1 - ot->beh.factories.PushLast(func->id); + ot->beh.factories.PushLast(func->id); - // Set the default factory as well - if( ot->beh.constructors[n] == ot->beh.construct ) - ot->beh.factory = func->id; + // Set the default factory as well + if( ot->beh.constructors[n] == ot->beh.construct ) + ot->beh.factory = func->id; + } + } + else + { + // Generate factory stubs for each of the constructors + for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) + { + asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, ot->beh.constructors[n]); + + if( ot->beh.constructors[n] == ot->beh.construct ) + ot->beh.construct = func->id; + + // Release previous constructor + scriptFunctions[ot->beh.constructors[n]]->ReleaseInternal(); + + ot->beh.constructors[n] = func->id; + } } // Generate stub for the list factory as well @@ -3435,48 +3591,31 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT { asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, templateType->beh.listFactory); - // The function's refCount was already initialized to 1 ot->beh.listFactory = func->id; } ot->beh.addref = templateType->beh.addref; - if( scriptFunctions[ot->beh.addref] ) scriptFunctions[ot->beh.addref]->AddRef(); + if( scriptFunctions[ot->beh.addref] ) scriptFunctions[ot->beh.addref]->AddRefInternal(); ot->beh.release = templateType->beh.release; - if( scriptFunctions[ot->beh.release] ) scriptFunctions[ot->beh.release]->AddRef(); + if( scriptFunctions[ot->beh.release] ) scriptFunctions[ot->beh.release]->AddRefInternal(); + ot->beh.destruct = templateType->beh.destruct; + if( scriptFunctions[ot->beh.destruct] ) scriptFunctions[ot->beh.destruct]->AddRefInternal(); ot->beh.copy = templateType->beh.copy; - if( scriptFunctions[ot->beh.copy] ) scriptFunctions[ot->beh.copy]->AddRef(); - ot->beh.operators = templateType->beh.operators; - for( n = 1; n < ot->beh.operators.GetLength(); n += 2 ) - scriptFunctions[ot->beh.operators[n]]->AddRef(); + if( scriptFunctions[ot->beh.copy] ) scriptFunctions[ot->beh.copy]->AddRefInternal(); ot->beh.gcGetRefCount = templateType->beh.gcGetRefCount; - if( scriptFunctions[ot->beh.gcGetRefCount] ) scriptFunctions[ot->beh.gcGetRefCount]->AddRef(); + if( scriptFunctions[ot->beh.gcGetRefCount] ) scriptFunctions[ot->beh.gcGetRefCount]->AddRefInternal(); ot->beh.gcSetFlag = templateType->beh.gcSetFlag; - if( scriptFunctions[ot->beh.gcSetFlag] ) scriptFunctions[ot->beh.gcSetFlag]->AddRef(); + if( scriptFunctions[ot->beh.gcSetFlag] ) scriptFunctions[ot->beh.gcSetFlag]->AddRefInternal(); ot->beh.gcGetFlag = templateType->beh.gcGetFlag; - if( scriptFunctions[ot->beh.gcGetFlag] ) scriptFunctions[ot->beh.gcGetFlag]->AddRef(); + if( scriptFunctions[ot->beh.gcGetFlag] ) scriptFunctions[ot->beh.gcGetFlag]->AddRefInternal(); ot->beh.gcEnumReferences = templateType->beh.gcEnumReferences; - if( scriptFunctions[ot->beh.gcEnumReferences] ) scriptFunctions[ot->beh.gcEnumReferences]->AddRef(); + if( scriptFunctions[ot->beh.gcEnumReferences] ) scriptFunctions[ot->beh.gcEnumReferences]->AddRefInternal(); ot->beh.gcReleaseAllReferences = templateType->beh.gcReleaseAllReferences; - if( scriptFunctions[ot->beh.gcReleaseAllReferences] ) scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRef(); + if( scriptFunctions[ot->beh.gcReleaseAllReferences] ) scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRefInternal(); ot->beh.getWeakRefFlag = templateType->beh.getWeakRefFlag; - if( scriptFunctions[ot->beh.getWeakRefFlag] ) scriptFunctions[ot->beh.getWeakRefFlag]->AddRef(); + if( scriptFunctions[ot->beh.getWeakRefFlag] ) scriptFunctions[ot->beh.getWeakRefFlag]->AddRefInternal(); - // As the new template type is instanciated the engine should - // generate new functions to substitute the ones with the template subtype. - for( n = 1; n < ot->beh.operators.GetLength(); n += 2 ) - { - int funcId = ot->beh.operators[n]; - asCScriptFunction *func = scriptFunctions[funcId]; - - if( GenerateNewTemplateFunction(templateType, ot, func, &func) ) - { - // Release the old function, the new one already has its ref count set to 1 - scriptFunctions[funcId]->Release(); - ot->beh.operators[n] = func->id; - } - } - - // As the new template type is instanciated, the engine should + // As the new template type is instantiated, the engine should // generate new functions to substitute the ones with the template subtype. for( n = 0; n < ot->methods.GetLength(); n++ ) { @@ -3486,7 +3625,7 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT if( GenerateNewTemplateFunction(templateType, ot, func, &func) ) { // Release the old function, the new one already has its ref count set to 1 - scriptFunctions[funcId]->Release(); + scriptFunctions[funcId]->ReleaseInternal(); ot->methods[n] = func->id; } } @@ -3494,13 +3633,16 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT // Increase ref counter for sub type if it is an object type for( n = 0; n < ot->templateSubTypes.GetLength(); n++ ) if( ot->templateSubTypes[n].GetObjectType() ) - ot->templateSubTypes[n].GetObjectType()->AddRef(); + ot->templateSubTypes[n].GetObjectType()->AddRefInternal(); - templateInstanceTypes.PushLast(ot); - - // Store the template instance types that have been created automatically by the engine from a template type - // The object types in templateInstanceTypes that are not also in generatedTemplateTypes are registered template specializations - generatedTemplateTypes.PushLast(ot); + // Copy the properties to the template instance + for( n = 0; n < templateType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = templateType->properties[n]; + ot->properties.PushLast(asNEW(asCObjectProperty)(*prop)); + if( prop->type.GetObjectType() ) + prop->type.GetObjectType()->AddRefInternal(); + } return ot; } @@ -3563,6 +3705,64 @@ asCDataType asCScriptEngine::DetermineTypeForTemplate(const asCDataType &orig, a dt.MakeReference(orig.IsReference()); dt.MakeReadOnly(orig.IsReadOnly()); } + else if( orig.GetObjectType() && (orig.GetObjectType()->flags & asOBJ_TEMPLATE) ) + { + // The type is itself a template, so it is necessary to find the correct template instance type + asCArray tmplSubTypes; + asCObjectType *origType = orig.GetObjectType(); + bool needInstance = true; + + // Find the matching replacements for the subtypes + for( asUINT n = 0; n < origType->templateSubTypes.GetLength(); n++ ) + { + if( origType->templateSubTypes[n].GetObjectType() == 0 || + !(origType->templateSubTypes[n].GetObjectType()->flags & asOBJ_TEMPLATE_SUBTYPE) ) + { + // The template is already an instance so we shouldn't attempt to create another instance + needInstance = false; + break; + } + + for( asUINT m = 0; m < tmpl->templateSubTypes.GetLength(); m++ ) + if( origType->templateSubTypes[n].GetObjectType() == tmpl->templateSubTypes[m].GetObjectType() ) + tmplSubTypes.PushLast(ot->templateSubTypes[m]); + + if( tmplSubTypes.GetLength() != n+1 ) + { + asASSERT( false ); + return orig; + } + } + + asCObjectType *ntype = origType; + if( needInstance ) + { + // Always find the original template type when creating a new template instance otherwise the + // generation will fail since it will attempt to create factory stubs when they already exists, etc + for( asUINT n = 0; n < registeredTemplateTypes.GetLength(); n++ ) + if( registeredTemplateTypes[n]->name == origType->name ) + { + origType = registeredTemplateTypes[n]; + break; + } + + ntype = GetTemplateInstanceType(origType, tmplSubTypes, ot->module); + if( ntype == 0 ) + { + // It not possible to instantiate the subtype + asASSERT( false ); + ntype = tmpl; + } + } + + if( orig.IsObjectHandle() ) + dt = asCDataType::CreateObjectHandle(ntype, false); + else + dt = asCDataType::CreateObject(ntype, false); + + dt.MakeReference(orig.IsReference()); + dt.MakeReadOnly(orig.IsReadOnly()); + } else dt = orig; @@ -3589,21 +3789,30 @@ asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *t func->AllocateScriptFunctionData(); func->name = "factstub"; func->id = GetNextScriptFunctionId(); - func->returnType = asCDataType::CreateObjectHandle(ot, false); + AddScriptFunction(func); + func->isShared = true; + if( templateType->flags & asOBJ_REF ) + { + func->returnType = asCDataType::CreateObjectHandle(ot, false); + } + else + { + func->returnType = factory->returnType; // constructors return nothing + func->objectType = ot; + func->objectType->AddRefInternal(); + } // Skip the first parameter as this is the object type pointer that the stub will add func->parameterTypes.SetLength(factory->parameterTypes.GetLength()-1); func->inOutFlags.SetLength(factory->inOutFlags.GetLength()-1); for( asUINT p = 1; p < factory->parameterTypes.GetLength(); p++ ) { - func->parameterTypes[p-1] = DetermineTypeForTemplate(factory->parameterTypes[p], templateType, ot); + func->parameterTypes[p-1] = factory->parameterTypes[p]; func->inOutFlags[p-1] = factory->inOutFlags[p]; } func->scriptData->objVariablesOnHeap = 0; - - SetScriptFunction(func); - + // Generate the bytecode for the factory stub asUINT bcLength = asBCTypeSize[asBCInfo[asBC_OBJTYPE].type] + asBCTypeSize[asBCInfo[asBC_CALLSYS].type] + @@ -3611,6 +3820,8 @@ asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *t if( ep.includeJitInstructions ) bcLength += asBCTypeSize[asBCInfo[asBC_JitEntry].type]; + if( templateType->flags & asOBJ_VALUE ) + bcLength += asBCTypeSize[asBCInfo[asBC_SwapPtr].type]; func->scriptData->byteCode.SetLength(bcLength); asDWORD *bc = func->scriptData->byteCode.AddressOf(); @@ -3625,11 +3836,17 @@ asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *t *(asBYTE*)bc = asBC_OBJTYPE; *(asPWORD*)(bc+1) = (asPWORD)ot; bc += asBCTypeSize[asBCInfo[asBC_OBJTYPE].type]; + if( templateType->flags & asOBJ_VALUE ) + { + // Swap the object pointer with the object type + *(asBYTE*)bc = asBC_SwapPtr; + bc += asBCTypeSize[asBCInfo[asBC_SwapPtr].type]; + } *(asBYTE*)bc = asBC_CALLSYS; *(asDWORD*)(bc+1) = factoryId; bc += asBCTypeSize[asBCInfo[asBC_CALLSYS].type]; *(asBYTE*)bc = asBC_RET; - *(((asWORD*)bc)+1) = (asWORD)func->GetSpaceNeededForArguments(); + *(((asWORD*)bc)+1) = (asWORD)func->GetSpaceNeededForArguments() + (func->objectType ? AS_PTR_SIZE : 0); func->AddReferences(); func->scriptData->stackNeeded = AS_PTR_SIZE; @@ -3667,18 +3884,31 @@ asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *t return func; } +bool asCScriptEngine::RequireTypeReplacement(asCDataType &type, asCObjectType *templateType) +{ + if( type.GetObjectType() == templateType ) return true; + if( type.GetObjectType() && (type.GetObjectType()->flags & asOBJ_TEMPLATE_SUBTYPE) ) return true; + if( type.GetObjectType() && (type.GetObjectType()->flags & asOBJ_TEMPLATE) ) + { + for( asUINT n = 0; n < type.GetObjectType()->templateSubTypes.GetLength(); n++ ) + if( type.GetObjectType()->templateSubTypes[n].GetObjectType() && + type.GetObjectType()->templateSubTypes[n].GetObjectType()->flags & asOBJ_TEMPLATE_SUBTYPE ) + return true; + } + + return false; +} + bool asCScriptEngine::GenerateNewTemplateFunction(asCObjectType *templateType, asCObjectType *ot, asCScriptFunction *func, asCScriptFunction **newFunc) { bool needNewFunc = false; - if( (func->returnType.GetObjectType() && (func->returnType.GetObjectType()->flags & asOBJ_TEMPLATE_SUBTYPE)) || - func->returnType.GetObjectType() == templateType ) + if( RequireTypeReplacement(func->returnType, templateType) ) needNewFunc = true; else { for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) { - if( (func->parameterTypes[p].GetObjectType() && (func->parameterTypes[p].GetObjectType()->flags & asOBJ_TEMPLATE_SUBTYPE)) || - func->parameterTypes[p].GetObjectType() == templateType ) + if( RequireTypeReplacement(func->parameterTypes[p], templateType) ) { needNewFunc = true; break; @@ -3697,25 +3927,32 @@ bool asCScriptEngine::GenerateNewTemplateFunction(asCObjectType *templateType, a } func2->name = func->name; - func2->id = GetNextScriptFunctionId(); func2->returnType = DetermineTypeForTemplate(func->returnType, templateType, ot); - func2->parameterTypes.SetLength(func->parameterTypes.GetLength()); for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) func2->parameterTypes[p] = DetermineTypeForTemplate(func->parameterTypes[p], templateType, ot); - // TODO: template: Must be careful when instanciating templates for garbage collected types + // TODO: template: Must be careful when instantiating templates for garbage collected types // If the template hasn't been registered with the behaviours, it shouldn't - // permit instanciation of garbage collected types that in turn may refer to + // permit instantiation of garbage collected types that in turn may refer to // this instance. func2->inOutFlags = func->inOutFlags; func2->isReadOnly = func->isReadOnly; func2->objectType = ot; + func2->objectType->AddRefInternal(); func2->sysFuncIntf = asNEW(asSSystemFunctionInterface)(*func->sysFuncIntf); - SetScriptFunction(func2); + // Adjust the clean up instructions + if( func2->sysFuncIntf->callConv == ICC_GENERIC_FUNC || + func2->sysFuncIntf->callConv == ICC_GENERIC_METHOD ) + PrepareSystemFunctionGeneric(func2, func2->sysFuncIntf, this); + else + PrepareSystemFunction(func2, func2->sysFuncIntf, this); + + func2->id = GetNextScriptFunctionId(); + AddScriptFunction(func2); // Return the new function *newFunc = func2; @@ -3732,7 +3969,7 @@ void asCScriptEngine::CallObjectMethod(void *obj, int func) const void asCScriptEngine::CallObjectMethod(void *obj, asSSystemFunctionInterface *i, asCScriptFunction *s) const { -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(AS_PSVITA) if( i->callConv == ICC_GENERIC_METHOD ) { asCGeneric gen(const_cast(this), s, obj, 0); @@ -3752,7 +3989,7 @@ void asCScriptEngine::CallObjectMethod(void *obj, asSSystemFunctionInterface *i, asPWORD baseOffset; // Same size as the pointer } f; } p; - p.f.func = (void (*)())(i->func); + p.f.func = (asFUNCTION_t)(i->func); p.f.baseOffset = asPWORD(i->baseOffset); void (asCSimpleDummy::*f)() = p.mthd; (((asCSimpleDummy*)obj)->*f)(); @@ -3771,7 +4008,7 @@ void asCScriptEngine::CallObjectMethod(void *obj, asSSystemFunctionInterface *i, asSIMPLEMETHOD_t mthd; asFUNCTION_t func; } p; - p.func = (void (*)())(i->func); + p.func = (asFUNCTION_t)(i->func); void (asCSimpleDummy::*f)() = p.mthd; obj = (void*)(asPWORD(obj) + i->baseOffset); (((asCSimpleDummy*)obj)->*f)(); @@ -3798,7 +4035,7 @@ bool asCScriptEngine::CallObjectMethodRetBool(void *obj, int func) const asASSERT( s != 0 ); asSSystemFunctionInterface *i = s->sysFuncIntf; -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(AS_PSVITA) if( i->callConv == ICC_GENERIC_METHOD ) { asCGeneric gen(const_cast(this), s, obj, 0); @@ -3818,7 +4055,7 @@ bool asCScriptEngine::CallObjectMethodRetBool(void *obj, int func) const asPWORD baseOffset; } f; } p; - p.f.func = (void (*)())(i->func); + p.f.func = (asFUNCTION_t)(i->func); p.f.baseOffset = asPWORD(i->baseOffset); bool (asCSimpleDummy::*f)() = (bool (asCSimpleDummy::*)())(p.mthd); return (((asCSimpleDummy*)obj)->*f)(); @@ -3837,7 +4074,7 @@ bool asCScriptEngine::CallObjectMethodRetBool(void *obj, int func) const asSIMPLEMETHOD_t mthd; asFUNCTION_t func; } p; - p.func = (void (*)())(i->func); + p.func = (asFUNCTION_t)(i->func); bool (asCSimpleDummy::*f)() = (bool (asCSimpleDummy::*)())p.mthd; obj = (void*)(asPWORD(obj) + i->baseOffset); return (((asCSimpleDummy*)obj)->*f)(); @@ -3865,7 +4102,7 @@ int asCScriptEngine::CallObjectMethodRetInt(void *obj, int func) const asASSERT( s != 0 ); asSSystemFunctionInterface *i = s->sysFuncIntf; -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(AS_PSVITA) if( i->callConv == ICC_GENERIC_METHOD ) { asCGeneric gen(const_cast(this), s, obj, 0); @@ -3885,7 +4122,7 @@ int asCScriptEngine::CallObjectMethodRetInt(void *obj, int func) const asPWORD baseOffset; } f; } p; - p.f.func = (void (*)())(i->func); + p.f.func = (asFUNCTION_t)(i->func); p.f.baseOffset = asPWORD(i->baseOffset); int (asCSimpleDummy::*f)() = (int (asCSimpleDummy::*)())(p.mthd); return (((asCSimpleDummy*)obj)->*f)(); @@ -3904,7 +4141,7 @@ int asCScriptEngine::CallObjectMethodRetInt(void *obj, int func) const asSIMPLEMETHOD_t mthd; asFUNCTION_t func; } p; - p.func = (void (*)())(i->func); + p.func = (asFUNCTION_t)(i->func); int (asCSimpleDummy::*f)() = (int (asCSimpleDummy::*)())p.mthd; obj = (void*)(asPWORD(obj) + i->baseOffset); return (((asCSimpleDummy*)obj)->*f)(); @@ -3932,7 +4169,7 @@ void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int func) const asASSERT( s != 0 ); asSSystemFunctionInterface *i = s->sysFuncIntf; -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(AS_PSVITA) if( i->callConv == ICC_GENERIC_METHOD ) { asCGeneric gen(const_cast(this), s, obj, 0); @@ -3952,7 +4189,7 @@ void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int func) const asPWORD baseOffset; } f; } p; - p.f.func = (void (*)())(i->func); + p.f.func = (asFUNCTION_t)(i->func); p.f.baseOffset = asPWORD(i->baseOffset); void *(asCSimpleDummy::*f)() = (void *(asCSimpleDummy::*)())(p.mthd); return (((asCSimpleDummy*)obj)->*f)(); @@ -3971,7 +4208,7 @@ void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int func) const asSIMPLEMETHOD_t mthd; asFUNCTION_t func; } p; - p.func = (void (*)())(i->func); + p.func = (asFUNCTION_t)(i->func); void *(asCSimpleDummy::*f)() = (void *(asCSimpleDummy::*)())p.mthd; obj = (void*)(asPWORD(obj) + i->baseOffset); return (((asCSimpleDummy*)obj)->*f)(); @@ -4060,7 +4297,7 @@ void asCScriptEngine::CallObjectMethod(void *obj, void *param, int func) const void asCScriptEngine::CallObjectMethod(void *obj, void *param, asSSystemFunctionInterface *i, asCScriptFunction *s) const { -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(AS_PSVITA) if( i->callConv == ICC_CDECL_OBJLAST ) { void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); @@ -4085,7 +4322,7 @@ void asCScriptEngine::CallObjectMethod(void *obj, void *param, asSSystemFunction asPWORD baseOffset; // Same size as the pointer } f; } p; - p.f.func = (void (*)())(i->func); + p.f.func = (asFUNCTION_t)(i->func); p.f.baseOffset = asPWORD(i->baseOffset); void (asCSimpleDummy::*f)(void*) = (void (asCSimpleDummy::*)(void*))(p.mthd); (((asCSimpleDummy*)obj)->*f)(param); @@ -4104,7 +4341,7 @@ void asCScriptEngine::CallObjectMethod(void *obj, void *param, asSSystemFunction asSIMPLEMETHOD_t mthd; asFUNCTION_t func; } p; - p.func = (void (*)())(i->func); + p.func = (asFUNCTION_t)(i->func); void (asCSimpleDummy::*f)(void *) = (void (asCSimpleDummy::*)(void *))(p.mthd); obj = (void*)(asPWORD(obj) + i->baseOffset); (((asCSimpleDummy*)obj)->*f)(param); @@ -4185,26 +4422,38 @@ bool asCScriptEngine::CallGlobalFunctionRetBool(void *param1, void *param2, asSS } } -void *asCScriptEngine::CallAlloc(asCObjectType *type) const +void *asCScriptEngine::CallAlloc(const asCObjectType *type) const { - // Allocate 4 bytes as the smallest size. Otherwise CallSystemFunction may try to - // copy a DWORD onto a smaller memory block, in case the object type is return in registers. + // Allocate 4 bytes as the smallest size. Otherwise CallSystemFunction may try to + // copy a DWORD onto a smaller memory block, in case the object type is return in registers. // Pad to the next even 4 bytes to avoid asBC_CPY writing outside of allocated buffer for registered POD types asUINT size = type->size; - if( size & 0x3 ) + if( size & 0x3 ) size += 4 - (size & 0x3); +#ifndef WIP_16BYTE_ALIGN #if defined(AS_DEBUG) - return ((asALLOCFUNCDEBUG_t)(userAlloc))(size, __FILE__, __LINE__); + return ((asALLOCFUNCDEBUG_t)userAlloc)(size, __FILE__, __LINE__); #else return userAlloc(size); #endif +#else +#if defined(AS_DEBUG) + return ((asALLOCALIGNEDFUNCDEBUG_t)userAllocAligned)(size, type->alignment, __FILE__, __LINE__); +#else + return userAllocAligned(size, type->alignment); +#endif +#endif } void asCScriptEngine::CallFree(void *obj) const { +#ifndef WIP_16BYTE_ALIGN userFree(obj); +#else + userFreeAligned(obj); +#endif } // interface @@ -4220,9 +4469,18 @@ int asCScriptEngine::GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj, asIOb } // interface -int asCScriptEngine::GarbageCollect(asDWORD flags) +int asCScriptEngine::GarbageCollect(asDWORD flags, asUINT iterations) { - return gc.GarbageCollect(flags); + int r = gc.GarbageCollect(flags, iterations); + + if( r == 0 ) + { + // Delete any modules that have been discarded previously but not + // removed due to being referred to by objects in the garbage collector + DeleteDiscardedModules(); + } + + return r; } // interface @@ -4258,7 +4516,7 @@ int asCScriptEngine::GetTypeIdFromDataType(const asCDataType &dtIn) const int typeId = mapTypeIdToDataType.GetKey(cursor); if( dtIn.GetObjectType() && !(dtIn.GetObjectType()->flags & asOBJ_ASHANDLE) ) { - // The the ASHANDLE types behave like handles, but are really + // The ASHANDLE types behave like handles, but are really // value types so the typeId is never returned as a handle if( dtIn.IsObjectHandle() ) typeId |= asTYPEID_OBJHANDLE; @@ -4343,6 +4601,23 @@ void asCScriptEngine::RemoveFromTypeIdMap(asCObjectType *type) } } +// interface +asIObjectType *asCScriptEngine::GetObjectTypeByDecl(const char *decl) const +{ + asCDataType dt; + // This cast is ok, because we are not changing anything in the engine + asCBuilder bld(const_cast(this), 0); + + // Don't write parser errors to the message callback + bld.silent = true; + + int r = bld.ParseDataType(decl, &dt, defaultNamespace); + if( r < 0 ) + return 0; + + return dt.GetObjectType(); +} + // interface int asCScriptEngine::GetTypeIdByDecl(const char *decl) const { @@ -4352,7 +4627,7 @@ int asCScriptEngine::GetTypeIdByDecl(const char *decl) const // Don't write parser errors to the message callback bld.silent = true; - + int r = bld.ParseDataType(decl, &dt, defaultNamespace); if( r < 0 ) return asINVALID_TYPE; @@ -4366,12 +4641,12 @@ const char *asCScriptEngine::GetTypeDeclaration(int typeId, bool includeNamespac asCDataType dt = GetDataTypeFromTypeId(typeId); asCString *tempString = &asCThreadManager::GetLocalData()->string; - *tempString = dt.Format(includeNamespace); + *tempString = dt.Format(defaultNamespace, includeNamespace); return tempString->AddressOf(); } -// TODO: interface: Deprecate. This function is not necessary now that all primitive types have fixed typeIds +// interface int asCScriptEngine::GetSizeOfPrimitiveType(int typeId) const { asCDataType dt = GetDataTypeFromTypeId(typeId); @@ -4380,24 +4655,118 @@ int asCScriptEngine::GetSizeOfPrimitiveType(int typeId) const return dt.GetSizeInMemoryBytes(); } -#ifdef AS_DEPRECATED -// Deprecated since 2.27.0, 2013-07-18 -void *asCScriptEngine::CreateScriptObject(int typeId) +// interface +int asCScriptEngine::RefCastObject(void *obj, asIObjectType *fromType, asIObjectType *toType, void **newPtr, bool useOnlyImplicitCast) { - // Make sure the type id is for an object type, and not a primitive or a handle - if( (typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR)) != typeId ) return 0; - if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return 0; + if( newPtr == 0 ) return asINVALID_ARG; + *newPtr = 0; - asCDataType dt = GetDataTypeFromTypeId(typeId); + if( fromType == 0 || toType == 0 ) return asINVALID_ARG; - // Is the type id valid? - if( !dt.IsValid() ) return 0; + // A null-pointer can always be cast to another type, so it will always be successful + if( obj == 0 ) + return asSUCCESS; - asCObjectType *objType = dt.GetObjectType(); + // This method doesn't support casting function pointers, since they cannot be described with just an object type + if( fromType->GetFlags() & asOBJ_SCRIPT_FUNCTION ) + return asNOT_SUPPORTED; - return CreateScriptObject(objType); + if( fromType == toType ) + { + *newPtr = obj; + AddRefScriptObject(*newPtr, toType); + return asSUCCESS; + } + + // Look for ref cast behaviours + asCScriptFunction *universalCastFunc = 0; + asCObjectType *from = reinterpret_cast(fromType); + for( asUINT n = 0; n < from->methods.GetLength(); n++ ) + { + asCScriptFunction *func = scriptFunctions[from->methods[n]]; + if( func->name == "opImplCast" || + (!useOnlyImplicitCast && func->name == "opCast") ) + { + if( func->returnType.GetObjectType() == toType ) + { + *newPtr = CallObjectMethodRetPtr(obj, func->id); + // The ref cast behaviour returns a handle with incremented + // ref counter, so there is no need to call AddRef explicitly + // unless the function is registered with autohandle + if( func->sysFuncIntf->returnAutoHandle ) + AddRefScriptObject(*newPtr, toType); + return asSUCCESS; + } + else + { + asASSERT( func->returnType.GetTokenType() == ttVoid && + func->parameterTypes.GetLength() == 1 && + func->parameterTypes[0].GetTokenType() == ttQuestion ); + universalCastFunc = func; + } + } + } + + // One last chance if the object has a void opCast(?&out) behaviour + if( universalCastFunc ) + { + // TODO: Add proper error handling + asIScriptContext *ctx = RequestContext(); + ctx->Prepare(universalCastFunc); + ctx->SetObject(obj); + ctx->SetArgVarType(0, newPtr, toType->GetTypeId() | asTYPEID_OBJHANDLE); + ctx->Execute(); + ReturnContext(ctx); + + // The opCast(?&out) method already incremented the + // refCount so there is no need to do it manually + return asSUCCESS; + } + + // For script classes and interfaces there is a quick route + if( (fromType->GetFlags() & asOBJ_SCRIPT_OBJECT) && (toType->GetFlags() & asOBJ_SCRIPT_OBJECT) ) + { + if( fromType == toType ) + { + *newPtr = obj; + reinterpret_cast(*newPtr)->AddRef(); + return asSUCCESS; + } + + // Up casts to base class or interface can be done implicitly + if( fromType->DerivesFrom(toType) || + fromType->Implements(toType) ) + { + *newPtr = obj; + reinterpret_cast(*newPtr)->AddRef(); + return asSUCCESS; + } + // Down casts to derived class or from interface can only be done explicitly + if( !useOnlyImplicitCast ) + { + if( toType->Implements(fromType) || + toType->DerivesFrom(fromType) ) + { + *newPtr = obj; + reinterpret_cast(*newPtr)->AddRef(); + return asSUCCESS; + } + + // Get the true type of the object so the explicit cast can evaluate all possibilities + asIObjectType *from = reinterpret_cast(obj)->GetObjectType(); + if( from->DerivesFrom(toType) || + from->Implements(toType) ) + { + *newPtr = obj; + reinterpret_cast(*newPtr)->AddRef(); + return asSUCCESS; + } + } + } + + // The cast is not available, but it is still a success + return asSUCCESS; } -#endif // interface void *asCScriptEngine::CreateScriptObject(const asIObjectType *type) @@ -4425,19 +4794,42 @@ void *asCScriptEngine::CreateScriptObject(const asIObjectType *type) else if( objType->flags & asOBJ_TEMPLATE ) { // The registered factory that takes the object type is moved - // to the construct behaviour when the type is instanciated + // to the construct behaviour when the type is instantiated +#ifdef AS_NO_EXCEPTIONS ptr = CallGlobalFunctionRetPtr(objType->beh.construct, objType); +#else + try + { + ptr = CallGlobalFunctionRetPtr(objType->beh.construct, objType); + } + catch(...) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException(TXT_EXCEPTION_CAUGHT); + } +#endif } else if( objType->flags & asOBJ_REF ) { // Call the default factory directly +#ifdef AS_NO_EXCEPTIONS ptr = CallGlobalFunctionRetPtr(objType->beh.factory); +#else + try + { + ptr = CallGlobalFunctionRetPtr(objType->beh.factory); + } + catch(...) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException(TXT_EXCEPTION_CAUGHT); + } +#endif } else { - // TODO: Shouldn't support allocating object like this, because the - // caller cannot be certain how the memory was allocated. - // Make sure there is a default constructor or that it is a POD type if( objType->beh.construct == 0 && !(objType->flags & asOBJ_POD) ) { @@ -4451,34 +4843,31 @@ void *asCScriptEngine::CreateScriptObject(const asIObjectType *type) ptr = CallAlloc(objType); int funcIndex = objType->beh.construct; if( funcIndex ) + { +#ifdef AS_NO_EXCEPTIONS CallObjectMethod(ptr, funcIndex); +#else + try + { + CallObjectMethod(ptr, funcIndex); + } + catch(...) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException(TXT_EXCEPTION_CAUGHT); + + // Free the memory + CallFree(ptr); + ptr = 0; + } +#endif + } } return ptr; } -#ifdef AS_DEPRECATED -// Deprecated since 2.27.0, 2013-07-18 -// interface -void *asCScriptEngine::CreateUninitializedScriptObject(int typeId) -{ - // Make sure the type id is for an object type, and not a primitive or a handle. - // This function only works for script classes. Registered types cannot be created this way. - if( (typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR)) != typeId ) return 0; - if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return 0; - if( (typeId & asTYPEID_SCRIPTOBJECT) == 0 ) return 0; - - asCDataType dt = GetDataTypeFromTypeId(typeId); - - // Is the type id valid? - if( !dt.IsValid() ) return 0; - - asCObjectType *objType = dt.GetObjectType(); - - return CreateUninitializedScriptObject(objType); -} -#endif - // interface void *asCScriptEngine::CreateUninitializedScriptObject(const asIObjectType *type) { @@ -4498,31 +4887,46 @@ void *asCScriptEngine::CreateUninitializedScriptObject(const asIObjectType *type return obj; } -#ifdef AS_DEPRECATED -// Deprecated since 2.27.0, 2013-07-18 -void *asCScriptEngine::CreateScriptObjectCopy(void *origObj, int typeId) -{ - asCDataType dt = GetDataTypeFromTypeId(typeId); - if( !dt.IsValid() ) return 0; - asCObjectType *objType = dt.GetObjectType(); - - void *newObj = CreateScriptObject(objType); - if( newObj == 0 ) return 0; - - AssignScriptObject(newObj, origObj, typeId); - - return newObj; -} -#endif - // interface void *asCScriptEngine::CreateScriptObjectCopy(void *origObj, const asIObjectType *type) { - // TODO: Should use the copy constructor if available - void *newObj = CreateScriptObject(type); - if( newObj == 0 ) return 0; + if( origObj == 0 || type == 0 ) return 0; - AssignScriptObject(newObj, origObj, type); + void *newObj = 0; + + const asCObjectType *ot = reinterpret_cast(type); + // TODO: runtime optimize: Should call copy factory for ref types too + if( ot->beh.copyconstruct ) + { + // Manually allocate the memory, then call the copy constructor + newObj = CallAlloc(ot); +#ifdef AS_NO_EXCEPTIONS + CallObjectMethod(newObj, origObj, ot->beh.copyconstruct); +#else + try + { + CallObjectMethod(newObj, origObj, ot->beh.copyconstruct); + } + catch(...) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException(TXT_EXCEPTION_CAUGHT); + + // Free the memory + CallFree(newObj); + newObj = 0; + } +#endif + } + else + { + // Allocate the object and then do a value assign + newObj = CreateScriptObject(type); + if( newObj == 0 ) return 0; + + AssignScriptObject(newObj, origObj, type); + } return newObj; } @@ -4530,47 +4934,39 @@ void *asCScriptEngine::CreateScriptObjectCopy(void *origObj, const asIObjectType // internal void asCScriptEngine::ConstructScriptObjectCopy(void *mem, void *obj, asCObjectType *type) { + if( type == 0 || mem == 0 || obj == 0 ) return; + // This function is only meant to be used for value types asASSERT( type->flags & asOBJ_VALUE ); - // TODO: runtime optimize: Should use the copy constructor when available - int funcIndex = type->beh.construct; + // Call the copy constructor if available, else call the default constructor followed by the opAssign + int funcIndex = type->beh.copyconstruct; if( funcIndex ) - CallObjectMethod(mem, funcIndex); + { + CallObjectMethod(mem, obj, funcIndex); + } + else + { + funcIndex = type->beh.construct; + if( funcIndex ) + CallObjectMethod(mem, funcIndex); - AssignScriptObject(mem, obj, type); + AssignScriptObject(mem, obj, type); + } } -#ifdef AS_DEPRECATED -// Deprecated since 2.27.0, 2013-07-18 -void asCScriptEngine::AssignScriptObject(void *dstObj, void *srcObj, int typeId) -{ - // Make sure the type id is for an object type, and not a primitive or a handle - if( (typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR)) != typeId ) return; - if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return; - - // Copy the contents from the original object, using the assignment operator - asCDataType dt = GetDataTypeFromTypeId(typeId); - - // Is the type id valid? - if( !dt.IsValid() ) return; - - asCObjectType *objType = dt.GetObjectType(); - - AssignScriptObject(dstObj, srcObj, objType); -} -#endif - // interface -void asCScriptEngine::AssignScriptObject(void *dstObj, void *srcObj, const asIObjectType *type) +int asCScriptEngine::AssignScriptObject(void *dstObj, void *srcObj, const asIObjectType *type) { - if( type == 0 ) return; + // TODO: Warn about invalid call in message stream + // TODO: Should a script exception be set in case a context is active? + if( type == 0 || dstObj == 0 || srcObj == 0 ) return asINVALID_ARG; const asCObjectType *objType = reinterpret_cast(type); // If value assign for ref types has been disabled, then don't do anything if the type is a ref type if( ep.disallowValueAssignForRefType && (objType->flags & asOBJ_REF) && !(objType->flags & asOBJ_SCOPED) ) - return; + return asNOT_SUPPORTED; // Must not copy if the opAssign is not available and the object is not a POD object if( objType->beh.copy ) @@ -4589,33 +4985,10 @@ void asCScriptEngine::AssignScriptObject(void *dstObj, void *srcObj, const asIOb { memcpy(dstObj, srcObj, objType->size); } + + return asSUCCESS; } -#ifdef AS_DEPRECATED -// Deprecated since 2.27.0, 2013-07-18 -void asCScriptEngine::AddRefScriptObject(void *obj, int typeId) -{ - // Make sure it is not a null pointer - if( obj == 0 ) return; - - // Make sure the type id is for an object type or a handle - if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return; - - asCDataType dt = GetDataTypeFromTypeId(typeId); - - // Is the type id valid? - if( !dt.IsValid() ) return; - - asCObjectType *objType = dt.GetObjectType(); - - if( objType->beh.addref ) - { - // Call the addref behaviour - CallObjectMethod(obj, objType->beh.addref); - } -} -#endif - // interface void asCScriptEngine::AddRefScriptObject(void *obj, const asIObjectType *type) { @@ -4630,27 +5003,6 @@ void asCScriptEngine::AddRefScriptObject(void *obj, const asIObjectType *type) } } -#ifdef AS_DEPRECATED -// Deprecated since 2.27.0, 2013-07-18 -void asCScriptEngine::ReleaseScriptObject(void *obj, int typeId) -{ - // Make sure it is not a null pointer - if( obj == 0 ) return; - - // Make sure the type id is for an object type or a handle - if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return; - - asCDataType dt = GetDataTypeFromTypeId(typeId); - - // Is the type id valid? - if( !dt.IsValid() ) return; - - asCObjectType *objType = dt.GetObjectType(); - - ReleaseScriptObject(obj, objType); -} -#endif - // interface void asCScriptEngine::ReleaseScriptObject(void *obj, const asIObjectType *type) { @@ -4669,21 +5021,22 @@ void asCScriptEngine::ReleaseScriptObject(void *obj, const asIObjectType *type) } else { - // There is really only one reason why the application would want to - // call this method for a value type, and that is if it is calling it - // as from a JIT compiled asBC_FREE instruction. - // Call the destructor if( objType->beh.destruct ) CallObjectMethod(obj, objType->beh.destruct); else if( objType->flags & asOBJ_LIST_PATTERN ) DestroyList((asBYTE*)obj, objType); + // We'll have to trust that the memory for the object was allocated with CallAlloc. + // This is true if the object was created in the context, or with CreateScriptObject. + // Then free the memory CallFree(obj); } } +#ifdef AS_DEPRECATED +// Deprecated since 2.30.0, 2014-11-04 // interface bool asCScriptEngine::IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) const { @@ -4718,6 +5071,7 @@ bool asCScriptEngine::IsHandleCompatibleWithObject(void *obj, int objTypeId, int return false; } +#endif // interface int asCScriptEngine::BeginConfigGroup(const char *groupName) @@ -4774,6 +5128,12 @@ int asCScriptEngine::RemoveConfigGroup(const char *groupName) { asCConfigGroup *group = configGroups[n]; + // Remove any unused generated template instances + // before verifying if the config group is still in use. + // RemoveTemplateInstanceType() checks if the instance is in use + for( asUINT g = generatedTemplateTypes.GetLength(); g-- > 0; ) + RemoveTemplateInstanceType(generatedTemplateTypes[g]); + // Make sure the group isn't referenced by anyone if( group->refCount > 0 ) return asCONFIG_GROUP_IS_IN_USE; @@ -4873,7 +5233,7 @@ int asCScriptEngine::GetNextScriptFunctionId() return (int)scriptFunctions.GetLength(); } -void asCScriptEngine::SetScriptFunction(asCScriptFunction *func) +void asCScriptEngine::AddScriptFunction(asCScriptFunction *func) { // Update the internal arrays with the function id that is now used if( freeScriptFunctionIds.GetLength() && freeScriptFunctionIds[freeScriptFunctionIds.GetLength()-1] == func->id ) @@ -4889,52 +5249,79 @@ void asCScriptEngine::SetScriptFunction(asCScriptFunction *func) } } -void asCScriptEngine::FreeScriptFunctionId(int id) +void asCScriptEngine::RemoveScriptFunction(asCScriptFunction *func) { - if( id < 0 ) return; - id &= ~FUNC_IMPORTED; - if( id >= (int)scriptFunctions.GetLength() ) return; - - if( scriptFunctions[id] ) + if( func == 0 || func->id < 0 ) return; + int id = func->id & ~FUNC_IMPORTED; + if( func->funcType == asFUNC_IMPORTED ) { - asCScriptFunction *func = scriptFunctions[id]; + if( id >= (int)importedFunctions.GetLength() ) return; - // Remove the function from the list of script functions - if( id == (int)scriptFunctions.GetLength() - 1 ) + if( importedFunctions[id] ) { - scriptFunctions.PopLast(); - } - else - { - scriptFunctions[id] = 0; - freeScriptFunctionIds.PushLast(id); - } - - // Is the function used as signature id? - if( func->signatureId == id ) - { - // Remove the signature id - signatureIds.RemoveValue(func); - - // Update all functions using the signature id - int newSigId = 0; - for( asUINT n = 0; n < scriptFunctions.GetLength(); n++ ) + // Remove the function from the list of script functions + if( id == (int)importedFunctions.GetLength() - 1 ) { - if( scriptFunctions[n] && scriptFunctions[n]->signatureId == id ) - { - if( newSigId == 0 ) - { - newSigId = scriptFunctions[n]->id; - signatureIds.PushLast(scriptFunctions[n]); - } + importedFunctions.PopLast(); + } + else + { + importedFunctions[id] = 0; + freeImportedFunctionIdxs.PushLast(id); + } + } + } + else + { + if( id >= (int)scriptFunctions.GetLength() ) return; + asASSERT( func == scriptFunctions[id] ); - scriptFunctions[n]->signatureId = newSigId; + if( scriptFunctions[id] ) + { + // Remove the function from the list of script functions + if( id == (int)scriptFunctions.GetLength() - 1 ) + { + scriptFunctions.PopLast(); + } + else + { + scriptFunctions[id] = 0; + freeScriptFunctionIds.PushLast(id); + } + + // Is the function used as signature id? + if( func->signatureId == id ) + { + // Remove the signature id + signatureIds.RemoveValue(func); + + // Update all functions using the signature id + int newSigId = 0; + for( asUINT n = 0; n < scriptFunctions.GetLength(); n++ ) + { + if( scriptFunctions[n] && scriptFunctions[n]->signatureId == id ) + { + if( newSigId == 0 ) + { + newSigId = scriptFunctions[n]->id; + signatureIds.PushLast(scriptFunctions[n]); + } + + scriptFunctions[n]->signatureId = newSigId; + } } } } } } +// internal +void asCScriptEngine::RemoveFuncdef(asCScriptFunction *funcdef) +{ + // TODO: 2.30.0: redesign: How to avoid removing a funcdef that is shared by multiple modules? + funcDefs.RemoveValue(funcdef); +} + // interface int asCScriptEngine::RegisterFuncdef(const char *decl) { @@ -4964,26 +5351,15 @@ int asCScriptEngine::RegisterFuncdef(const char *decl) } func->id = GetNextScriptFunctionId(); - SetScriptFunction(func); + AddScriptFunction(func); funcDefs.PushLast(func); + func->AddRefInternal(); registeredFuncDefs.PushLast(func); currentGroup->funcDefs.PushLast(func); // If parameter type from other groups are used, add references - if( func->returnType.GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType()); - currentGroup->RefConfigGroup(group); - } - for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) - { - if( func->parameterTypes[n].GetObjectType() ) - { - asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType()); - currentGroup->RefConfigGroup(group); - } - } + currentGroup->AddReferencesForFunc(this, func); // Return the function id as success return func->id; @@ -5121,7 +5497,6 @@ int asCScriptEngine::RegisterEnum(const char *name) return ConfigError(asINVALID_NAME, "RegisterEnum", name, 0); // Verify if the name has been registered as a type already - // TODO: Must check for registered funcdefs too if( GetRegisteredObjectType(name, defaultNamespace) ) return asALREADY_REGISTERED; @@ -5132,7 +5507,12 @@ int asCScriptEngine::RegisterEnum(const char *name) int r = bld.ParseDataType(name, &dt, defaultNamespace); msgCallback = oldMsgCallback; if( r >= 0 ) - return ConfigError(asERROR, "RegisterEnum", name, 0); + { + // If it is not in the defaultNamespace then the type was successfully parsed because + // it is declared in a parent namespace which shouldn't be treated as an error + if( dt.GetObjectType() && dt.GetObjectType()->nameSpace == defaultNamespace ) + return ConfigError(asERROR, "RegisterEnum", name, 0); + } // Make sure the name is not a reserved keyword size_t tokenLen; @@ -5186,9 +5566,9 @@ int asCScriptEngine::RegisterEnumValue(const char *typeName, const char *valueNa if( NULL == valueName ) return ConfigError(asINVALID_NAME, "RegisterEnumValue", typeName, valueName); - int tokenLen; + asUINT tokenLen = 0; asETokenClass tokenClass = ParseToken(valueName, 0, &tokenLen); - if( tokenClass != asTC_IDENTIFIER || tokenLen != (int)strlen(valueName) ) + if( tokenClass != asTC_IDENTIFIER || tokenLen != strlen(valueName) ) return ConfigError(asINVALID_NAME, "RegisterEnumValue", typeName, valueName); for( unsigned int n = 0; n < ot->enumValues.GetLength(); n++ ) @@ -5289,21 +5669,28 @@ asIObjectType *asCScriptEngine::GetObjectTypeByIndex(asUINT index) const // interface asIObjectType *asCScriptEngine::GetObjectTypeByName(const char *name) const { - // Check the object types - for( asUINT n = 0; n < registeredObjTypes.GetLength(); n++ ) + asSNameSpace *ns = defaultNamespace; + while( ns ) { - if( registeredObjTypes[n]->name == name && - registeredObjTypes[n]->nameSpace == defaultNamespace ) - return registeredObjTypes[n]; - } + // Check the object types + for( asUINT n = 0; n < registeredObjTypes.GetLength(); n++ ) + { + if( registeredObjTypes[n]->name == name && + registeredObjTypes[n]->nameSpace == ns ) + return registeredObjTypes[n]; + } - // Perhaps it is a template type? In this case - // the returned type will be the generic type - for( asUINT n = 0; n < registeredTemplateTypes.GetLength(); n++ ) - { - if( registeredTemplateTypes[n]->name == name && - registeredTemplateTypes[n]->nameSpace == defaultNamespace ) - return registeredTemplateTypes[n]; + // Perhaps it is a template type? In this case + // the returned type will be the generic type + for( asUINT n = 0; n < registeredTemplateTypes.GetLength(); n++ ) + { + if( registeredTemplateTypes[n]->name == name && + registeredTemplateTypes[n]->nameSpace == ns ) + return registeredTemplateTypes[n]; + } + + // Recursively search parent namespace + ns = GetParentNameSpace(ns); } return 0; @@ -5439,21 +5826,69 @@ void asCScriptEngine::SetEngineUserDataCleanupCallback(asCLEANENGINEFUNC_t callb } // interface -void asCScriptEngine::SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback) +void asCScriptEngine::SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback, asPWORD type) { - cleanModuleFunc = callback; + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanModuleFuncs.GetLength(); n++ ) + { + if( cleanModuleFuncs[n].type == type ) + { + cleanModuleFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SModuleClean otc = {type, callback}; + cleanModuleFuncs.PushLast(otc); + + RELEASEEXCLUSIVE(engineRWLock); } // interface -void asCScriptEngine::SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback) +void asCScriptEngine::SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback, asPWORD type) { - cleanContextFunc = callback; + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanContextFuncs.GetLength(); n++ ) + { + if( cleanContextFuncs[n].type == type ) + { + cleanContextFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SContextClean otc = {type, callback}; + cleanContextFuncs.PushLast(otc); + + RELEASEEXCLUSIVE(engineRWLock); } // interface -void asCScriptEngine::SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback) +void asCScriptEngine::SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback, asPWORD type) { - cleanFunctionFunc = callback; + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanFunctionFuncs.GetLength(); n++ ) + { + if( cleanFunctionFuncs[n].type == type ) + { + cleanFunctionFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SFunctionClean otc = {type, callback}; + cleanFunctionFuncs.PushLast(otc); + + RELEASEEXCLUSIVE(engineRWLock); } // interface @@ -5478,6 +5913,28 @@ void asCScriptEngine::SetObjectTypeUserDataCleanupCallback(asCLEANOBJECTTYPEFUNC RELEASEEXCLUSIVE(engineRWLock); } +// interface +void asCScriptEngine::SetScriptObjectUserDataCleanupCallback(asCLEANSCRIPTOBJECTFUNC_t callback, asPWORD type) +{ + ACQUIREEXCLUSIVE(engineRWLock); + + for( asUINT n = 0; n < cleanScriptObjectFuncs.GetLength(); n++ ) + { + if( cleanScriptObjectFuncs[n].type == type ) + { + cleanScriptObjectFuncs[n].cleanFunc = callback; + + RELEASEEXCLUSIVE(engineRWLock); + + return; + } + } + SScriptObjClean soc = {type, callback}; + cleanScriptObjectFuncs.PushLast(soc); + + RELEASEEXCLUSIVE(engineRWLock); +} + // internal asCObjectType *asCScriptEngine::GetListPatternType(int listPatternFuncId) { @@ -5532,7 +5989,7 @@ void asCScriptEngine::DestroySubList(asBYTE *&buffer, asSListPatternNode *&node) node = node->next; while( node ) { - if( node->type == asLPT_REPEAT ) + if( node->type == asLPT_REPEAT || node->type == asLPT_REPEAT_SAME ) { // Align the offset to 4 bytes boundary if( (asPWORD(buffer) & 0x3) ) @@ -5541,6 +5998,26 @@ void asCScriptEngine::DestroySubList(asBYTE *&buffer, asSListPatternNode *&node) // Determine how many times the pattern repeat count = *(asUINT*)buffer; buffer += 4; + + if( count == 0 ) + { + // Skip the sub pattern that was expected to be repeated, otherwise + // we'll try to delete things that don't exist in the buffer + node = node->next; + if( node->type == asLPT_START ) + { + int subCount = 1; + do + { + node = node->next; + if( node->type == asLPT_START ) + subCount++; + else if( node->type == asLPT_END ) + subCount--; + } while( subCount > 0 ); + return; + } + } } else if( node->type == asLPT_TYPE ) { @@ -5582,11 +6059,11 @@ void asCScriptEngine::DestroySubList(asBYTE *&buffer, asSListPatternNode *&node) // We'll assume the object has been created if any byte in // the memory is different from 0. // TODO: This is not really correct, as bytes may have been - // modified by the constructor, but then an exception + // modified by the constructor, but then an exception // thrown aborting the initialization. The engine // really should be keeping track of which objects has // been successfully initialized. - + for( asUINT n = 0; n < size; n++ ) { if( buffer[n] != 0 ) @@ -5606,7 +6083,7 @@ void asCScriptEngine::DestroySubList(asBYTE *&buffer, asSListPatternNode *&node) // Align the offset to 4 bytes boundary if( asPWORD(buffer) & 0x3 ) buffer += 4 - (asPWORD(buffer) & 0x3); - + // Call the release behaviour void *ptr = *(void**)buffer; if( ptr ) @@ -5657,5 +6134,22 @@ void asCScriptEngine::DestroySubList(asBYTE *&buffer, asSListPatternNode *&node) } } +// internal +asSNameSpace *asCScriptEngine::GetParentNameSpace(asSNameSpace *ns) const +{ + if( ns == 0 ) return 0; + if( ns == nameSpaces[0] ) return 0; + + asCString scope = ns->name; + int pos = scope.FindLast("::"); + if( pos >= 0 ) + { + scope = scope.SubString(0, pos); + return FindNameSpace(scope.AddressOf()); + } + + return nameSpaces[0]; +} + END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_scriptengine.h b/lib/angelscript/source/as_scriptengine.h index e40b48a99..161d22781 100644 --- a/lib/angelscript/source/as_scriptengine.h +++ b/lib/angelscript/source/as_scriptengine.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -70,6 +70,7 @@ public: // Memory management virtual int AddRef() const; virtual int Release() const; + virtual int ShutDownAndRelease(); // Engine properties virtual int SetEngineProperty(asEEngineProp property, asPWORD value); @@ -100,17 +101,18 @@ public: // Type registration virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags); virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset); - virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv); + virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall = 0); virtual int RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall = 0); virtual int RegisterInterface(const char *name); virtual int RegisterInterfaceMethod(const char *intf, const char *declaration); virtual asUINT GetObjectTypeCount() const; virtual asIObjectType *GetObjectTypeByIndex(asUINT index) const; virtual asIObjectType *GetObjectTypeByName(const char *name) const; + virtual asIObjectType *GetObjectTypeByDecl(const char *decl) const; // String factory virtual int RegisterStringFactory(const char *datatype, const asSFuncPtr &factoryFunc, asDWORD callConv, void *objForThiscall = 0); - virtual int GetStringFactoryReturnTypeId() const; + virtual int GetStringFactoryReturnTypeId(asDWORD *flags) const; // Default array type virtual int RegisterDefaultArrayType(const char *type); @@ -160,48 +162,45 @@ public: virtual int GetSizeOfPrimitiveType(int typeId) const; // Script execution - virtual asIScriptContext *CreateContext(); + virtual asIScriptContext *CreateContext(); + virtual void *CreateScriptObject(const asIObjectType *type); + virtual void *CreateScriptObjectCopy(void *obj, const asIObjectType *type); + virtual void *CreateUninitializedScriptObject(const asIObjectType *type); + virtual asIScriptFunction *CreateDelegate(asIScriptFunction *func, void *obj); + virtual int AssignScriptObject(void *dstObj, void *srcObj, const asIObjectType *type); + virtual void ReleaseScriptObject(void *obj, const asIObjectType *type); + virtual void AddRefScriptObject(void *obj, const asIObjectType *type); + virtual int RefCastObject(void *obj, asIObjectType *fromType, asIObjectType *toType, void **newPtr, bool useOnlyImplicitCast = false); #ifdef AS_DEPRECATED -// Deprecated since 2.27.0, 2013-07-18 - virtual void *CreateScriptObject(int typeId); - virtual void *CreateScriptObjectCopy(void *obj, int typeId); - virtual void *CreateUninitializedScriptObject(int typeId); - virtual void AssignScriptObject(void *dstObj, void *srcObj, int typeId); - virtual void ReleaseScriptObject(void *obj, int typeId); - virtual void AddRefScriptObject(void *obj, int typeId); + // Deprecated since 2.30.0, 2014-11-04 + virtual bool IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) const; #endif - virtual void *CreateScriptObject(const asIObjectType *type); - virtual void *CreateScriptObjectCopy(void *obj, const asIObjectType *type); - virtual void *CreateUninitializedScriptObject(const asIObjectType *type); - virtual asIScriptFunction *CreateDelegate(asIScriptFunction *func, void *obj); - virtual void AssignScriptObject(void *dstObj, void *srcObj, const asIObjectType *type); - virtual void ReleaseScriptObject(void *obj, const asIObjectType *type); - virtual void AddRefScriptObject(void *obj, const asIObjectType *type); - // TODO: interface: Should have a method void *CastObject(void *obj, asIObjectType *fromType, asIObjectType *toType); - // For script objects it should simply check if the object implements or derives from the toType - // For application objects it should look for ref cast behaviours and call the matching one - // Once implemented the IsHandleCompatibleWithObject should be removed from the engine - virtual bool IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) const; - asILockableSharedBool *GetWeakRefFlagOfScriptObject(void *obj, const asIObjectType *type) const; + virtual asILockableSharedBool *GetWeakRefFlagOfScriptObject(void *obj, const asIObjectType *type) const; + + // Context pooling + virtual asIScriptContext *RequestContext(); + virtual void ReturnContext(asIScriptContext *ctx); + virtual int SetContextCallbacks(asREQUESTCONTEXTFUNC_t requestCtx, asRETURNCONTEXTFUNC_t returnCtx, void *param = 0); // String interpretation - virtual asETokenClass ParseToken(const char *string, size_t stringLength = 0, int *tokenLength = 0) const; + virtual asETokenClass ParseToken(const char *string, size_t stringLength = 0, asUINT *tokenLength = 0) const; // Garbage collection - virtual int GarbageCollect(asDWORD flags = asGC_FULL_CYCLE); + virtual int GarbageCollect(asDWORD flags = asGC_FULL_CYCLE, asUINT numIterations = 1); virtual void GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected, asUINT *newObjects, asUINT *totalNewDestroyed) const; virtual int NotifyGarbageCollectorOfNewObject(void *obj, asIObjectType *type); virtual int GetObjectInGC(asUINT idx, asUINT *seqNbr, void **obj = 0, asIObjectType **type = 0); virtual void GCEnumCallback(void *reference); // User data - virtual void *SetUserData(void *data, asPWORD type = 0); - virtual void *GetUserData(asPWORD type = 0) const; - virtual void SetEngineUserDataCleanupCallback(asCLEANENGINEFUNC_t callback, asPWORD type = 0); - virtual void SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback); - virtual void SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback); - virtual void SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback); + virtual void *SetUserData(void *data, asPWORD type); + virtual void *GetUserData(asPWORD type) const; + virtual void SetEngineUserDataCleanupCallback(asCLEANENGINEFUNC_t callback, asPWORD type); + virtual void SetModuleUserDataCleanupCallback(asCLEANMODULEFUNC_t callback, asPWORD type); + virtual void SetContextUserDataCleanupCallback(asCLEANCONTEXTFUNC_t callback, asPWORD type); + virtual void SetFunctionUserDataCleanupCallback(asCLEANFUNCTIONFUNC_t callback, asPWORD type); virtual void SetObjectTypeUserDataCleanupCallback(asCLEANOBJECTTYPEFUNC_t callback, asPWORD type); + virtual void SetScriptObjectUserDataCleanupCallback(asCLEANSCRIPTOBJECTFUNC_t callback, asPWORD type); //=========================================================== // internal methods @@ -220,12 +219,12 @@ public: friend class asCByteCode; friend int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine); - int RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv); + int RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall = 0); int RegisterBehaviourToObjectType(asCObjectType *objectType, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv, void *objForThiscall); int VerifyVarTypeNotInFunction(asCScriptFunction *func); - void *CallAlloc(asCObjectType *objType) const; + void *CallAlloc(const asCObjectType *objType) const; void CallFree(void *obj) const; void *CallGlobalFunctionRetPtr(int func) const; @@ -244,11 +243,10 @@ public: void ConstructScriptObjectCopy(void *mem, void *obj, asCObjectType *type); - void CleanupAfterDiscardModule(); + void DeleteDiscardedModules(); - int ClearUnusedTypes(); void RemoveTemplateInstanceType(asCObjectType *t); - void RemoveTypeAndRelatedFromList(asCArray &types, asCObjectType *ot); + void RemoveTypeAndRelatedFromList(asCMap &types, asCObjectType *ot); asCConfigGroup *FindConfigGroupForFunction(int funcId) const; asCConfigGroup *FindConfigGroupForGlobalVar(int gvarId) const; @@ -282,8 +280,9 @@ public: int GetFactoryIdByDecl(const asCObjectType *ot, const char *decl); int GetNextScriptFunctionId(); - void SetScriptFunction(asCScriptFunction *func); - void FreeScriptFunctionId(int id); + void AddScriptFunction(asCScriptFunction *func); + void RemoveScriptFunction(asCScriptFunction *func); + void RemoveFuncdef(asCScriptFunction *func); int ConfigError(int err, const char *funcName, const char *arg1, const char *arg2); @@ -293,11 +292,14 @@ public: void RemoveFromTypeIdMap(asCObjectType *type); bool IsTemplateType(const char *name) const; - asCObjectType *GetTemplateInstanceType(asCObjectType *templateType, asCArray &subTypes); + asCObjectType *GetTemplateInstanceType(asCObjectType *templateType, asCArray &subTypes, asCModule *requestingModule); asCScriptFunction *GenerateTemplateFactoryStub(asCObjectType *templateType, asCObjectType *templateInstanceType, int origFactoryId); bool GenerateNewTemplateFunction(asCObjectType *templateType, asCObjectType *templateInstanceType, asCScriptFunction *templateFunc, asCScriptFunction **newFunc); - void OrphanTemplateInstances(asCObjectType *subType); asCDataType DetermineTypeForTemplate(const asCDataType &orig, asCObjectType *tmpl, asCObjectType *ot); + bool RequireTypeReplacement(asCDataType &type, asCObjectType *templateType); + + asCModule *FindNewOwnerForSharedType(asCObjectType *type, asCModule *mod); + asCModule *FindNewOwnerForSharedFunc(asCScriptFunction *func, asCModule *mod); // String constants // TODO: Must free unused string constants, thus the ref count for each must be tracked @@ -306,13 +308,14 @@ public: // Global property management asCGlobalProperty *AllocateGlobalProperty(); - void FreeUnusedGlobalProperties(); + void RemoveGlobalProperty(asCGlobalProperty *prop); int GetScriptSectionNameIndex(const char *name); // Namespace management asSNameSpace *AddNameSpace(const char *name); - asSNameSpace *FindNameSpace(const char *name); + asSNameSpace *FindNameSpace(const char *name) const; + asSNameSpace *GetParentNameSpace(asSNameSpace *ns) const; //=========================================================== // internal properties @@ -324,14 +327,12 @@ public: asCObjectType *defaultArrayObjectType; asCObjectType scriptTypeBehaviours; asCObjectType functionBehaviours; - asCObjectType objectTypeBehaviours; - asCObjectType globalPropertyBehaviours; // Registered interface asCArray registeredObjTypes; asCArray registeredTypeDefs; asCArray registeredEnums; - asCSymbolTable registeredGlobalProps; // TODO: memory savings: Since there can be only one property with the same name a simpler symbol table should be used + asCSymbolTable registeredGlobalProps; // increases ref count // TODO: memory savings: Since there can be only one property with the same name a simpler symbol table should be used asCSymbolTable registeredGlobalFuncs; asCArray registeredFuncDefs; asCArray registeredTemplateTypes; @@ -339,54 +340,69 @@ public: bool configFailed; // Stores all registered types except funcdefs - asCMap allRegisteredTypes; + asCMap allRegisteredTypes; // increases ref count // Dummy types used to name the subtypes in the template objects asCArray templateSubTypes; // Store information about template types // This list will contain all instances of templates, both registered specialized - // types and those automacially instanciated from scripts - asCArray templateInstanceTypes; + // types and those automacially instantiated from scripts + asCArray templateInstanceTypes; // increases ref count // Store information about list patterns - asCArray listPatternTypes; + asCArray listPatternTypes; // increases ref count // Stores all global properties, both those registered by application, and those declared by scripts. // The id of a global property is the index in this array. - asCArray globalProperties; + asCArray globalProperties; // increases ref count + asCArray freeGlobalPropertyIds; // This map is used to quickly find a property by its memory address // It is used principally during building, cleanup, and garbage detection for script functions - asCMap varAddressMap; - - asCArray freeGlobalPropertyIds; + asCMap varAddressMap; // doesn't increase ref count // Stores all functions, i.e. registered functions, script functions, class methods, behaviours, etc. - asCArray scriptFunctions; + asCArray scriptFunctions; // doesn't increase ref count asCArray freeScriptFunctionIds; asCArray signatureIds; // An array with all module imported functions - asCArray importedFunctions; + asCArray importedFunctions; // doesn't increase ref count asCArray freeImportedFunctionIdxs; - // These resources must be protected for multiple accesses + // Synchronized mutable asCAtomic refCount; + // Synchronized with engineRWLock + // This array holds all live script modules asCArray scriptModules; + // Synchronized with engineRWLock + // This is a pointer to the last module that was requested. It is used for performance + // improvement, since it is common that the same module is accessed many times in a row asCModule *lastModule; + // Synchronized with engineRWLock + // This flag is true if a script is currently being compiled. It is used to prevent multiple + // threads from requesting builds at the same time (without blocking) bool isBuilding; + // Synchronized with engineRWLock + // This array holds modules that have been discard (thus are no longer visible to the application) + // but cannot yet be deleted due to having external references to some of the entities in them + asCArray discardedModules; + // This flag is set to true during compilations of scripts (or loading pre-compiled scripts) + // to delay the validation of template types until the subtypes have been fully declared bool deferValidationOfTemplateTypes; - // Tokenizer is instanciated once to share resources + // Tokenizer is instantiated once to share resources asCTokenizer tok; - // Stores script declared object types - asCArray classTypes; + // Stores shared script declared types (classes, interfaces, enums) + asCArray sharedScriptTypes; // increases ref count // This array stores the template instances types that have been automatically generated from template types asCArray generatedTemplateTypes; // Stores the funcdefs - asCArray funcDefs; + // TODO: 2.30.0: redesign: Only shared funcdefs should be stored here + // a funcdef becomes shared if all arguments and the return type are shared (or application registered) + asCArray funcDefs; // doesn't increase ref count // Stores the names of the script sections for debugging purposes asCArray scriptSectionNames; @@ -409,7 +425,17 @@ public: bool msgCallback; asSSystemFunctionInterface msgCallbackFunc; void *msgCallbackObj; + struct preMessage_t + { + preMessage_t() { isSet = false; } + bool isSet; + asCString message; + asCString scriptname; + int r; + int c; + } preMessage; + // JIt compilation asIJITCompiler *jitCompiler; // Namespaces @@ -423,16 +449,26 @@ public: asCArray stringConstants; asCMap stringToIdMap; + // Callbacks for context pooling + asREQUESTCONTEXTFUNC_t requestCtxFunc; + asRETURNCONTEXTFUNC_t returnCtxFunc; + void *ctxCallbackParam; + // User data asCArray userData; - struct SEngineClean { asPWORD type; asCLEANENGINEFUNC_t cleanFunc; }; - asCArray cleanEngineFuncs; - asCLEANMODULEFUNC_t cleanModuleFunc; - asCLEANCONTEXTFUNC_t cleanContextFunc; - asCLEANFUNCTIONFUNC_t cleanFunctionFunc; - struct SObjTypeClean { asPWORD type; asCLEANOBJECTTYPEFUNC_t cleanFunc; }; - asCArray cleanObjectTypeFuncs; + struct SEngineClean { asPWORD type; asCLEANENGINEFUNC_t cleanFunc; }; + asCArray cleanEngineFuncs; + struct SModuleClean { asPWORD type; asCLEANMODULEFUNC_t cleanFunc; }; + asCArray cleanModuleFuncs; + struct SContextClean { asPWORD type; asCLEANCONTEXTFUNC_t cleanFunc; }; + asCArray cleanContextFuncs; + struct SFunctionClean { asPWORD type; asCLEANFUNCTIONFUNC_t cleanFunc; }; + asCArray cleanFunctionFuncs; + struct SObjTypeClean { asPWORD type; asCLEANOBJECTTYPEFUNC_t cleanFunc; }; + asCArray cleanObjectTypeFuncs; + struct SScriptObjClean { asPWORD type; asCLEANSCRIPTOBJECTFUNC_t cleanFunc; }; + asCArray cleanScriptObjectFuncs; // Synchronization for threads DECLAREREADWRITELOCK(mutable engineRWLock) @@ -460,10 +496,21 @@ public: bool alwaysImplDefaultConstruct; int compilerWarnings; bool disallowValueAssignForRefType; + // TODO: 3.0.0: Remove the alterSyntaxNamedArgs + int alterSyntaxNamedArgs; + bool disableIntegerDivision; + bool disallowEmptyListElements; + // TODO: 3.0.0: Remove the privatePropAsProtected + bool privatePropAsProtected; } ep; // This flag is to allow a quicker shutdown when releasing the engine bool shuttingDown; + + // This flag is set when the engine's destructor is called, this is to + // avoid recursive calls if an object happens to increment/decrement + // the ref counter during shutdown + bool inDestructor; }; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_scriptfunction.cpp b/lib/angelscript/source/as_scriptfunction.cpp index 8b084fbce..874cab558 100644 --- a/lib/angelscript/source/as_scriptfunction.cpp +++ b/lib/angelscript/source/as_scriptfunction.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -105,6 +105,15 @@ static void ScriptFunction_CreateDelegate_Generic(asIScriptGeneric *gen) gen->SetReturnAddress(CreateDelegate(func, obj)); } +// TODO: 2.29.0: operator== +/*static void ScriptFunction_opEquals_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *funcSelf = (asCScriptFunction*)gen->GetObject(); + asCScriptFunction *funcOther = (asCScriptFunction*)gen->GetArgAddress(0); + *(bool*)gen->GetAddressOfReturnLocation() = *funcSelf == *funcOther; +} +*/ + #endif @@ -124,6 +133,8 @@ void RegisterScriptFunction(asCScriptEngine *engine) r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptFunction,GetFlag), asCALL_THISCALL, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptFunction,EnumReferences), asCALL_THISCALL, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptFunction,ReleaseAllHandles), asCALL_THISCALL, 0); asASSERT( r >= 0 ); + // TODO: 2.29.0: Need some way to allow the arg type to adapt when the funcdefs are instantiated +// r = engine->RegisterMethodToObjectType(&engine->functionBehaviours, "bool opEquals(const int &in)", asMETHOD(asCScriptFunction,operator==), asCALL_THISCALL); asASSERT( r >= 0 ); #else r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptFunction_AddRef_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptFunction_Release_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); @@ -132,6 +143,7 @@ void RegisterScriptFunction(asCScriptEngine *engine) r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptFunction_GetFlag_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptFunction_EnumReferences_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptFunction_ReleaseAllHandles_Generic), asCALL_GENERIC, 0); asASSERT( r >= 0 ); +// r = engine->RegisterMethodToObjectType(&engine->functionBehaviours, "bool opEquals(const int &in)", asFUNCTION(ScriptFunction_opEquals_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); #endif // Register the builtin function for creating delegates @@ -214,6 +226,24 @@ asIScriptFunction *asCScriptFunction::GetDelegateFunction() const return funcForDelegate; } +// TODO: 2.29.0: operator== +/* +// internal +bool asCScriptFunction::operator==(const asCScriptFunction &other) const +{ + if( this == &other ) return true; + + if( this->funcType == asFUNC_DELEGATE && other.funcType == asFUNC_DELEGATE ) + { + if( this->objForDelegate == other.objForDelegate && + this->funcForDelegate == other.funcForDelegate ) + return true; + } + + return false; +} +*/ + // internal int asCScriptFunction::RegisterListPattern(const char *decl, asCScriptNode *listNodes) { @@ -243,8 +273,23 @@ int asCScriptFunction::ParseListPattern(asSListPatternNode *&target, const char { if( listNodes->nodeType == snIdentifier ) { - node->next = asNEW(asSListPatternNode)(asLPT_REPEAT); - node = node->next; + asCString token(&decl[listNodes->tokenPos], listNodes->tokenLength); + if( token == "repeat" ) + { + node->next = asNEW(asSListPatternNode)(asLPT_REPEAT); + node = node->next; + } + else if( token == "repeat_same" ) + { + // TODO: list: Should make sure this is a sub-list + node->next = asNEW(asSListPatternNode)(asLPT_REPEAT_SAME); + node = node->next; + } + else + { + // Shouldn't happen as the parser already reported the error + asASSERT(false); + } } else if( listNodes->nodeType == snDataType ) { @@ -287,15 +332,27 @@ int asCScriptFunction::ParseListPattern(asSListPatternNode *&target, const char // internal asCScriptFunction::asCScriptFunction(asCScriptEngine *engine, asCModule *mod, asEFuncType _funcType) { - refCount.set(1); + funcType = _funcType; + if( funcType == asFUNC_DELEGATE ) + { + // Delegates behave like object instances, rather than script code + externalRefCount.set(1); + internalRefCount.set(0); + } + else + { + internalRefCount.set(1); + externalRefCount.set(0); + } + this->engine = engine; this->scriptData = 0; - funcType = _funcType; module = mod; objectType = 0; name = ""; isReadOnly = false; isPrivate = false; + isProtected = false; isFinal = false; isOverride = false; sysFuncIntf = 0; @@ -315,8 +372,8 @@ asCScriptFunction::asCScriptFunction(asCScriptEngine *engine, asCModule *mod, as if( funcType == asFUNC_SCRIPT ) AllocateScriptFunctionData(); - // Notify the GC of script functions - if( (funcType == asFUNC_SCRIPT && mod == 0) || (funcType == asFUNC_DELEGATE) ) + // Notify the GC of delegates + if( funcType == asFUNC_DELEGATE ) engine->gc.AddScriptObjectToGC(this, &engine->functionBehaviours); } @@ -348,35 +405,58 @@ void asCScriptFunction::DeallocateScriptFunctionData() // internal asCScriptFunction::~asCScriptFunction() { - // Imported functions are not reference counted, nor are dummy - // functions that are allocated on the stack + // Dummy functions that are allocated on the stack are not reference counted asASSERT( funcType == asFUNC_DUMMY || - funcType == asFUNC_IMPORTED || - refCount.get() == 0 ); + (externalRefCount.get() == 0 && internalRefCount.get() == 0) ); + + // Remove the script function from the engine's scriptFunctions array here + // Don't remove it before, because there may still be functions referring to it + // by index until now. If it was removed in DestroyInternal, those would not + // be able to release the refcount, thus causing memory leak. + if( engine && id != 0 && funcType != asFUNC_DUMMY ) + engine->RemoveScriptFunction(this); // If the engine pointer is 0, then DestroyInternal has already been called and there is nothing more to do if( engine == 0 ) return; + // TODO: 2.30.0: redesign: Shouldn't this have been done already? DestroyInternal(); - // Tell engine to free the function id. This will make it impossible to - // refer to the function by id. Where this is done, it is quite possible - // they will leak. - if( funcType != -1 && funcType != asFUNC_IMPORTED && id ) - engine->FreeScriptFunctionId(id); - id = 0; - // Finally set the engine pointer to 0 because it must not be accessed again engine = 0; } +// internal +void asCScriptFunction::DestroyHalfCreated() +{ + asASSERT( externalRefCount.get() == 0 && internalRefCount.get() == 1 ); + + // Set the funcType to dummy so the destructor won't complain + funcType = asFUNC_DUMMY; + + // If the bytecode exist remove it before destroying, otherwise it + // will fail when the destructor releases the references as the bytecode + // is not fully constructed. + if( scriptData ) + scriptData->byteCode.SetLength(0); + + asDELETE(this, asCScriptFunction); +} + // internal void asCScriptFunction::DestroyInternal() { // Clean up user data - if( userData && engine->cleanFunctionFunc ) - engine->cleanFunctionFunc(this); - userData = 0; + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n+1] ) + { + for( asUINT c = 0; c < engine->cleanFunctionFuncs.GetLength(); c++ ) + if( engine->cleanFunctionFuncs[c].type == userData[n] ) + engine->cleanFunctionFuncs[c].cleanFunc(this); + } + } + userData.SetLength(0); // Release all references the function holds to other objects ReleaseReferences(); @@ -392,6 +472,12 @@ void asCScriptFunction::DestroyInternal() asDELETE(sysFuncIntf,asSSystemFunctionInterface); sysFuncIntf = 0; + if( objectType ) + { + objectType->ReleaseInternal(); + objectType = 0; + } + DeallocateScriptFunctionData(); // Deallocate list pattern data @@ -413,38 +499,54 @@ int asCScriptFunction::GetId() const int asCScriptFunction::AddRef() const { gcFlag = false; - asASSERT( funcType != asFUNC_IMPORTED ); - return refCount.atomicInc(); + return externalRefCount.atomicInc(); } // interface int asCScriptFunction::Release() const { gcFlag = false; - asASSERT( funcType != asFUNC_IMPORTED ); - int r = refCount.atomicDec(); + int r = externalRefCount.atomicDec(); if( r == 0 && - funcType != asFUNC_FUNCDEF && // Funcdefs are treated as object types and will be deleted by ClearUnusedTypes() funcType != asFUNC_DUMMY ) // Dummy functions are allocated on the stack and cannot be deleted - asDELETE(const_cast(this),asCScriptFunction); + { + // There are no more external references, if there are also no + // internal references then it is time to delete the function + if( internalRefCount.get() == 0 ) + { + // If there are no internal references, then no module is owning the function + // For example if the function was dynamically compiled without adding it to the scope of the module + asASSERT( module == 0 ); + + asDELETE(const_cast(this),asCScriptFunction); + } + } return r; } // internal -void asCScriptFunction::Orphan(asIScriptModule *mod) +int asCScriptFunction::AddRefInternal() { - if( mod && module == mod ) + return internalRefCount.atomicInc(); +} + +// internal +int asCScriptFunction::ReleaseInternal() +{ + int r = internalRefCount.atomicDec(); + if( r == 0 && + funcType != asFUNC_DUMMY ) { - module = 0; - if( funcType == asFUNC_SCRIPT && refCount.get() > 1 ) + // There are no more internal references, if there are also no + // external references then it is time to delete the function + if( externalRefCount.get() == 0 ) { - // This function is being orphaned, so notify the GC so it can check for circular references - engine->gc.AddScriptObjectToGC(this, &engine->functionBehaviours); + asDELETE(const_cast(this),asCScriptFunction); } } - Release(); + return r; } // interface @@ -531,6 +633,12 @@ bool asCScriptFunction::IsPrivate() const return isPrivate; } +// interface +bool asCScriptFunction::IsProtected() const +{ + return isProtected; +} + // internal int asCScriptFunction::GetSpaceNeededForArguments() { @@ -560,7 +668,7 @@ bool asCScriptFunction::DoesReturnOnStack() const } // internal -asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool includeNamespace) const +asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool includeNamespace, bool includeParamNames) const { asCString str; @@ -572,12 +680,12 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl (name == objectType->name || (name.GetLength() > 0 && name[0] == '~') || name == "_beh_0_" || name == "_beh_2_")) ) { - str = returnType.Format(); + str = returnType.Format(nameSpace, includeNamespace); str += " "; } if( objectType && includeObjectName ) { - if( includeNamespace ) + if( includeNamespace && objectType->nameSpace->name != "" ) str += objectType->nameSpace->name + "::"; if( objectType->name != "" ) @@ -585,7 +693,7 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl else str += "_unnamed_type_::"; } - else if( includeNamespace ) + else if( includeNamespace && nameSpace->name != "" ) { str += nameSpace->name + "::"; } @@ -610,7 +718,7 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl asUINT n; for( n = 0; n < parameterTypes.GetLength() - 1; n++ ) { - str += parameterTypes[n].Format(); + str += parameterTypes[n].Format(nameSpace, includeNamespace); if( parameterTypes[n].IsReference() && inOutFlags.GetLength() > n ) { if( inOutFlags[n] == asTM_INREF ) str += "in"; @@ -618,6 +726,12 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl else if( inOutFlags[n] == asTM_INOUTREF ) str += "inout"; } + if( includeParamNames && n < parameterNames.GetLength() && parameterNames[n].GetLength() != 0 ) + { + str += " "; + str += parameterNames[n]; + } + if( defaultArgs.GetLength() > n && defaultArgs[n] ) { asCString tmp; @@ -629,7 +743,7 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl } // Add the last parameter - str += parameterTypes[n].Format(); + str += parameterTypes[n].Format(nameSpace, includeNamespace); if( parameterTypes[n].IsReference() && inOutFlags.GetLength() > n ) { if( inOutFlags[n] == asTM_INREF ) str += "in"; @@ -637,6 +751,12 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl else if( inOutFlags[n] == asTM_INOUTREF ) str += "inout"; } + if( includeParamNames && n < parameterNames.GetLength() && parameterNames[n].GetLength() != 0 ) + { + str += " "; + str += parameterNames[n]; + } + if( defaultArgs.GetLength() > n && defaultArgs[n] ) { asCString tmp; @@ -654,18 +774,33 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl if( listPattern ) { asSListPatternNode *n = listPattern; + bool first = true; while( n ) { if( n->type == asLPT_START ) + { str += " {"; + first = true; + } else if( n->type == asLPT_END ) + { str += " }"; + first = false; + } else if( n->type == asLPT_REPEAT ) str += " repeat"; + else if( n->type == asLPT_REPEAT_SAME ) + str += " repeat_same"; else if( n->type == asLPT_TYPE ) { - str += " "; - str += reinterpret_cast(n)->dataType.Format(); + if( first ) + { + str += " "; + first = false; + } + else + str += ", "; + str += reinterpret_cast(n)->dataType.Format(nameSpace, includeNamespace); } n = n->next; @@ -694,7 +829,7 @@ int asCScriptFunction::FindNextLineWithCode(int line) const { static int cmp(const void *a, const void *b) { return *(int*)a - *(int*)b; } }; - qsort(&lineNbrs[0], lineNbrs.GetLength(), sizeof(int), C::cmp); + std::qsort(&lineNbrs[0], lineNbrs.GetLength(), sizeof(int), C::cmp); if( line < lineNbrs[0] && line < (scriptData->declaredAt&0xFFFFF)) return -1; if( line > lineNbrs[lineNbrs.GetLength()-1] ) return -1; @@ -811,7 +946,7 @@ const char *asCScriptFunction::GetVarDecl(asUINT index, bool includeNamespace) c return 0; asCString *tempString = &asCThreadManager::GetLocalData()->string; - *tempString = scriptData->variables[index]->type.Format(includeNamespace); + *tempString = scriptData->variables[index]->type.Format(nameSpace, includeNamespace); *tempString += " " + scriptData->variables[index]->name; return tempString->AddressOf(); @@ -928,15 +1063,31 @@ void asCScriptFunction::AddReferences() // Only count references if there is any bytecode if( scriptData && scriptData->byteCode.GetLength() ) { - if( returnType.IsObject() ) - returnType.GetObjectType()->AddRef(); + if( returnType.GetObjectType() ) + { + returnType.GetObjectType()->AddRefInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForObjectType(returnType.GetObjectType()); + if( group != 0 ) group->AddRef(); + } for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) - if( parameterTypes[p].IsObject() ) - parameterTypes[p].GetObjectType()->AddRef(); + if( parameterTypes[p].GetObjectType() ) + { + parameterTypes[p].GetObjectType()->AddRefInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForObjectType(parameterTypes[p].GetObjectType()); + if( group != 0 ) group->AddRef(); + } for( asUINT v = 0; v < scriptData->objVariableTypes.GetLength(); v++ ) - scriptData->objVariableTypes[v]->AddRef(); + if( scriptData->objVariableTypes[v] ) // The null handle is also stored, but it doesn't have an object type + { + scriptData->objVariableTypes[v]->AddRefInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForObjectType(scriptData->objVariableTypes[v]); + if( group != 0 ) group->AddRef(); + } // Go through the byte code and add references to all resources used by the function asCArray &bc = scriptData->byteCode; @@ -951,7 +1102,9 @@ void asCScriptFunction::AddReferences() case asBC_RefCpyV: { asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); - objType->AddRef(); + asASSERT( objType ); + if( objType ) + objType->AddRefInternal(); } break; @@ -959,11 +1112,13 @@ void asCScriptFunction::AddReferences() case asBC_ALLOC: { asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); - objType->AddRef(); + asASSERT( objType ); + if( objType ) + objType->AddRefInternal(); - int func = asBC_INTARG(&bc[n]+AS_PTR_SIZE); - if( func ) - engine->scriptFunctions[func]->AddRef(); + int funcId = asBC_INTARG(&bc[n]+AS_PTR_SIZE); + if( funcId ) + engine->scriptFunctions[funcId]->AddRefInternal(); } break; @@ -1002,7 +1157,9 @@ void asCScriptFunction::AddReferences() asCConfigGroup *group = engine->FindConfigGroupForFunction(funcId); if( group != 0 ) group->AddRef(); - engine->scriptFunctions[funcId]->AddRef(); + asASSERT( funcId > 0 ); + if( funcId > 0 ) + engine->scriptFunctions[funcId]->AddRefInternal(); } break; @@ -1010,8 +1167,10 @@ void asCScriptFunction::AddReferences() case asBC_CALL: case asBC_CALLINTF: { - int func = asBC_INTARG(&bc[n]); - engine->scriptFunctions[func]->AddRef(); + int funcId = asBC_INTARG(&bc[n]); + asASSERT( funcId > 0 ); + if( funcId > 0 ) + engine->scriptFunctions[funcId]->AddRefInternal(); } break; @@ -1019,7 +1178,9 @@ void asCScriptFunction::AddReferences() case asBC_FuncPtr: { asCScriptFunction *func = (asCScriptFunction*)asBC_PTRARG(&bc[n]); - func->AddRef(); + asASSERT( func ); + if( func ) + func->AddRefInternal(); } break; } @@ -1035,16 +1196,31 @@ void asCScriptFunction::ReleaseReferences() // Only count references if there is any bytecode if( scriptData && scriptData->byteCode.GetLength() ) { - if( returnType.IsObject() ) - returnType.GetObjectType()->Release(); + if( returnType.GetObjectType() ) + { + returnType.GetObjectType()->ReleaseInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForObjectType(returnType.GetObjectType()); + if( group != 0 ) group->Release(); + } for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) - if( parameterTypes[p].IsObject() ) - parameterTypes[p].GetObjectType()->Release(); + if( parameterTypes[p].GetObjectType() ) + { + parameterTypes[p].GetObjectType()->ReleaseInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForObjectType(parameterTypes[p].GetObjectType()); + if( group != 0 ) group->Release(); + } for( asUINT v = 0; v < scriptData->objVariableTypes.GetLength(); v++ ) - if( scriptData->objVariableTypes[v] ) - scriptData->objVariableTypes[v]->Release(); + if( scriptData->objVariableTypes[v] ) // The null handle is also stored, but it doesn't have an object type + { + scriptData->objVariableTypes[v]->ReleaseInternal(); + + asCConfigGroup *group = engine->FindConfigGroupForObjectType(scriptData->objVariableTypes[v]); + if( group != 0 ) group->Release(); + } // Go through the byte code and release references to all resources used by the function asCArray &bc = scriptData->byteCode; @@ -1060,7 +1236,7 @@ void asCScriptFunction::ReleaseReferences() { asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); if( objType ) - objType->Release(); + objType->ReleaseInternal(); } break; @@ -1069,14 +1245,14 @@ void asCScriptFunction::ReleaseReferences() { asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); if( objType ) - objType->Release(); + objType->ReleaseInternal(); - int func = asBC_INTARG(&bc[n]+AS_PTR_SIZE); - if( func ) + int funcId = asBC_INTARG(&bc[n]+AS_PTR_SIZE); + if( funcId > 0 ) { - asCScriptFunction *fptr = engine->scriptFunctions[func]; + asCScriptFunction *fptr = engine->scriptFunctions[funcId]; if( fptr ) - fptr->Release(); + fptr->ReleaseInternal(); // The engine may have been forced to destroy the function internals early // and this may will make it impossible to find the function by id anymore. @@ -1123,7 +1299,7 @@ void asCScriptFunction::ReleaseReferences() if( group != 0 ) group->Release(); if( funcId ) - engine->scriptFunctions[funcId]->Release(); + engine->scriptFunctions[funcId]->ReleaseInternal(); } break; @@ -1131,12 +1307,12 @@ void asCScriptFunction::ReleaseReferences() case asBC_CALL: case asBC_CALLINTF: { - int func = asBC_INTARG(&bc[n]); - if( func ) + int funcId = asBC_INTARG(&bc[n]); + if( funcId ) { - asCScriptFunction *fptr = engine->scriptFunctions[func]; + asCScriptFunction *fptr = engine->scriptFunctions[funcId]; if( fptr ) - fptr->Release(); + fptr->ReleaseInternal(); // The engine may have been forced to destroy the function internals early // and this may will make it impossible to find the function by id anymore. @@ -1152,7 +1328,7 @@ void asCScriptFunction::ReleaseReferences() { asCScriptFunction *func = (asCScriptFunction*)asBC_PTRARG(&bc[n]); if( func ) - func->Release(); + func->ReleaseInternal(); } break; } @@ -1197,6 +1373,42 @@ asUINT asCScriptFunction::GetParamCount() const } // interface +int asCScriptFunction::GetParam(asUINT index, int *typeId, asDWORD *flags, const char **name, const char **defaultArg) const +{ + if( index >= parameterTypes.GetLength() ) + return asINVALID_ARG; + + if( typeId ) + *typeId = engine->GetTypeIdFromDataType(parameterTypes[index]); + + if( flags ) + { + *flags = inOutFlags[index]; + *flags |= parameterTypes[index].IsReadOnly() ? asTM_CONST : 0; + } + + if( name ) + { + // The parameter names are not stored if loading from bytecode without debug information + if( index < parameterNames.GetLength() ) + *name = parameterNames[index].AddressOf(); + else + *name = 0; + } + + if( defaultArg ) + { + if( index < defaultArgs.GetLength() && defaultArgs[index] ) + *defaultArg = defaultArgs[index]->AddressOf(); + else + *defaultArg = 0; + } + + return asSUCCESS; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2014-04-06, 2.29.0 int asCScriptFunction::GetParamTypeId(asUINT index, asDWORD *flags) const { if( index >= parameterTypes.GetLength() ) @@ -1210,6 +1422,7 @@ int asCScriptFunction::GetParamTypeId(asUINT index, asDWORD *flags) const return engine->GetTypeIdFromDataType(parameterTypes[index]); } +#endif // interface asIScriptEngine *asCScriptFunction::GetEngine() const @@ -1218,10 +1431,10 @@ asIScriptEngine *asCScriptFunction::GetEngine() const } // interface -const char *asCScriptFunction::GetDeclaration(bool includeObjectName, bool includeNamespace) const +const char *asCScriptFunction::GetDeclaration(bool includeObjectName, bool includeNamespace, bool includeParamNames) const { asCString *tempString = &asCThreadManager::GetLocalData()->string; - *tempString = GetDeclarationStr(includeObjectName, includeNamespace); + *tempString = GetDeclarationStr(includeObjectName, includeNamespace, includeParamNames); return tempString->AddressOf(); } @@ -1267,6 +1480,33 @@ void asCScriptFunction::JITCompile() if( !jit ) return; + // Make sure the function has been compiled with JitEntry instructions + // For functions that has JitEntry this will be a quick test + asUINT length; + asDWORD *byteCode = GetByteCode(&length); + asDWORD *end = byteCode + length; + bool foundJitEntry = false; + while( byteCode < end ) + { + // Determine the instruction + asEBCInstr op = asEBCInstr(*(asBYTE*)byteCode); + if( op == asBC_JitEntry ) + { + foundJitEntry = true; + break; + } + + // Move to next instruction + byteCode += asBCTypeSize[asBCInfo[op].type]; + } + + if( !foundJitEntry ) + { + asCString msg; + msg.Format(TXT_NO_JIT_IN_FUNC_s, GetDeclaration()); + engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); + } + // Release the previous function, if any if( scriptData->jitFunction ) { @@ -1277,9 +1517,7 @@ void asCScriptFunction::JITCompile() // Compile for native system int r = jit->CompileFunction(this, &scriptData->jitFunction); if( r < 0 ) - { asASSERT( scriptData->jitFunction == 0 ); - } } // interface @@ -1297,17 +1535,55 @@ asDWORD *asCScriptFunction::GetByteCode(asUINT *length) } // interface -void *asCScriptFunction::SetUserData(void *data) +void *asCScriptFunction::SetUserData(void *data, asPWORD type) { - void *oldData = userData; - userData = data; - return oldData; + // As a thread might add a new new user data at the same time as another + // it is necessary to protect both read and write access to the userData member + ACQUIREEXCLUSIVE(engine->engineRWLock); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + void *oldData = reinterpret_cast(userData[n+1]); + userData[n+1] = reinterpret_cast(data); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return oldData; + } + } + + userData.PushLast(type); + userData.PushLast(reinterpret_cast(data)); + + RELEASEEXCLUSIVE(engine->engineRWLock); + + return 0; } // interface -void *asCScriptFunction::GetUserData() const +void *asCScriptFunction::GetUserData(asPWORD type) const { - return userData; + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + ACQUIRESHARED(engine->engineRWLock); + + for( asUINT n = 0; n < userData.GetLength(); n += 2 ) + { + if( userData[n] == type ) + { + RELEASESHARED(engine->engineRWLock); + return reinterpret_cast(userData[n+1]); + } + } + + RELEASESHARED(engine->engineRWLock); + + return 0; } // internal @@ -1326,7 +1602,9 @@ asCGlobalProperty *asCScriptFunction::GetPropertyByGlobalVarPtr(void *gvarPtr) // internal int asCScriptFunction::GetRefCount() { - return refCount.get(); + asASSERT( funcType == asFUNC_DELEGATE ); + + return externalRefCount.get(); } // internal @@ -1344,192 +1622,24 @@ bool asCScriptFunction::GetFlag() // internal void asCScriptFunction::EnumReferences(asIScriptEngine *) { - // Notify the GC of all object types used - if( returnType.IsObject() ) - engine->GCEnumCallback(returnType.GetObjectType()); - - for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) - if( parameterTypes[p].IsObject() ) - engine->GCEnumCallback(parameterTypes[p].GetObjectType()); - - if( scriptData ) - { - for( asUINT t = 0; t < scriptData->objVariableTypes.GetLength(); t++ ) - engine->GCEnumCallback(scriptData->objVariableTypes[t]); - - // Notify the GC of all script functions that is accessed - asCArray &bc = scriptData->byteCode; - for( asUINT n = 0; n < bc.GetLength(); n += asBCTypeSize[asBCInfo[*(asBYTE*)&bc[n]].type] ) - { - switch( *(asBYTE*)&bc[n] ) - { - case asBC_OBJTYPE: - case asBC_FREE: - case asBC_REFCPY: - case asBC_RefCpyV: - { - asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); - engine->GCEnumCallback(objType); - } - break; - - case asBC_ALLOC: - { - asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); - engine->GCEnumCallback(objType); - - int func = asBC_INTARG(&bc[n]+AS_PTR_SIZE); - if( func ) - engine->GCEnumCallback(engine->scriptFunctions[func]); - } - break; - - case asBC_CALL: - case asBC_CALLINTF: - { - int func = asBC_INTARG(&bc[n]); - if( func ) - engine->GCEnumCallback(engine->scriptFunctions[func]); - } - break; - - // Function pointers - case asBC_FuncPtr: - { - asCScriptFunction *func = (asCScriptFunction*)asBC_PTRARG(&bc[n]); - if( func ) - engine->GCEnumCallback(func); - } - break; - - // Global variables - case asBC_PGA: - case asBC_PshGPtr: - case asBC_LDG: - case asBC_PshG4: - case asBC_LdGRdR4: - case asBC_CpyGtoV4: - case asBC_CpyVtoG4: - case asBC_SetG4: - // Need to enumerate the reference for each global variable - { - // TODO: optimize: Keep an array of accessed global properties - void *gvarPtr = (void*)asBC_PTRARG(&bc[n]); - asCGlobalProperty *prop = GetPropertyByGlobalVarPtr(gvarPtr); - - engine->GCEnumCallback(prop); - } - break; - } - } - } + asASSERT( funcType == asFUNC_DELEGATE ); // Delegate if( objForDelegate ) engine->GCEnumCallback(objForDelegate); - if( funcForDelegate ) - engine->GCEnumCallback(funcForDelegate); } // internal void asCScriptFunction::ReleaseAllHandles(asIScriptEngine *) { + asASSERT( funcType == asFUNC_DELEGATE ); + // Release paramaters - if( scriptData && scriptData->byteCode.GetLength() ) - { - if( returnType.IsObject() ) - { - returnType.GetObjectType()->Release(); - returnType = asCDataType::CreatePrimitive(ttVoid, false); - } - - for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) - if( parameterTypes[p].IsObject() ) - { - parameterTypes[p].GetObjectType()->Release(); - parameterTypes[p] = asCDataType::CreatePrimitive(ttInt, false); - } - - for( asUINT n = 0; n < scriptData->objVariableTypes.GetLength(); n++ ) - scriptData->objVariableTypes[n]->Release(); - scriptData->objVariableTypes.SetLength(0); - - // Release all script functions - asCArray &bc = scriptData->byteCode; - for( asUINT n = 0; n < bc.GetLength(); n += asBCTypeSize[asBCInfo[*(asBYTE*)&bc[n]].type] ) - { - switch( *(asBYTE*)&bc[n] ) - { - // Object types - case asBC_OBJTYPE: - case asBC_FREE: - case asBC_REFCPY: - case asBC_RefCpyV: - { - asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); - if( objType ) - { - objType->Release(); - *(asPWORD*)&bc[n+1] = 0; - } - } - break; - - case asBC_ALLOC: - { - asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&bc[n]); - if( objType ) - { - objType->Release(); - *(asPWORD*)&bc[n+1] = 0; - } - - int func = asBC_INTARG(&bc[n]+AS_PTR_SIZE); - if( func ) - { - engine->scriptFunctions[func]->Release(); - bc[n+AS_PTR_SIZE+1] = 0; - } - } - break; - - case asBC_CALL: - case asBC_CALLINTF: - { - int func = asBC_INTARG(&bc[n]); - if( func ) - { - engine->scriptFunctions[func]->Release(); - bc[n+1] = 0; - } - } - break; - - // Function pointers - case asBC_FuncPtr: - { - asCScriptFunction *func = (asCScriptFunction*)asBC_PTRARG(&bc[n]); - if( func ) - { - func->Release(); - *(asPWORD*)&bc[n+1] = 0; - } - } - break; - - // The global variables are not released here. It is enough that the global - // variable itself release the function to break the circle - } - } - } // Delegate if( objForDelegate ) engine->ReleaseScriptObject(objForDelegate, funcForDelegate->GetObjectType()); objForDelegate = 0; - if( funcForDelegate ) - funcForDelegate->Release(); - funcForDelegate = 0; } // internal @@ -1539,6 +1649,7 @@ bool asCScriptFunction::IsShared() const if( funcType == asFUNC_SYSTEM ) return true; // All class methods for shared classes are also shared + asASSERT( objectType == 0 || objectType->engine == engine || objectType->engine == 0 ); if( objectType && (objectType->flags & asOBJ_SHARED) ) return true; // Functions that have been specifically marked as shared are shared diff --git a/lib/angelscript/source/as_scriptfunction.h b/lib/angelscript/source/as_scriptfunction.h index 1bc2a8736..a5044d859 100644 --- a/lib/angelscript/source/as_scriptfunction.h +++ b/lib/angelscript/source/as_scriptfunction.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -66,6 +66,7 @@ struct asSScriptVariable enum asEListPatternNodeType { asLPT_REPEAT, + asLPT_REPEAT_SAME, asLPT_START, asLPT_END, asLPT_TYPE @@ -134,14 +135,19 @@ public: const char *GetObjectName() const; const char *GetName() const; const char *GetNamespace() const; - const char *GetDeclaration(bool includeObjectName = true, bool includeNamespace = false) const; + const char *GetDeclaration(bool includeObjectName = true, bool includeNamespace = false, bool includeParamNames = false) const; bool IsReadOnly() const; bool IsPrivate() const; + bool IsProtected() const; bool IsFinal() const; bool IsOverride() const; bool IsShared() const; asUINT GetParamCount() const; + int GetParam(asUINT index, int *typeId, asDWORD *flags = 0, const char **name = 0, const char **defaultArg = 0) const; +#ifdef AS_DEPRECATED + // Deprecated, since 2.29.0, 2014-04-06 int GetParamTypeId(asUINT index, asDWORD *flags = 0) const; +#endif int GetReturnTypeId(asDWORD *flags = 0) const; // Type id for function pointers @@ -163,8 +169,8 @@ public: asDWORD *GetByteCode(asUINT *length = 0); // User data - void *SetUserData(void *userData); - void *GetUserData() const; + void *SetUserData(void *userData, asPWORD type); + void *GetUserData(asPWORD type) const; public: //----------------------------------- @@ -173,14 +179,28 @@ public: asCScriptFunction(asCScriptEngine *engine, asCModule *mod, asEFuncType funcType); ~asCScriptFunction(); + // Keep an internal reference counter to separate references coming from + // application or script objects and references coming from the script code + int AddRefInternal(); + int ReleaseInternal(); + + void DestroyHalfCreated(); + + // TODO: 2.29.0: operator== + // TODO: 2.29.0: The asIScriptFunction should provide operator== and operator!= that should do a + // a value comparison. Two delegate objects that point to the same object and class method should compare as equal + // TODO: 2.29.0: The operator== should also be provided in script as opEquals to allow the same comparison in script + // To do this we'll need some way to adapt the argtype for opEquals for each funcdef, preferrably without instantiating lots of different methods + // Perhaps reusing 'auto' to mean the same type as the object + //bool operator==(const asCScriptFunction &other) const; + void DestroyInternal(); - void Orphan(asIScriptModule *mod); void AddVariable(asCString &name, asCDataType &type, int stackOffset); int GetSpaceNeededForArguments(); int GetSpaceNeededForReturnValue(); - asCString GetDeclarationStr(bool includeObjectName = true, bool includeNamespace = false) const; + asCString GetDeclarationStr(bool includeObjectName = true, bool includeNamespace = false, bool includeParamNames = false) const; int GetLineNumber(int programPosition, int *sectionIdx); void ComputeSignatureId(); bool IsSignatureEqual(const asCScriptFunction *func) const; @@ -209,7 +229,7 @@ public: asCGlobalProperty *GetPropertyByGlobalVarPtr(void *gvarPtr); - // GC methods + // GC methods (for delegates) int GetRefCount(); void SetFlag(); bool GetFlag(); @@ -220,21 +240,24 @@ public: //----------------------------------- // Properties - mutable asCAtomic refCount; + mutable asCAtomic externalRefCount; // Used for external referneces + asCAtomic internalRefCount; // Used for internal references mutable bool gcFlag; asCScriptEngine *engine; asCModule *module; - void *userData; + asCArray userData; // Function signature asCString name; asCDataType returnType; asCArray parameterTypes; + asCArray parameterNames; asCArray inOutFlags; asCArray defaultArgs; bool isReadOnly; bool isPrivate; + bool isProtected; bool isFinal; bool isOverride; asCObjectType *objectType; @@ -264,7 +287,7 @@ public: // The stack space needed for the local variables asDWORD variableSpace; - // These hold information objects and function pointers, including temporary + // These hold information on objects and function pointers, including temporary // variables used by exception handler and when saving bytecode asCArray objVariableTypes; asCArray funcVariableTypes; @@ -289,7 +312,7 @@ public: asCArray lineNumbers; // Store the script section where the code was declared int scriptSectionIdx; - // Store the location where the function was declared + // Store the location where the function was declared (row in the lower 20 bits, and column in the upper 12) int declaredAt; // Store position/index pairs if the bytecode is compiled from multiple script sections asCArray sectionIdxs; diff --git a/lib/angelscript/source/as_scriptnode.h b/lib/angelscript/source/as_scriptnode.h index e66a639be..66f7bac03 100644 --- a/lib/angelscript/source/as_scriptnode.h +++ b/lib/angelscript/source/as_scriptnode.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -88,7 +88,8 @@ enum eScriptNode snVirtualProperty, snNamespace, snMixin, - snListPattern + snListPattern, + snNamedArgument }; struct sToken diff --git a/lib/angelscript/source/as_scriptobject.cpp b/lib/angelscript/source/as_scriptobject.cpp index fa07188ca..89cc4d16e 100644 --- a/lib/angelscript/source/as_scriptobject.cpp +++ b/lib/angelscript/source/as_scriptobject.cpp @@ -44,11 +44,6 @@ asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngi int r = 0; bool isNested = false; - // TODO: runtime optimize: There should be a pool for the context so it doesn't - // have to be allocated just for creating the script object - - // TODO: It must be possible for the application to debug the creation of the object too - // Use nested call in the context if there is an active context ctx = asGetActiveContext(); if( ctx ) @@ -63,9 +58,13 @@ asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngi if( ctx == 0 ) { - r = engine->CreateContext(&ctx, true); - if( r < 0 ) + // Request a context from the engine + ctx = engine->RequestContext(); + if( ctx == 0 ) + { + // TODO: How to best report this failure? return 0; + } } r = ctx->Prepare(engine->scriptFunctions[objType->beh.factory]); @@ -74,7 +73,7 @@ asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngi if( isNested ) ctx->PopState(); else - ctx->Release(); + engine->ReturnContext(ctx); return 0; } @@ -105,7 +104,7 @@ asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngi ctx->Abort(); } else - ctx->Release(); + engine->ReturnContext(ctx); return 0; } @@ -117,7 +116,7 @@ asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngi if( isNested ) ctx->PopState(); else - ctx->Release(); + engine->ReturnContext(ctx); return ptr; } @@ -241,7 +240,7 @@ asCScriptObject::asCScriptObject(asCObjectType *ot, bool doInitialize) objType = ot; objType->AddRef(); isDestructCalled = false; - weakRefFlag = 0; + extra = 0; hasRefCountReachedZero = false; // Notify the garbage collector of this object @@ -250,7 +249,7 @@ asCScriptObject::asCScriptObject(asCObjectType *ot, bool doInitialize) // Initialize members to zero. Technically we only need to zero the pointer // members, but just the memset is faster than having to loop and check the datatypes - memset(this+1, 0, objType->size - sizeof(asCScriptObject)); + memset((void*)(this+1), 0, objType->size - sizeof(asCScriptObject)); if( doInitialize ) { @@ -299,15 +298,40 @@ void asCScriptObject::Destruct() this->~asCScriptObject(); // Free the memory +#ifndef WIP_16BYTE_ALIGN userFree(this); +#else + // Script object memory is allocated through asCScriptEngine::CallAlloc() + // This free call must match the allocator used in CallAlloc(). + userFreeAligned(this); +#endif } asCScriptObject::~asCScriptObject() { - if( weakRefFlag ) + if( extra ) { - weakRefFlag->Release(); - weakRefFlag = 0; + if( extra->weakRefFlag ) + { + extra->weakRefFlag->Release(); + extra->weakRefFlag = 0; + } + + if( objType->engine ) + { + // Clean the user data + for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 ) + { + if( extra->userData[n+1] ) + { + for( asUINT c = 0; c < objType->engine->cleanScriptObjectFuncs.GetLength(); c++ ) + if( objType->engine->cleanScriptObjectFuncs[c].type == extra->userData[n] ) + objType->engine->cleanScriptObjectFuncs[c].cleanFunc(this); + } + } + } + + asDELETE(extra, SExtra); } // The engine pointer should be available from the objectType @@ -361,8 +385,8 @@ asILockableSharedBool *asCScriptObject::GetWeakRefFlag() const // If the object's refCount has already reached zero then the object is already // about to be destroyed so it's ok to return null if the weakRefFlag doesn't already // exist - if( weakRefFlag || hasRefCountReachedZero ) - return weakRefFlag; + if( (extra && extra->weakRefFlag) || hasRefCountReachedZero ) + return extra->weakRefFlag; // Lock globally so no other thread can attempt // to create a shared bool at the same time. @@ -373,12 +397,77 @@ asILockableSharedBool *asCScriptObject::GetWeakRefFlag() const // Make sure another thread didn't create the // flag while we waited for the lock - if( !weakRefFlag ) - weakRefFlag = asNEW(asCLockableSharedBool); + if( !extra ) + extra = asNEW(SExtra); + if( !extra->weakRefFlag ) + extra->weakRefFlag = asNEW(asCLockableSharedBool); asReleaseExclusiveLock(); - return weakRefFlag; + return extra->weakRefFlag; +} + +void *asCScriptObject::GetUserData(asPWORD type) const +{ + if( !extra ) + return 0; + + // There may be multiple threads reading, but when + // setting the user data nobody must be reading. + // TODO: runtime optimize: Would it be worth it to have a rwlock per object type? + asAcquireSharedLock(); + + for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 ) + { + if( extra->userData[n] == type ) + { + void *userData = reinterpret_cast(extra->userData[n+1]); + asReleaseSharedLock(); + return userData; + } + } + + asReleaseSharedLock(); + + return 0; +} + +void *asCScriptObject::SetUserData(void *data, asPWORD type) +{ + // Lock globally so no other thread can attempt + // to manipulate the extra data at the same time. + // TODO: runtime optimize: Instead of locking globally, it would be possible to have + // a critical section per object type. This would reduce the + // chances of two threads lock on the same critical section. + asAcquireExclusiveLock(); + + // Make sure another thread didn't create the + // flag while we waited for the lock + if( !extra ) + extra = asNEW(SExtra); + + // It is not intended to store a lot of different types of userdata, + // so a more complex structure like a associative map would just have + // more overhead than a simple array. + for( asUINT n = 0; n < extra->userData.GetLength(); n += 2 ) + { + if( extra->userData[n] == type ) + { + void *oldData = reinterpret_cast(extra->userData[n+1]); + extra->userData[n+1] = reinterpret_cast(data); + + asReleaseExclusiveLock(); + + return oldData; + } + } + + extra->userData.PushLast(type); + extra->userData.PushLast(reinterpret_cast(data)); + + asReleaseExclusiveLock(); + + return 0; } asIScriptEngine *asCScriptObject::GetEngine() const @@ -417,14 +506,14 @@ int asCScriptObject::Release() const // is ok to check the existance of the weakRefFlag without locking here // because if the refCount is 1 then no other thread is currently // creating the weakRefFlag. - if( refCount.get() == 1 && weakRefFlag ) + if( refCount.get() == 1 && extra && extra->weakRefFlag ) { // Set the flag to tell others that the object is no longer alive // We must do this before decreasing the refCount to 0 so we don't // end up with a race condition between this thread attempting to // destroy the object and the other that temporary added a strong // ref from the weak ref. - weakRefFlag->Set(true); + extra->weakRefFlag->Set(true); } // Call the script destructor behaviour if the reference counter is 1. @@ -491,10 +580,13 @@ void asCScriptObject::CallDestructor() if( ctx == 0 ) { - // Setup a context for calling the default constructor - asCScriptEngine *engine = objType->engine; - int r = engine->CreateContext(&ctx, true); - if( r < 0 ) return; + // Request a context from the engine + ctx = objType->engine->RequestContext(); + if( ctx == 0 ) + { + // TODO: How to best report this failure? + return; + } } } @@ -538,7 +630,10 @@ void asCScriptObject::CallDestructor() ctx->Abort(); } else - ctx->Release(); + { + // Return the context to engine + objType->engine->ReturnContext(ctx); + } } } @@ -726,9 +821,13 @@ asCScriptObject &asCScriptObject::operator=(const asCScriptObject &other) if( ctx == 0 ) { - r = engine->CreateContext(&ctx, true); - if( r < 0 ) + // Request a context from the engine + ctx = engine->RequestContext(); + if( ctx == 0 ) + { + // TODO: How to best report this failure? return *this; + } } r = ctx->Prepare(engine->scriptFunctions[objType->beh.copy]); @@ -737,7 +836,8 @@ asCScriptObject &asCScriptObject::operator=(const asCScriptObject &other) if( isNested ) ctx->PopState(); else - ctx->Release(); + engine->ReturnContext(ctx); + // TODO: How to best report this failure? return *this; } @@ -773,14 +873,20 @@ asCScriptObject &asCScriptObject::operator=(const asCScriptObject &other) ctx->Abort(); } else - ctx->Release(); + { + // Return the context to the engine + engine->ReturnContext(ctx); + } return *this; } if( isNested ) ctx->PopState(); else - ctx->Release(); + { + // Return the context to the engine + engine->ReturnContext(ctx); + } } } diff --git a/lib/angelscript/source/as_scriptobject.h b/lib/angelscript/source/as_scriptobject.h index 7e3d98aad..eab7ca64b 100644 --- a/lib/angelscript/source/as_scriptobject.h +++ b/lib/angelscript/source/as_scriptobject.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -76,11 +76,11 @@ public: //=================================== // From asIScriptObject //=================================== - asIScriptEngine *GetEngine() const; // Memory management - int AddRef() const; - int Release() const; + int AddRef() const; + int Release() const; + asILockableSharedBool *GetWeakRefFlag() const; // Type info int GetTypeId() const; @@ -92,7 +92,13 @@ public: const char *GetPropertyName(asUINT prop) const; void *GetAddressOfProperty(asUINT prop); - int CopyFrom(asIScriptObject *other); + // Miscellaneous + asIScriptEngine *GetEngine() const; + int CopyFrom(asIScriptObject *other); + + // User data + void *SetUserData(void *data, asPWORD type = 0); + void *GetUserData(asPWORD type = 0) const; //==================================== // Internal @@ -110,9 +116,6 @@ public: void EnumReferences(asIScriptEngine *engine); void ReleaseAllHandles(asIScriptEngine *engine); - // Weakref methods - asILockableSharedBool *GetWeakRefFlag() const; - // Used for properties void *AllocateUninitializedObject(asCObjectType *objType, asCScriptEngine *engine); void FreeObject(void *ptr, asCObjectType *objType, asCScriptEngine *engine); @@ -124,15 +127,26 @@ public: //============================================= // Properties //============================================= -public: - asCObjectType *objType; - protected: + friend class asCContext; + asCObjectType *objType; + mutable asCAtomic refCount; - mutable asBYTE gcFlag:1; - mutable asBYTE hasRefCountReachedZero:1; - bool isDestructCalled; - mutable asCLockableSharedBool *weakRefFlag; + mutable asBYTE gcFlag:1; + mutable asBYTE hasRefCountReachedZero:1; + bool isDestructCalled; + + // Most script classes instances won't have neither the weakRefFlags nor + // userData so we only allocate this if requested. Even when used it is + // not something that will be accessed all the time so having the extra + // indirection will not affect the performance significantly. + struct SExtra + { + SExtra() : weakRefFlag(0) {}; + asCLockableSharedBool *weakRefFlag; + asCArray userData; + }; + mutable SExtra *extra; }; void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self); diff --git a/lib/angelscript/source/as_string.cpp b/lib/angelscript/source/as_string.cpp index fcbffe14b..aaa280c23 100644 --- a/lib/angelscript/source/as_string.cpp +++ b/lib/angelscript/source/as_string.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -31,8 +31,8 @@ #include "as_config.h" #include // va_list, va_start(), etc -#include // strtod(), strtol() -#include // some compilers declare memcpy() here +#include // strtod(), strtol() +#include // some compilers declare memcpy() here #if !defined(AS_NO_MEMORY_H) #include @@ -56,6 +56,26 @@ asCString::asCString(const asCString &str) Assign(str.AddressOf(), str.length); } +#ifdef AS_CAN_USE_CPP11 +asCString::asCString(asCString &&str) +{ + if( str.length <= 11 ) + { + length = str.length; + memcpy(local, str.local, length); + local[length] = 0; + } + else + { + dynamic = str.dynamic; + length = str.length; + } + + str.dynamic = 0; + str.length = 0; +} +#endif // c++11 + asCString::asCString(const char *str, size_t len) { length = 0; @@ -183,6 +203,37 @@ asCString &asCString::operator =(const asCString &str) return *this; } +#ifdef AS_CAN_USE_CPP11 +asCString &asCString::operator =(asCString &&str) +{ + if( this != &str ) + { + if( length > 11 && dynamic ) + { + asDELETEARRAY(dynamic); + } + + if ( str.length <= 11 ) + { + length = str.length; + + memcpy(local, str.local, length); + local[length] = 0; + } + else + { + dynamic = str.dynamic; + length = str.length; + } + + str.dynamic = 0; + str.length = 0; + } + + return *this; +} +#endif // c++11 + asCString &asCString::operator =(char ch) { Assign(&ch, 1); diff --git a/lib/angelscript/source/as_string.h b/lib/angelscript/source/as_string.h index 05e677e3e..0ee7469c0 100644 --- a/lib/angelscript/source/as_string.h +++ b/lib/angelscript/source/as_string.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2012 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -38,14 +38,17 @@ #include #include -// TODO: optimize: On compilers with C++11 support the string class should take advantage of the move operator && - class asCString { public: asCString(); ~asCString(); +#ifdef AS_CAN_USE_CPP11 + asCString(asCString &&); + asCString &operator =(asCString &&); +#endif // c++11 + asCString(const asCString &); asCString(const char *); asCString(const char *, size_t length); diff --git a/lib/angelscript/source/as_string_util.cpp b/lib/angelscript/source/as_string_util.cpp index 9a5d692a9..8ee210dc5 100644 --- a/lib/angelscript/source/as_string_util.cpp +++ b/lib/angelscript/source/as_string_util.cpp @@ -78,7 +78,7 @@ double asStringScanDouble(const char *string, size_t *numScanned) { // I decided to do my own implementation of strtod() because this function // doesn't seem to be present on all systems. iOS 5 for example doesn't appear - // to include the function in the standard lib. + // to include the function in the standard lib. // Another reason is that the standard implementation of strtod() is dependent // on the locale on some systems, i.e. it may use comma instead of dot for diff --git a/lib/angelscript/source/as_symboltable.h b/lib/angelscript/source/as_symboltable.h index 114a127f6..11a7115c0 100644 --- a/lib/angelscript/source/as_symboltable.h +++ b/lib/angelscript/source/as_symboltable.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2012-2013 Andreas Jonsson + Copyright (c) 2012-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -59,6 +59,7 @@ BEGIN_AS_NAMESPACE struct asIFilter { virtual bool operator()(const void*) const = 0; + virtual ~asIFilter() {}; }; @@ -107,7 +108,7 @@ public: typedef asCSymbolTableIterator iterator; typedef asCSymbolTableIterator const_iterator; - asCSymbolTable(unsigned int initialCapacity = 0); + asCSymbolTable(asUINT initialCapacity = 0); int GetFirstIndex(const asSNameSpace *ns, const asCString &name, const asIFilter &comparator) const; int GetFirstIndex(const asSNameSpace *ns, const asCString &name) const; @@ -118,22 +119,22 @@ public: T* GetFirst(const asSNameSpace *ns, const asCString &name, const asIFilter &comparator) const; T* GetFirst(const asSNameSpace *ns, const asCString &name); const T* GetFirst(const asSNameSpace *ns, const asCString &name) const; - T* Get(unsigned int index); - const T* Get(unsigned int index) const; + T* Get(asUINT index); + const T* Get(asUINT index) const; T* GetLast(); const T* GetLast() const; - const asCArray &GetIndexes(const asSNameSpace *ns, const asCString &name) const; + const asCArray &GetIndexes(const asSNameSpace *ns, const asCString &name) const; - int Put(T* entry); + asUINT Put(T* entry); - unsigned int GetSize() const; + asUINT GetSize() const; void SwapWith(asCSymbolTable &other); void Clear(); - bool Erase(unsigned int idx); - void Allocate(unsigned int elem_cnt, bool keep_data); + bool Erase(asUINT idx); + void Allocate(asUINT elem_cnt, bool keep_data); iterator List(); const_iterator List() const; @@ -146,11 +147,11 @@ private: friend class asCSymbolTableIterator; void GetKey(const T *entry, asSNameSpaceNamePair &key) const; - bool CheckIdx(unsigned idx) const; + bool CheckIdx(asUINT idx) const; - asCMap > m_map; - asCArray m_entries; - unsigned int m_size; + asCMap > m_map; + asCArray m_entries; + unsigned int m_size; }; @@ -162,7 +163,7 @@ void asCSymbolTable::SwapWith(asCSymbolTable &other) m_map.SwapWith(other.m_map); m_entries.SwapWith(other.m_entries); - unsigned int tmp = m_size; + asUINT tmp = m_size; m_size = other.m_size; other.m_size = tmp; } @@ -173,7 +174,7 @@ void asCSymbolTable::SwapWith(asCSymbolTable &other) // Constructor // initialCapacity gives the number of entries to allocate in advance template -asCSymbolTable::asCSymbolTable(unsigned initialCapacity) : m_entries(initialCapacity) +asCSymbolTable::asCSymbolTable(asUINT initialCapacity) : m_entries(initialCapacity) { m_size = 0; } @@ -188,11 +189,11 @@ int asCSymbolTable::GetFirstIndex( { asSNameSpaceNamePair key(ns, name); - asSMapNode > *cursor; + asSMapNode > *cursor; if( m_map.MoveTo(&cursor, key) ) { - const asCArray &arr = m_map.GetValue(cursor); - for( unsigned int n = 0; n < arr.GetLength(); n++ ) + const asCArray &arr = m_map.GetValue(cursor); + for( asUINT n = 0; n < arr.GetLength(); n++ ) { T *entry = m_entries[arr[n]]; if( entry && filter(entry) ) @@ -206,15 +207,15 @@ int asCSymbolTable::GetFirstIndex( template -const asCArray &asCSymbolTable::GetIndexes(const asSNameSpace *ns, const asCString &name) const +const asCArray &asCSymbolTable::GetIndexes(const asSNameSpace *ns, const asCString &name) const { asSNameSpaceNamePair key(ns, name); - asSMapNode > *cursor; + asSMapNode > *cursor; if( m_map.MoveTo(&cursor, key) ) return m_map.GetValue(cursor); - static asCArray dummy; + static asCArray dummy; return dummy; } @@ -237,7 +238,7 @@ int asCSymbolTable::GetFirstIndex(const asSNameSpace *ns, const asCString &na { asSNameSpaceNamePair key(ns, name); - asSMapNode > *cursor; + asSMapNode > *cursor; if( m_map.MoveTo(&cursor, key) ) return m_map.GetValue(cursor)[0]; @@ -252,7 +253,7 @@ int asCSymbolTable::GetFirstIndex(const asSNameSpace *ns, const asCString &na template int asCSymbolTable::GetIndex(const T* entry) const { - for( unsigned int n = 0; n < m_entries.GetLength(); n++ ) + for( asUINT n = 0; n < m_entries.GetLength(); n++ ) if( m_entries[n] == entry ) return n; @@ -265,7 +266,7 @@ int asCSymbolTable::GetIndex(const T* entry) const template -T* asCSymbolTable::Get(unsigned idx) +T* asCSymbolTable::Get(asUINT idx) { if( !CheckIdx(idx) ) return 0; @@ -274,7 +275,7 @@ T* asCSymbolTable::Get(unsigned idx) } template -const T* asCSymbolTable::Get(unsigned idx) const +const T* asCSymbolTable::Get(asUINT idx) const { return const_cast< asCSymbolTable* >(this)->Get(idx); } @@ -331,7 +332,7 @@ void asCSymbolTable::Clear() // Pre-allocate slots for elemCnt entries template -void asCSymbolTable::Allocate(unsigned elemCnt, bool keepData) +void asCSymbolTable::Allocate(asUINT elemCnt, bool keepData) { asASSERT( elemCnt >= m_entries.GetLength() ); m_entries.Allocate(elemCnt, keepData); @@ -342,7 +343,7 @@ void asCSymbolTable::Allocate(unsigned elemCnt, bool keepData) template -bool asCSymbolTable::Erase(unsigned idx) +bool asCSymbolTable::Erase(asUINT idx) { if( !CheckIdx(idx) ) { @@ -355,26 +356,14 @@ bool asCSymbolTable::Erase(unsigned idx) if( !entry ) return false; - if( idx == m_entries.GetLength() - 1 ) - { - m_entries.PopLast(); - - // TODO: Should remove all trailing empty slots - } - else - { - // TODO: Must pack or reuse empty slots - m_entries[idx] = 0; - } - m_size--; - + // Remove the symbol from the lookup map asSNameSpaceNamePair key; GetKey(entry, key); - asSMapNode > *cursor; + asSMapNode > *cursor; if( m_map.MoveTo(&cursor, key) ) { - asCArray &arr = m_map.GetValue(cursor); + asCArray &arr = m_map.GetValue(cursor); arr.RemoveValue(idx); if( arr.GetLength() == 0 ) m_map.Erase(cursor); @@ -382,6 +371,28 @@ bool asCSymbolTable::Erase(unsigned idx) else asASSERT(false); + // Remove the symbol from the indexed array + if( idx == m_entries.GetLength() - 1 ) + m_entries.PopLast(); + else + { + // Must keep the array packed + int prevIdx = int(m_entries.GetLength()-1); + m_entries[idx] = m_entries.PopLast(); + + // Update the index in the lookup map + entry = m_entries[idx]; + GetKey(entry, key); + if( m_map.MoveTo(&cursor, key) ) + { + asCArray &arr = m_map.GetValue(cursor); + arr[arr.IndexOf(prevIdx)] = idx; + } + else + asASSERT(false); + } + m_size--; + return true; } @@ -389,18 +400,18 @@ bool asCSymbolTable::Erase(unsigned idx) template -int asCSymbolTable::Put(T *entry) +asUINT asCSymbolTable::Put(T *entry) { - unsigned int idx = (unsigned int)(m_entries.GetLength()); + asUINT idx = m_entries.GetLength(); asSNameSpaceNamePair key; GetKey(entry, key); - asSMapNode > *cursor; + asSMapNode > *cursor; if( m_map.MoveTo(&cursor, key) ) m_map.GetValue(cursor).PushLast(idx); else { - asCArray arr(1); + asCArray arr(1); arr.PushLast(idx); m_map.Insert(key, arr); } @@ -424,7 +435,7 @@ void asCSymbolTable::GetKey(const T *entry, asSNameSpaceNamePair &key) const template -unsigned int asCSymbolTable::GetSize() const +asUINT asCSymbolTable::GetSize() const { return m_size; } @@ -433,7 +444,7 @@ unsigned int asCSymbolTable::GetSize() const template -bool asCSymbolTable::CheckIdx(unsigned int idx) const +bool asCSymbolTable::CheckIdx(asUINT idx) const { return idx < m_entries.GetLength(); } @@ -444,9 +455,9 @@ bool asCSymbolTable::CheckIdx(unsigned int idx) const template int asCSymbolTable::GetLastIndex() const { - unsigned int idx = (unsigned int)(m_entries.GetLength()) - 1; - asASSERT( idx == asUINT(-1) || m_entries[idx] ); - return int(idx); + int idx = int(m_entries.GetLength()) - 1; + asASSERT( idx == -1 || m_entries[idx] ); + return idx; } @@ -475,7 +486,7 @@ typename asCSymbolTable::const_iterator asCSymbolTable::List() const template asCSymbolTableIterator::asCSymbolTableIterator(asCSymbolTable *table) : m_table(table), m_idx(0) { - unsigned int sz = (unsigned int)(m_table->m_entries.GetLength()); + asUINT sz = m_table->m_entries.GetLength(); while( m_idx < sz && m_table->m_entries[m_idx] == 0 ) m_idx++; } @@ -523,7 +534,7 @@ asCSymbolTableIterator::operator bool() const template void asCSymbolTableIterator::Next() { - unsigned int sz = (unsigned int)(m_table->m_entries.GetLength()); + asUINT sz = m_table->m_entries.GetLength(); m_idx++; while( m_idx < sz && m_table->m_entries[m_idx] == 0 ) m_idx++; @@ -535,7 +546,7 @@ template void asCSymbolTableIterator::Previous() { // overflow on stepping over first element - unsigned int sz = (unsigned int)(m_table->m_entries.GetLength()); + asUINT sz = m_table->m_entries.GetLength(); m_idx--; while( m_idx < sz && m_table->m_entries[m_idx] == 0 ) m_idx--; diff --git a/lib/angelscript/source/as_texts.h b/lib/angelscript/source/as_texts.h index 7a1923a53..808e1644c 100644 --- a/lib/angelscript/source/as_texts.h +++ b/lib/angelscript/source/as_texts.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2014 Andreas Jonsson + Copyright (c) 2003-2015 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -41,59 +41,70 @@ // Compiler messages -#define TXT_s_ALREADY_DECLARED "'%s' is already declared" -#define TXT_ARG_NOT_LVALUE "Argument cannot be assigned. Output will be discarded." +#define TXT_s_ALREADY_DECLARED "'%s' is already declared" +#define TXT_ABSTRACT_CLASS_s_CANNOT_BE_INSTANTIATED "Abstract class '%s' cannot be instantiated" +#define TXT_ACCESSING_PRIVATE_PROP_s "Accessing private property '%s' of parent class" +#define TXT_ARG_NOT_LVALUE "Output argument expression is not assignable" +#define TXT_ATTR_s_INFORMED_MULTIPLE_TIMES "Attribute '%s' informed multiple times" +#define TXT_AUTO_NOT_ALLOWED "Auto is not allowed here" #define TXT_BOTH_MUST_BE_SAME "Both expressions must have the same type" #define TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR "Both conditions must call constructor" #define TEXT_BASE_DOESNT_HAVE_DEF_CONSTR "Base class doesn't have default constructor. Make explicit call to base constructor" -#define TXT_CANDIDATES_ARE "Candidates are:" -#define TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS "Can't call a constructor in loops" -#define TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH "Can't call a constructor in switch" -#define TXT_CANNOT_CALL_CONSTRUCTOR_TWICE "Can't call a constructor multiple times" +#define TXT_CANDIDATES_ARE "Candidates are:" +#define TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS "Can't call a constructor in loops" +#define TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH "Can't call a constructor in switch" +#define TXT_CANNOT_CALL_CONSTRUCTOR_TWICE "Can't call a constructor multiple times" #define TXT_CANNOT_CREATE_DELEGATE_FOR_NOREF_TYPES "Can't create delegate for types that do not support handles" -#define TXT_CANNOT_IMPLEMENT_SELF "Can't implement itself, or another interface that implements this interface" -#define TXT_CANNOT_INHERIT_FROM_s_FINAL "Can't inherit from class '%s' marked as final" -#define TXT_CANNOT_INHERIT_FROM_MULTIPLE_CLASSES "Can't inherit from multiple classes" -#define TXT_CANNOT_INHERIT_FROM_SELF "Can't inherit from itself, or another class that inherits from this class" -#define TXT_CANNOT_INSTANCIATE_TEMPLATE_s_WITH_s "Can't instanciate template '%s' with subtype '%s'" -#define TXT_CANNOT_RETURN_REF_TO_LOCAL "Can't return reference to local value." -#define TXT_CANT_IMPLICITLY_CONVERT_s_TO_s "Can't implicitly convert from '%s' to '%s'." -#define TXT_CANT_RETURN_VALUE "Can't return value when return type is 'void'" -#define TXT_CHANGE_SIGN "Implicit conversion changed sign of value" -#define TXT_COMPILING_s "Compiling %s" -#define TXT_COMPOUND_ASGN_WITH_PROP "Compound assignments with property accessors are not allowed" -#define TXT_CONSTRUCTOR_NAME_ERROR "The name of constructors and destructors must be the same as the class" +#define TXT_CANNOT_IMPLEMENT_SELF "Can't implement itself, or another interface that implements this interface" +#define TXT_CANNOT_INHERIT_FROM_s_FINAL "Can't inherit from class '%s' marked as final" +#define TXT_CANNOT_INHERIT_FROM_MULTIPLE_CLASSES "Can't inherit from multiple classes" +#define TXT_CANNOT_INHERIT_FROM_SELF "Can't inherit from itself, or another class that inherits from this class" +#define TXT_CANNOT_PASS_CLASS_METHOD_AS_ARG "Can't pass class method as arg directly. Use a delegate object instead" +#define TXT_CANNOT_RESOLVE_AUTO "Unable to resolve auto type" +#define TXT_CANNOT_RETURN_REF_TO_LOCAL "Can't return reference to local value." +#define TXT_CANT_CONSTRUCT_s_USE_REF_CAST "Can't construct handle '%s'. Use ref cast instead" +#define TXT_CANT_IMPLICITLY_CONVERT_s_TO_s "Can't implicitly convert from '%s' to '%s'." +#define TXT_CANT_RETURN_VALUE "Can't return value when return type is 'void'" +#define TXT_CHANGE_SIGN "Implicit conversion changed sign of value" +#define TXT_CLASS_CANT_BE_FINAL_AND_ABSTRACT "A class cannot be both abstract and final" +#define TXT_COMPILING_s "Compiling %s" +#define TXT_COMPOUND_ASGN_ON_VALUE_TYPE "Compound assignments with property accessors on value types are not supported" +#define TXT_COMPOUND_ASGN_WITH_IDX_PROP "Compound assignments with indexed property accessors are not supported" +#define TXT_COMPOUND_ASGN_REQUIRE_GET_SET "Compound assignments with property accessors require both get and set accessors" -#define TXT_DATA_TYPE_CANT_BE_s "Data type can't be '%s'" -#define TXT_DECL_IN_SWITCH "Variables cannot be declared in switch cases, except inside statement blocks" -#define TXT_DEFAULT_MUST_BE_LAST "The default case must be the last one" -#define TXT_DEF_ARG_MISSING_IN_FUNC_s "All subsequent parameters after the first default value must have default values in function '%s'" -#define TXT_DEF_ARG_TYPE_DOESNT_MATCH "The type of the default argument expression doesn't match the function parameter type" +#define TXT_DATA_TYPE_CANT_BE_s "Data type can't be '%s'" +#define TXT_DECL_IN_SWITCH "Variables cannot be declared in switch cases, except inside statement blocks" +#define TXT_DEFAULT_MUST_BE_LAST "The default case must be the last one" +#define TXT_DEF_ARG_MISSING_IN_FUNC_s "All subsequent parameters after the first default value must have default values in function '%s'" +#define TXT_DEF_ARG_TYPE_DOESNT_MATCH "The type of the default argument expression doesn't match the function parameter type" +#define TXT_DUPLICATE_NAMED_ARG "Duplicate named argument" #define TXT_DERIVED_METHOD_MUST_HAVE_SAME_RETTYPE_s "The method in the derived class must have the same return type as in the base class: '%s'" -#define TXT_DESTRUCTOR_MAY_NOT_HAVE_PARM "The destructor must not have any parameters" -#define TXT_DISALLOW_ASSIGN_ON_REF_TYPE "Value assignment on reference types is not allowed. Did you mean to do a handle assignment?" -#define TXT_DISALLOW_COMPOUND_ASSIGN_ON_REF_TYPE "Compound assignment on reference types is not allowed" -#define TXT_DUPLICATE_SWITCH_CASE "Duplicate switch case" +#define TXT_DESTRUCTOR_MAY_NOT_HAVE_PARM "The destructor must not have any parameters" +#define TXT_DESTRUCTOR_s_s_NAME_ERROR "The name of the destructor '%s::~%s' must be the same as the class" +#define TXT_DISALLOW_ASSIGN_ON_REF_TYPE "Value assignment on reference types is not allowed. Did you mean to do a handle assignment?" +#define TXT_DISALLOW_COMPOUND_ASSIGN_ON_REF_TYPE "Compound assignment on reference types is not allowed" +#define TXT_DUPLICATE_SWITCH_CASE "Duplicate switch case" -#define TXT_ELSE_WITH_EMPTY_STATEMENT "Else with empty statement" -#define TXT_EMPTY_SWITCH "Empty switch statement" -#define TXT_EXPECTED_s "Expected '%s'" -#define TXT_EXPECTED_CONSTANT "Expected constant" -#define TXT_EXPECTED_DATA_TYPE "Expected data type" -#define TXT_EXPECTED_EXPRESSION_VALUE "Expected expression value" -#define TXT_EXPECTED_IDENTIFIER "Expected identifier" -#define TXT_EXPECTED_LIST "Expected a list enclosed by { } to match pattern" -#define TXT_EXPECTED_METHOD_OR_PROPERTY "Expected method or property" -#define TXT_EXPECTED_ONE_OF "Expected one of: " -#define TXT_EXPECTED_OPERATOR "Expected operator" -#define TXT_EXPECTED_s_OR_s "Expected '%s' or '%s'" -#define TXT_EXPECTED_POST_OPERATOR "Expected post operator" -#define TXT_EXPECTED_PRE_OPERATOR "Expected pre operator" -#define TXT_EXPECTED_STRING "Expected string" -#define TXT_EXPR_DOESNT_EVAL_TO_FUNC "Expression doesn't evaluate to a function" -#define TXT_EXPR_MUST_BE_BOOL "Expression must be of boolean type" +#define TXT_ELSE_WITH_EMPTY_STATEMENT "Else with empty statement" +#define TXT_EMPTY_LIST_ELEMENT_IS_NOT_ALLOWED "Empty list element is not allowed" +#define TXT_EMPTY_SWITCH "Empty switch statement" +#define TXT_EXPECTED_s "Expected '%s'" +#define TXT_EXPECTED_CONSTANT "Expected constant" +#define TXT_EXPECTED_DATA_TYPE "Expected data type" +#define TXT_EXPECTED_EXPRESSION_VALUE "Expected expression value" +#define TXT_EXPECTED_IDENTIFIER "Expected identifier" +#define TXT_EXPECTED_LIST "Expected a list enclosed by { } to match pattern" +#define TXT_EXPECTED_METHOD_OR_PROPERTY "Expected method or property" +#define TXT_EXPECTED_ONE_OF "Expected one of: " +#define TXT_EXPECTED_OPERATOR "Expected operator" +#define TXT_EXPECTED_s_OR_s "Expected '%s' or '%s'" +#define TXT_EXPECTED_POST_OPERATOR "Expected post operator" +#define TXT_EXPECTED_PRE_OPERATOR "Expected pre operator" +#define TXT_EXPECTED_STRING "Expected string" +#define TXT_EXPR_DOESNT_EVAL_TO_FUNC "Expression doesn't evaluate to a function" +#define TXT_EXPR_MUST_BE_BOOL "Expression must be of boolean type" #define TXT_FAILED_TO_COMPILE_DEF_ARG_d_IN_FUNC_s "Failed while compiling default arg for parameter %d in function '%s'" #define TXT_FAILED_TO_CREATE_TEMP_OBJ "Previous error occurred while attempting to create a temporary copy of object" @@ -107,32 +118,42 @@ #define TXT_HANDLE_ASSIGN_ON_NON_HANDLE_PROP "It is not allowed to perform a handle assignment on a non-handle property" #define TXT_HANDLE_COMPARISON "The operand is implicitly converted to handle in order to compare them" +#define TXT_HANDLE_OF_HANDLE_IS_NOT_ALLOWED "Handle to handle is not allowed" +#define TXT_s_HIDES_VAR_IN_OUTER_SCOPE "Variable '%s' hides another variable of same name in outer scope" -#define TXT_IDENTIFIER_s_NOT_DATA_TYPE "Identifier '%s' is not a data type" -#define TXT_IF_WITH_EMPTY_STATEMENT "If with empty statement" -#define TXT_ILLEGAL_MEMBER_TYPE "Illegal member type" +#define TXT_IDENTIFIER_s_NOT_DATA_TYPE "Identifier '%s' is not a data type" +#define TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_GLOBAL_NS "Identifier '%s' is not a data type in global namespace" +#define TXT_IDENTIFIER_s_NOT_DATA_TYPE_IN_NS_s "Identifier '%s' is not a data type in namespace '%s' or parent" +#define TXT_IF_WITH_EMPTY_STATEMENT "If with empty statement" +#define TXT_ILLEGAL_MEMBER_TYPE "Illegal member type" // TODO: Should be TXT_ILLEGAL_OPERATION_ON_s -#define TXT_ILLEGAL_OPERATION "Illegal operation on this datatype" -#define TXT_ILLEGAL_OPERATION_ON_s "Illegal operation on '%s'" -#define TXT_ILLEGAL_TARGET_TYPE_FOR_REF_CAST "Illegal target type for reference cast" -#define TXT_ILLEGAL_VARIABLE_NAME_s "Illegal variable name '%s'." -#define TXT_INIT_LIST_CANNOT_BE_USED_WITH_s "Initialization lists cannot be used with '%s'" +#define TXT_ILLEGAL_OPERATION "Illegal operation on this datatype" +#define TXT_ILLEGAL_OPERATION_ON_s "Illegal operation on '%s'" +#define TXT_ILLEGAL_TARGET_TYPE_FOR_REF_CAST "Illegal target type for reference cast" +#define TXT_ILLEGAL_VARIABLE_NAME_s "Illegal variable name '%s'." +#define TXT_INHERITED_PRIVATE_PROP_ACCESS_s "Illegal access to inherited private property '%s'" +#define TXT_INIT_LIST_CANNOT_BE_USED_WITH_s "Initialization lists cannot be used with '%s'" +#define TXT_INSTANCING_INVLD_TMPL_TYPE_s_s "Attempting to instantiate invalid template type '%s<%s>'" +#define TXT_INSTEAD_FOUND_s "Instead found '%s'" +#define TXT_INTERFACE_s_CANNOT_BE_INSTANTIATED "Interface '%s' cannot be instantiated" #define TXT_INTERFACE_CAN_ONLY_IMPLEMENT_INTERFACE "Interfaces can only implement other interfaces" -#define TXT_INVALID_BREAK "Invalid 'break'" -#define TXT_INVALID_CHAR_LITERAL "Invalid character literal" -#define TXT_INVALID_CONTINUE "Invalid 'continue'" -#define TXT_INVALID_ESCAPE_SEQUENCE "Invalid escape sequence" -#define TXT_INVALID_OP_ON_METHOD "Invalid operation on method" -#define TXT_INVALID_REF_PROP_ACCESS "Invalid reference. Property accessors cannot be used in combined read/write operations" -#define TXT_INVALID_SCOPE "Invalid scope resolution" -#define TXT_INVALID_TYPE "Invalid type" -#define TXT_INVALID_UNICODE_FORMAT_EXPECTED_d "Invalid unicode escape sequence, expected %d hex digits" -#define TXT_INVALID_UNICODE_VALUE "Invalid unicode code point" -#define TXT_INVALID_UNICODE_SEQUENCE_IN_SRC "Invalid unicode sequence in source" +#define TXT_INVALID_BREAK "Invalid 'break'" +#define TXT_INVALID_CHAR_LITERAL "Invalid character literal" +#define TXT_INVALID_CONTINUE "Invalid 'continue'" +#define TXT_INVALID_ESCAPE_SEQUENCE "Invalid escape sequence" +#define TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME "Invalid expression: ambiguous name" +#define TXT_INVALID_OP_ON_METHOD "Invalid operation on method" +#define TXT_INVALID_REF_PROP_ACCESS "Invalid reference. Property accessors cannot be used in combined read/write operations" +#define TXT_INVALID_SCOPE "Invalid scope resolution" +#define TXT_INVALID_TYPE "Invalid type" +#define TXT_INVALID_UNICODE_FORMAT_EXPECTED_d "Invalid unicode escape sequence, expected %d hex digits" +#define TXT_INVALID_UNICODE_VALUE "Invalid unicode code point" +#define TXT_INVALID_UNICODE_SEQUENCE_IN_SRC "Invalid unicode sequence in source" #define TXT_METHOD_CANNOT_OVERRIDE_s "Method '%s' declared as final and cannot be overridden" #define TXT_METHOD_CANT_HAVE_NAME_OF_CLASS "The method cannot be named with the class name" #define TXT_METHOD_s_DOES_NOT_OVERRIDE "Method '%s' marked as override but does not replace any base class or interface method" +#define TXT_METHOD_s_s_HAS_NO_RETURN_TYPE "Method '%s::%s' is missing the return type, nor is it the same name as object to be a constructor" #define TXT_MISSING_IMPLEMENTATION_OF_s "Missing implementation of '%s'" #define TXT_MIXIN_CANNOT_BE_DECLARED_AS_s "Mixin class cannot be declared as '%s'" #define TXT_MIXIN_CANNOT_HAVE_CONSTRUCTOR "Mixin classes cannot have constructors or destructors" @@ -155,14 +176,15 @@ #define TXT_NAME_CONFLICT_s_OBJ_PROPERTY "Name conflict. '%s' is an object property." #define TXT_NAME_CONFLICT_s_METHOD "Name conflict. '%s' is a class method." #define TXT_NAME_CONFLICT_s_ALREADY_USED "Name conflict. '%s' is already used." +#define TXT_NAMED_ARGS_WITH_OLD_SYNTAX "Detected named argument with old syntax" #define TXT_NO_APPROPRIATE_INDEX_OPERATOR "No appropriate indexing operator found" -#define TXT_NO_APPROPRIATE_OPASSIGN "No appropriate opAssign method found" +#define TXT_NO_APPROPRIATE_OPHNDLASSIGN_s "No appropriate opHndlAssign method found in '%s' for handle assignment" #define TXT_NO_APPROPRIATE_OPEQUALS "No appropriate opEquals method found" #define TXT_NO_CONVERSION_s_TO_s "No conversion from '%s' to '%s' available." #define TXT_NO_CONVERSION_s_TO_MATH_TYPE "No conversion from '%s' to math type available." #define TXT_NO_DEFAULT_ARRAY_TYPE "The application doesn't support the default array type." #define TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s "No default constructor for object of type '%s'." -#define TXT_NO_DEFAULT_COPY_OP_FOR_s "There is no copy operator for the type '%s' available." +#define TXT_NO_DEFAULT_COPY_OP_FOR_s "No appropriate opAssign method found in '%s' for value assignment" #define TXT_NO_COPY_CONSTRUCTOR_FOR_s "No copy constructor for object of type '%s'." #define TXT_NO_MATCHING_SIGNATURES_TO_s "No matching signatures to '%s'" #define TXT_NO_MATCHING_OP_FOUND_FOR_TYPE_s "No matching operator that takes the type '%s' found" @@ -181,18 +203,22 @@ #define TXT_NOT_VALID_LVALUE "Not a valid lvalue" #define TXT_NOTHING_WAS_BUILT "Nothing was built in the module" -#define TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP "Type '%s' doesn't support the indexing operator" -#define TXT_OBJECT_HANDLE_NOT_SUPPORTED "Object handle is not supported for this type" -#define TXT_ONLY_OBJECTS_MAY_USE_REF_INOUT "Only object types that support object handles can use &inout. Use &in or &out instead" -#define TXT_ONLY_ONE_ARGUMENT_IN_CAST "A cast operator has one argument" -#define TXT_ONLY_ONE_FUNCTION_ALLOWED "The code must contain one and only one function" -#define TXT_ONLY_ONE_VARIABLE_ALLOWED "The code must contain one and only one global variable" -#define TXT_OPERANDS_MUST_BE_HANDLES "Both operands must be handles when comparing identity" +#define TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP "Type '%s' doesn't support the indexing operator" +#define TXT_OBJECT_HANDLE_NOT_SUPPORTED "Object handle is not supported for this type" +#define TXT_ONLY_OBJECTS_MAY_USE_REF_INOUT "Only object types that support object handles can use &inout. Use &in or &out instead" +#define TXT_ONLY_ONE_ARGUMENT_IN_CAST "A cast operator has one argument" +#define TXT_ONLY_ONE_FUNCTION_ALLOWED "The code must contain one and only one function" +#define TXT_ONLY_ONE_VARIABLE_ALLOWED "The code must contain one and only one global variable" +#define TXT_OPERANDS_MUST_BE_HANDLES "Both operands must be handles when comparing identity" +#define TXT_OVERLOAD_CONFLICTS_DUE_TO_DEFAULT_ARGS "The overloaded functions are identical on initial parameters without default arguments" #define TXT_PARAMETER_ALREADY_DECLARED "Parameter already declared" -#define TXT_PARAMETER_CANT_BE_s "Parameter type can't be '%s', because the type cannot be instanciated." +#define TXT_PARAMETER_CANT_BE_s "Parameter type can't be '%s', because the type cannot be instantiated." +#define TXT_POS_ARG_AFTER_NAMED_ARG "Positional arguments cannot be passed after named arguments" #define TXT_PRIVATE_METHOD_CALL_s "Illegal call to private method '%s'" #define TXT_PRIVATE_PROP_ACCESS_s "Illegal access to private property '%s'" +#define TXT_PROTECTED_METHOD_CALL_s "Illegal call to protected method '%s'" +#define TXT_PROTECTED_PROP_ACCESS_s "Illegal access to protected property '%s'" #define TXT_PROPERTY_ACCESSOR_DISABLED "Property accessors have been disabled by the application" #define TXT_PROPERTY_ACCESSOR_MUST_BE_IMPLEMENTED "Property accessor must be implemented" #define TXT_PROPERTY_CANT_BE_CONST "Class properties cannot be declared as const" @@ -200,6 +226,7 @@ #define TXT_PROPERTY_HAS_NO_SET_ACCESSOR "The property has no set accessor" #define TXT_PROPERTY_WITHOUT_ACCESSOR "Virtual property must have at least one get or set accessor" +#define TXT_REF_CANT_BE_TO_LOCAL_VAR "Resulting reference cannot be returned. Returned references must not refer to local variables." #define TXT_REF_CANT_BE_RETURNED_DEFERRED_PARAM "Resulting reference cannot be returned. There are deferred arguments that may invalidate it." #define TXT_REF_CANT_BE_RETURNED_LOCAL_VARS "Resulting reference cannot be returned. The expression uses objects that during cleanup may invalidate it." #define TXT_REF_IS_READ_ONLY "Reference is read-only" @@ -226,13 +253,14 @@ #define TXT_TOO_MANY_VALUES_FOR_LIST "Too many values to match pattern" #define TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE "Type '%s' is not available for this module" -#define TXT_UNEXPECTED_END_OF_FILE "Unexpected end of file" -#define TXT_UNEXPECTED_TOKEN_s "Unexpected token '%s'" -#define TXT_UNINITIALIZED_GLOBAL_VAR_s "Use of uninitialized global variable '%s'." -#define TXT_UNKNOWN_SCOPE_s "Unknown scope '%s'" -#define TXT_UNREACHABLE_CODE "Unreachable code" +#define TXT_UNEXPECTED_END_OF_FILE "Unexpected end of file" +#define TXT_UNEXPECTED_TOKEN_s "Unexpected token '%s'" +#define TXT_UNEXPECTED_VAR_DECL "Unexpected variable declaration" +#define TXT_UNINITIALIZED_GLOBAL_VAR_s "Use of uninitialized global variable '%s'." +#define TXT_UNKNOWN_SCOPE_s "Unknown scope '%s'" +#define TXT_UNREACHABLE_CODE "Unreachable code" #define TXT_UNRECOGNIZED_VIRTUAL_PROPERTY_NODE "Virtual property contains unrecognized aspect" -#define TXT_UNUSED_SCRIPT_NODE "Unused script node" +#define TXT_UNUSED_SCRIPT_NODE "Unused script node" #define TXT_VALUE_TOO_LARGE_FOR_TYPE "Value is too large for data type" #define TXT_VOID_CANT_BE_OPERAND "Void cannot be an operand in expressions" @@ -247,7 +275,7 @@ // Global variable initialization -#define TXT_FAILED_TO_INITIALIZE_s "Failed to initialize global variable '%s'" +#define TXT_FAILED_TO_INITIALIZE_s "Failed to initialize global variable '%s'" #define TXT_EXCEPTION_s_IN_s "Exception '%s' in '%s'" // Engine message @@ -271,12 +299,12 @@ #define TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s "Object {%d}. GC cannot destroy an object of type '%s' as it doesn't know how many references to there are." #define TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s_REF_COUNT_d "Object {%d}. GC cannot destroy an object of type '%s' as it can't see all references. Current ref count is %d." #define TXT_OBJECT_TYPE_s_DOESNT_EXIST "Object type '%s' doesn't exist" +#define TXT_TEMPLATE_s_ALREADY_GENERATED_CANT_REGISTER "Cannot register. The template type instance '%s' has already been generated." #define TXT_TEMPLATE_TYPE_s_DOESNT_EXIST "Template type '%s' doesn't exist" #define TXT_TEMPLATE_SUBTYPE_s_DOESNT_EXIST "Template subtype '%s' doesn't exist" #define TXT_TEMPLATE_LIST_FACTORY_EXPECTS_2_REF_PARAMS "Template list factory expects two reference parameters. The last is the pointer to the initialization buffer" #define TXT_LIST_FACTORY_EXPECTS_1_REF_PARAM "List factory expects only one reference parameter. The pointer to the initialization buffer will be passed in this parameter" #define TXT_FAILED_READ_SUBTYPE_OF_TEMPLATE_s "Failed to read subtype of template type '%s'" -#define TXT_INSTANCING_INVLD_TMPL_TYPE_s_s "Attempting to instanciate invalid template type '%s<%s>'" #define TXT_FAILED_IN_FUNC_s_d "Failed in call to function '%s' (Code: %d)" #define TXT_FAILED_IN_FUNC_s_WITH_s_d "Failed in call to function '%s' with '%s' (Code: %d)" #define TXT_FAILED_IN_FUNC_s_WITH_s_AND_s_d "Failed in call to function '%s' with '%s' and '%s' (Code: %d)" @@ -287,6 +315,10 @@ #define TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d "The function in previous message is named '%s'. The func type is %d" #define TXT_RESURRECTING_SCRIPTOBJECT_s "The script object of type '%s' is being resurrected illegally during destruction" #define TXT_INVALID_BYTECODE_d "LoadByteCode failed. The bytecode is invalid. Number of bytes read from stream: %d" +#define TXT_NO_JIT_IN_FUNC_s "Function '%s' appears to have been compiled without JIT entry points" +#define TXT_ENGINE_REF_COUNT_ERROR_DURING_SHUTDOWN "Uh oh! The engine's reference count is increasing while it is being destroyed. Make sure references needed for clean-up are immediately released" +#define TXT_MODULE_IS_IN_USE "The module is still in use and cannot be rebuilt. Discard it and request another module" +#define TXT_EXTRNL_REF_TO_MODULE_s "There is an external reference to an object in module '%s', preventing it from being deleted" // Internal names diff --git a/lib/angelscript/source/as_thread.cpp b/lib/angelscript/source/as_thread.cpp index 480be8355..63ab15e1d 100644 --- a/lib/angelscript/source/as_thread.cpp +++ b/lib/angelscript/source/as_thread.cpp @@ -109,7 +109,7 @@ AS_API void asReleaseSharedLock() //====================================================================== -#if !defined(AS_NO_THREADS) && defined(_MSC_VER) && defined(AS_WINDOWS_THREADS) && !(WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) +#if !defined(AS_NO_THREADS) && defined(_MSC_VER) && defined(AS_WINDOWS_THREADS) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) __declspec(thread) asCThreadLocalData *asCThreadManager::tld = 0; #endif @@ -126,7 +126,7 @@ asCThreadManager::asCThreadManager() pthread_key_create(&pKey, 0); tlsKey = (asDWORD)pKey; #elif defined AS_WINDOWS_THREADS - #if defined(_MSC_VER) && !(WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) + #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) tld = 0; #else tlsKey = (asDWORD)TlsAlloc(); @@ -211,7 +211,7 @@ asCThreadManager::~asCThreadManager() #if defined AS_POSIX_THREADS pthread_key_delete((pthread_key_t)tlsKey); #elif defined AS_WINDOWS_THREADS - #if defined(_MSC_VER) && !(WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) + #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) tld = 0; #else TlsFree((DWORD)tlsKey); @@ -235,7 +235,7 @@ int asCThreadManager::CleanupLocalData() #if defined AS_POSIX_THREADS asCThreadLocalData *tld = (asCThreadLocalData*)pthread_getspecific((pthread_key_t)threadManager->tlsKey); #elif defined AS_WINDOWS_THREADS - #if !defined(_MSC_VER) || (WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) + #if !defined(_MSC_VER) || !(WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) asCThreadLocalData *tld = (asCThreadLocalData*)TlsGetValue((DWORD)threadManager->tlsKey); #endif #endif @@ -249,7 +249,7 @@ int asCThreadManager::CleanupLocalData() #if defined AS_POSIX_THREADS pthread_setspecific((pthread_key_t)threadManager->tlsKey, 0); #elif defined AS_WINDOWS_THREADS - #if defined(_MSC_VER) && !(WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) + #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) tld = 0; #else TlsSetValue((DWORD)threadManager->tlsKey, 0); @@ -289,7 +289,7 @@ asCThreadLocalData *asCThreadManager::GetLocalData() pthread_setspecific((pthread_key_t)threadManager->tlsKey, tld); } #elif defined AS_WINDOWS_THREADS - #if defined(_MSC_VER) && !(WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) + #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) if( tld == 0 ) tld = asNEW(asCThreadLocalData)(); #else @@ -329,10 +329,13 @@ asCThreadCriticalSection::asCThreadCriticalSection() #if defined AS_POSIX_THREADS pthread_mutex_init(&cs, 0); #elif defined AS_WINDOWS_THREADS -#ifdef _MSC_VER +#if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + // Only the Ex version is available on Windows Store InitializeCriticalSectionEx(&cs, 4000, 0); #else - InitializeCriticalSection(&cs); + // Only the non-Ex version is available on WinXP and older + // MinGW also only defines this version + InitializeCriticalSection(&cs); #endif #endif } @@ -382,7 +385,9 @@ asCThreadReadWriteLock::asCThreadReadWriteLock() asASSERT( r == 0 ); UNUSED_VAR(r); #elif defined AS_WINDOWS_THREADS -#ifdef _MSC_VER +#if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) + // Only the Ex versions are available on Windows Store + // Create a semaphore to allow up to maxReaders simultaneous readers readLocks = CreateSemaphoreExW(NULL, maxReaders, maxReaders, 0, 0, 0); // Create a critical section to synchronize writers diff --git a/lib/angelscript/source/as_thread.h b/lib/angelscript/source/as_thread.h index 1f39f47a2..f4abf88a1 100644 --- a/lib/angelscript/source/as_thread.h +++ b/lib/angelscript/source/as_thread.h @@ -70,7 +70,7 @@ protected: int refCount; #ifndef AS_NO_THREADS -#if defined(_MSC_VER) && defined(AS_WINDOWS_THREADS) && !(WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) +#if defined(_MSC_VER) && defined(AS_WINDOWS_THREADS) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP) // On Windows Store we must use MSVC specific thread variables for thread // local storage, as the TLS API isn't available. On desktop we can't use // this as it may cause problems if the library is used in a dll. diff --git a/lib/angelscript/source/as_tokendef.h b/lib/angelscript/source/as_tokendef.h index b3c8dd9d8..6e08650a3 100644 --- a/lib/angelscript/source/as_tokendef.h +++ b/lib/angelscript/source/as_tokendef.h @@ -171,8 +171,10 @@ enum eTokenType ttEnum, // enum ttCast, // cast ttPrivate, // private + ttProtected, // protected ttNamespace, // namespace - ttMixin // mixin + ttMixin, // mixin + ttAuto // auto }; struct sTokenWord @@ -239,6 +241,7 @@ sTokenWord const tokenWords[] = asTokenDef("!is" , ttNotIs), asTokenDef("@" , ttHandle), asTokenDef("and" , ttAnd), + asTokenDef("auto" , ttAuto), asTokenDef("bool" , ttBool), asTokenDef("break" , ttBreak), asTokenDef("case" , ttCase), @@ -277,6 +280,7 @@ sTokenWord const tokenWords[] = asTokenDef("or" , ttOr), asTokenDef("out" , ttOut), asTokenDef("private" , ttPrivate), + asTokenDef("protected" , ttProtected), asTokenDef("return" , ttReturn), asTokenDef("switch" , ttSwitch), asTokenDef("true" , ttTrue), @@ -306,6 +310,7 @@ const char * const FINAL_TOKEN = "final"; const char * const OVERRIDE_TOKEN = "override"; const char * const GET_TOKEN = "get"; const char * const SET_TOKEN = "set"; +const char * const ABSTRACT_TOKEN = "abstract"; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_tokenizer.cpp b/lib/angelscript/source/as_tokenizer.cpp index 2830d979f..82c1b80e0 100644 --- a/lib/angelscript/source/as_tokenizer.cpp +++ b/lib/angelscript/source/as_tokenizer.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -223,7 +223,7 @@ bool asCTokenizer::IsComment(const char *source, size_t sourceLength, size_t &to } tokenType = ttOnelineComment; - tokenLength = n+1; + tokenLength = n < sourceLength ? n+1 : n; return true; } @@ -443,7 +443,8 @@ bool asCTokenizer::IsKeyWord(const char *source, size_t sourceLength, size_t &to // and the tokens "!" and "isTrue" in the "!isTrue" expression. if( wlen < sourceLength && ((source[wlen-1] >= 'a' && source[wlen-1] <= 'z') || - (source[wlen-1] >= 'A' && source[wlen-1] <= 'Z')) && + (source[wlen-1] >= 'A' && source[wlen-1] <= 'Z') || + (source[wlen-1] >= '0' && source[wlen-1] <= '9')) && ((source[wlen] >= 'a' && source[wlen] <= 'z') || (source[wlen] >= 'A' && source[wlen] <= 'Z') || (source[wlen] >= '0' && source[wlen] <= '9') || diff --git a/lib/angelscript/source/as_typeinfo.cpp b/lib/angelscript/source/as_typeinfo.cpp index 16ac4b010..534ac0b53 100644 --- a/lib/angelscript/source/as_typeinfo.cpp +++ b/lib/angelscript/source/as_typeinfo.cpp @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -54,6 +54,7 @@ asCTypeInfo::asCTypeInfo() qwordValue = 0; isLValue = false; isVoidExpression = false; + isRefToLocal = false; } void asCTypeInfo::Set(const asCDataType &dt) @@ -68,6 +69,7 @@ void asCTypeInfo::Set(const asCDataType &dt) qwordValue = 0; isLValue = false; isVoidExpression = false; + isRefToLocal = false; } void asCTypeInfo::SetVariable(const asCDataType &dt, int stackOffset, bool isTemporary) diff --git a/lib/angelscript/source/as_typeinfo.h b/lib/angelscript/source/as_typeinfo.h index b551fddae..aa8ad528c 100644 --- a/lib/angelscript/source/as_typeinfo.h +++ b/lib/angelscript/source/as_typeinfo.h @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2013 Andreas Jonsson + Copyright (c) 2003-2014 Andreas Jonsson This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -74,7 +74,8 @@ struct asCTypeInfo bool isVariable : 1; bool isExplicitHandle : 1; bool isVoidExpression : 1; - short dummy : 10; + bool isRefToLocal : 1; // The reference may be to a local variable + short dummy : 9; short stackOffset; union { diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 138658e36..eb3a7af6b 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -59,7 +59,7 @@ ScriptEngine::ScriptEngine() m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); if (m_engine == NULL) { - Log::error("Scripting", "Failed to create script engine."); + Log::fatal("Scripting", "Failed to create script engine."); } // The script compiler will write any compiler messages to the callback. diff --git a/src/scriptengine/scriptarray.cpp b/src/scriptengine/scriptarray.cpp index edb2bcd1c..7689f2b95 100644 --- a/src/scriptengine/scriptarray.cpp +++ b/src/scriptengine/scriptarray.cpp @@ -1,19 +1,32 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2014-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/* +AngelCode Scripting Library +Copyright (c) 2003-2015 Andreas Jonsson + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +The original version of this library can be located at: +http://www.angelcode.com/angelscript/ + +Andreas Jonsson +andreas@angelcode.com +*/ #include #include @@ -27,6 +40,22 @@ using namespace std; BEGIN_AS_NAMESPACE +// This macro is used to avoid warnings about unused variables. +// Usually where the variables are only used in debug mode. +#define UNUSED_VAR(x) (void)(x) + +// Set the default memory routines +// Use the angelscript engine's memory routines by default +static asALLOCFUNC_t userAlloc = asAllocMem; +static asFREEFUNC_t userFree = asFreeMem; + +// Allows the application to set which memory routines should be used by the array object +void CScriptArray::SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc) +{ + userAlloc = allocFunc; + userFree = freeFunc; +} + static void RegisterScriptArray_Native(asIScriptEngine *engine); static void RegisterScriptArray_Generic(asIScriptEngine *engine); @@ -42,7 +71,7 @@ struct SArrayCache asIScriptFunction *cmpFunc; asIScriptFunction *eqFunc; int cmpFuncReturnCode; // To allow better error message in case of multiple matches - int eqFuncReturnCode; + int eqFuncReturnCode; }; // We just define a number here that we assume nobody else is using for @@ -54,16 +83,31 @@ static void CleanupObjectTypeArrayCache(asIObjectType *type) { SArrayCache *cache = reinterpret_cast(type->GetUserData(ARRAY_CACHE)); if( cache ) - delete cache; + { + cache->~SArrayCache(); + userFree(cache); + } } -static CScriptArray* ScriptArrayFactory2(asIObjectType *ot, asUINT length) +CScriptArray* CScriptArray::Create(asIObjectType *ot, asUINT length) { - CScriptArray *a = new CScriptArray(length, ot); + asIScriptContext *ctx = asGetActiveContext(); + + // Allocate the memory + void *mem = userAlloc(sizeof(CScriptArray)); + if( mem == 0 ) + { + if( ctx ) + ctx->SetException("Out of memory"); + + return 0; + } + + // Initialize the object + CScriptArray *a = new(mem) CScriptArray(length, ot); // It's possible the constructor raised a script exception, in which case we // need to free the memory and return null instead, else we get a memory leak. - asIScriptContext *ctx = asGetActiveContext(); if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION ) { a->Release(); @@ -73,13 +117,25 @@ static CScriptArray* ScriptArrayFactory2(asIObjectType *ot, asUINT length) return a; } -static CScriptArray* ScriptArrayListFactory(asIObjectType *ot, void *initList) +CScriptArray* CScriptArray::Create(asIObjectType *ot, void *initList) { - CScriptArray *a = new CScriptArray(ot, initList); + asIScriptContext *ctx = asGetActiveContext(); + + // Allocate the memory + void *mem = userAlloc(sizeof(CScriptArray)); + if( mem == 0 ) + { + if( ctx ) + ctx->SetException("Out of memory"); + + return 0; + } + + // Initialize the object + CScriptArray *a = new(mem) CScriptArray(ot, initList); // It's possible the constructor raised a script exception, in which case we // need to free the memory and return null instead, else we get a memory leak. - asIScriptContext *ctx = asGetActiveContext(); if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION ) { a->Release(); @@ -89,13 +145,25 @@ static CScriptArray* ScriptArrayListFactory(asIObjectType *ot, void *initList) return a; } -static CScriptArray* ScriptArrayFactoryDefVal(asIObjectType *ot, asUINT length, void *defVal) +CScriptArray* CScriptArray::Create(asIObjectType *ot, asUINT length, void *defVal) { - CScriptArray *a = new CScriptArray(length, defVal, ot); + asIScriptContext *ctx = asGetActiveContext(); + + // Allocate the memory + void *mem = userAlloc(sizeof(CScriptArray)); + if( mem == 0 ) + { + if( ctx ) + ctx->SetException("Out of memory"); + + return 0; + } + + // Initialize the object + CScriptArray *a = new(mem) CScriptArray(length, defVal, ot); // It's possible the constructor raised a script exception, in which case we // need to free the memory and return null instead, else we get a memory leak. - asIScriptContext *ctx = asGetActiveContext(); if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION ) { a->Release(); @@ -105,20 +173,20 @@ static CScriptArray* ScriptArrayFactoryDefVal(asIObjectType *ot, asUINT length, return a; } -static CScriptArray* ScriptArrayFactory(asIObjectType *ot) +CScriptArray* CScriptArray::Create(asIObjectType *ot) { - return ScriptArrayFactory2(ot, asUINT(0)); + return CScriptArray::Create(ot, asUINT(0)); } // This optional callback is called when the template type is first used by the compiler. -// It allows the application to validate if the template can be instanciated for the requested +// It allows the application to validate if the template can be instantiated for the requested // subtype at compile time, instead of at runtime. The output argument dontGarbageCollect // allow the callback to tell the engine if the template instance type shouldn't be garbage collected, // i.e. no asOBJ_GC flag. static bool ScriptArrayTemplateCallback(asIObjectType *ot, bool &dontGarbageCollect) { - // Make sure the subtype can be instanciated with a default factory/constructor, - // otherwise we won't be able to instanciate the elements. + // Make sure the subtype can be instantiated with a default factory/constructor, + // otherwise we won't be able to instantiate the elements. int typeId = ot->GetSubTypeId(); if( typeId == asTYPEID_VOID ) return false; @@ -147,6 +215,7 @@ static bool ScriptArrayTemplateCallback(asIObjectType *ot, bool &dontGarbageColl if( !found ) { // There is no default constructor + ot->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default constructor"); return false; } } @@ -174,6 +243,7 @@ static bool ScriptArrayTemplateCallback(asIObjectType *ot, bool &dontGarbageColl if( !found ) { // No default factory + ot->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default factory"); return false; } } @@ -188,6 +258,39 @@ static bool ScriptArrayTemplateCallback(asIObjectType *ot, bool &dontGarbageColl // thus there is no need to garbage collect them dontGarbageCollect = true; } + else + { + assert( typeId & asTYPEID_OBJHANDLE ); + + // It is not necessary to set the array as garbage collected for all handle types. + // If it is possible to determine that the handle cannot refer to an object type + // that can potentially form a circular reference with the array then it is not + // necessary to make the array garbage collected. + asIObjectType *subtype = ot->GetEngine()->GetObjectTypeById(typeId); + asDWORD flags = subtype->GetFlags(); + if( !(flags & asOBJ_GC) ) + { + if( (flags & asOBJ_SCRIPT_OBJECT) ) + { + // Even if a script class is by itself not garbage collected, it is possible + // that classes that derive from it may be, so it is not possible to know + // that no circular reference can occur. + if( (flags & asOBJ_NOINHERIT) ) + { + // A script class declared as final cannot be inherited from, thus + // we can be certain that the object cannot be garbage collected. + dontGarbageCollect = true; + } + } + else + { + // For application registered classes we assume the application knows + // what it is doing and don't mark the array as garbage collected unless + // the type is also garbage collected. + dontGarbageCollect = true; + } + } + } // The type is ok return true; @@ -204,12 +307,14 @@ void RegisterScriptArray(asIScriptEngine *engine, bool defaultArray) if( defaultArray ) { int r = engine->RegisterDefaultArrayType("array"); assert( r >= 0 ); + UNUSED_VAR(r); } } static void RegisterScriptArray_Native(asIScriptEngine *engine) { - int r; + int r = 0; + UNUSED_VAR(r); // Register the object type user data clean up engine->SetObjectTypeUserDataCleanupCallback(CleanupObjectTypeArrayCache, ARRAY_CACHE); @@ -221,12 +326,12 @@ static void RegisterScriptArray_Native(asIScriptEngine *engine) r = engine->RegisterObjectBehaviour("array", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptArrayTemplateCallback), asCALL_CDECL); assert( r >= 0 ); // Templates receive the object type as the first parameter. To the script writer this is hidden - r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in)", asFUNCTIONPR(ScriptArrayFactory, (asIObjectType*), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); - r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint)", asFUNCTIONPR(ScriptArrayFactory2, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); - r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint, const T &in)", asFUNCTIONPR(ScriptArrayFactoryDefVal, (asIObjectType*, asUINT, void *), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in)", asFUNCTIONPR(CScriptArray::Create, (asIObjectType*), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint)", asFUNCTIONPR(CScriptArray::Create, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint, const T &in)", asFUNCTIONPR(CScriptArray::Create, (asIObjectType*, asUINT, void *), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); // Register the factory that will be used for initialization lists - r = engine->RegisterObjectBehaviour("array", asBEHAVE_LIST_FACTORY, "array@ f(int&in type, int&in list) {repeat T}", asFUNCTIONPR(ScriptArrayListFactory, (asIObjectType*, void*), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_LIST_FACTORY, "array@ f(int&in type, int&in list) {repeat T}", asFUNCTIONPR(CScriptArray::Create, (asIObjectType*, void*), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); // The memory management methods r = engine->RegisterObjectBehaviour("array", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptArray,AddRef), asCALL_THISCALL); assert( r >= 0 ); @@ -337,14 +442,16 @@ CScriptArray::CScriptArray(asIObjectType *ot, void *buf) CreateBuffer(&buffer, length); // Copy the values of the primitive type into the internal buffer - memcpy(At(0), (((asUINT*)buf)+1), length * elementSize); + if( length > 0 ) + memcpy(At(0), (((asUINT*)buf)+1), length * elementSize); } else if( ot->GetSubTypeId() & asTYPEID_OBJHANDLE ) { CreateBuffer(&buffer, length); // Copy the handles into the internal buffer - memcpy(At(0), (((asUINT*)buf)+1), length * elementSize); + if( length > 0 ) + memcpy(At(0), (((asUINT*)buf)+1), length * elementSize); // With object handles it is safe to clear the memory in the received buffer // instead of increasing the ref count. It will save time both by avoiding the @@ -360,7 +467,8 @@ CScriptArray::CScriptArray(asIObjectType *ot, void *buf) subTypeId &= ~asTYPEID_OBJHANDLE; // Copy the handles into the internal buffer - memcpy(buffer->data, (((asUINT*)buf)+1), length * elementSize); + if( length > 0 ) + memcpy(buffer->data, (((asUINT*)buf)+1), length * elementSize); // For ref types we can do the same as for handles, as they are // implicitly stored as handles. @@ -539,12 +647,7 @@ void CScriptArray::Reserve(asUINT maxElements) return; // Allocate memory for the buffer - SArrayBuffer *newBuffer; - #if defined(__S3E__) // Marmalade doesn't understand (nothrow) - newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1 + elementSize*maxElements]; - #else - newBuffer = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1 + elementSize*maxElements]; - #endif + SArrayBuffer *newBuffer = reinterpret_cast(userAlloc(sizeof(SArrayBuffer)-1 + elementSize*maxElements)); if( newBuffer ) { newBuffer->numElements = buffer->numElements; @@ -559,10 +662,13 @@ void CScriptArray::Reserve(asUINT maxElements) return; } + // TODO: memcpy assumes the objects in the array doesn't hold pointers to themselves + // This should really be using the objects copy/move constructor to copy each object + // to the new location. It would most likely be a hit on the performance though. memcpy(newBuffer->data, buffer->data, buffer->numElements*elementSize); // Release the old buffer - delete[] (asBYTE*)buffer; + userFree(buffer); buffer = newBuffer; } @@ -600,12 +706,7 @@ void CScriptArray::Resize(int delta, asUINT at) if( buffer->maxElements < buffer->numElements + delta ) { // Allocate memory for the buffer - SArrayBuffer *newBuffer; - #if defined(__S3E__) // Marmalade doesn't understand (nothrow) - newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta)]; - #else - newBuffer = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta)]; - #endif + SArrayBuffer *newBuffer = reinterpret_cast(userAlloc(sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta))); if( newBuffer ) { newBuffer->numElements = buffer->numElements + delta; @@ -621,17 +722,17 @@ void CScriptArray::Resize(int delta, asUINT at) } // TODO: memcpy assumes the objects in the array doesn't hold pointers to themselves - // This should really be using the objects copy constructor to copy each object + // This should really be using the objects copy/move constructor to copy each object // to the new location. It would most likely be a hit on the performance though. memcpy(newBuffer->data, buffer->data, at*elementSize); if( at < buffer->numElements ) memcpy(newBuffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements-at)*elementSize); - if( subTypeId & asTYPEID_MASK_OBJECT ) - Construct(newBuffer, at, at+delta); + // Initialize the new elements with default values + Construct(newBuffer, at, at+delta); // Release the old buffer - delete[] (asBYTE*)buffer; + userFree(buffer); buffer = newBuffer; } @@ -639,7 +740,7 @@ void CScriptArray::Resize(int delta, asUINT at) { Destruct(buffer, at, at-delta); // TODO: memmove assumes the objects in the array doesn't hold pointers to themselves - // This should really be using the objects copy constructor to copy each object + // This should really be using the objects copy/move constructor to copy each object // to the new location. It would most likely be a hit on the performance though. memmove(buffer->data + at*elementSize, buffer->data + (at-delta)*elementSize, (buffer->numElements - (at-delta))*elementSize); buffer->numElements += delta; @@ -647,7 +748,7 @@ void CScriptArray::Resize(int delta, asUINT at) else { // TODO: memmove assumes the objects in the array doesn't hold pointers to themselves - // This should really be using the objects copy constructor to copy each object + // This should really be using the objects copy/move constructor to copy each object // to the new location. It would most likely be a hit on the performance though. memmove(buffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements - at)*elementSize); Construct(buffer, at, at+delta); @@ -662,19 +763,14 @@ bool CScriptArray::CheckMaxSize(asUINT numElements) // for the array doesn't overflow and becomes smaller than requested asUINT maxSize = 0xFFFFFFFFul - sizeof(SArrayBuffer) + 1; - if( subTypeId & asTYPEID_MASK_OBJECT ) - maxSize /= sizeof(void*); - else if( elementSize > 0 ) + if( elementSize > 0 ) maxSize /= elementSize; if( numElements > maxSize ) { asIScriptContext *ctx = asGetActiveContext(); if( ctx ) - { - // Set a script exception ctx->SetException("Too large array size"); - } return false; } @@ -754,7 +850,7 @@ const void *CScriptArray::At(asUINT index) const } if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) ) - return (void*)((size_t*)buffer->data)[index]; + return *(void**)(buffer->data + elementSize*index); else return buffer->data + elementSize*index; } @@ -767,22 +863,7 @@ void *CScriptArray::At(asUINT index) // internal void CScriptArray::CreateBuffer(SArrayBuffer **buf, asUINT numElements) { - if( subTypeId & asTYPEID_MASK_OBJECT ) - { - #if defined(__S3E__) // Marmalade doesn't understand (nothrow) - *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements]; - #else - *buf = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements]; - #endif - } - else - { - #if defined(__S3E__) - *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements]; - #else - *buf = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements]; - #endif - } + *buf = reinterpret_cast(userAlloc(sizeof(SArrayBuffer)-1+elementSize*numElements)); if( *buf ) { @@ -805,20 +886,15 @@ void CScriptArray::DeleteBuffer(SArrayBuffer *buf) Destruct(buf, 0, buf->numElements); // Free the buffer - delete[] (asBYTE*)buf; + userFree(buf); } // internal void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end) { - if( subTypeId & asTYPEID_OBJHANDLE ) - { - // Set all object handles to null - void *d = (void*)(buf->data + start * sizeof(void*)); - memset(d, 0, (end-start)*sizeof(void*)); - } - else if( subTypeId & asTYPEID_MASK_OBJECT ) + if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) ) { + // Create an object using the default constructor/factory for each element void **max = (void**)(buf->data + end * sizeof(void*)); void **d = (void**)(buf->data + start * sizeof(void*)); @@ -826,7 +902,25 @@ void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end) asIObjectType *subType = objType->GetSubType(); for( ; d < max; d++ ) + { *d = (void*)engine->CreateScriptObject(subType); + if( *d == 0 ) + { + // Set the remaining entries to null so the destructor + // won't attempt to destroy invalid objects later + memset(d, 0, sizeof(void*)*(max-d)); + + // There is no need to set an exception on the context, + // as CreateScriptObject has already done that + return; + } + } + } + else + { + // Set all elements to zero whether they are handles or primitives + void *d = (void*)(buf->data + start * elementSize); + memset(d, 0, (end-start)*elementSize); } } @@ -978,7 +1072,7 @@ bool CScriptArray::operator==(const CScriptArray &other) const } if( cmpContext ) - { + { if( isNested ) { asEContextState state = cmpContext->GetState(); @@ -988,7 +1082,7 @@ bool CScriptArray::operator==(const CScriptArray &other) const } else cmpContext->Release(); - } + } return isEqual; } @@ -1366,7 +1460,7 @@ void CScriptArray::Sort(asUINT startAt, asUINT count, bool asc) } if( cmpContext ) - { + { if( isNested ) { asEContextState state = cmpContext->GetState(); @@ -1376,7 +1470,7 @@ void CScriptArray::Sort(asUINT startAt, asUINT count, bool asc) } else cmpContext->Release(); - } + } } // internal @@ -1447,7 +1541,7 @@ void CScriptArray::Precache() // First check if a cache already exists for this array type SArrayCache *cache = reinterpret_cast(objType->GetUserData(ARRAY_CACHE)); - if( cache ) return; + if( cache ) return; // We need to make sure the cache is created only once, even // if multiple threads reach the same point at the same time @@ -1463,7 +1557,7 @@ void CScriptArray::Precache() } // Create the cache - cache = new SArrayCache(); + cache = reinterpret_cast(userAlloc(sizeof(SArrayCache))); memset(cache, 0, sizeof(SArrayCache)); // If the sub type is a handle to const, then the methods must be const too @@ -1496,7 +1590,8 @@ void CScriptArray::Precache() continue; // The parameter must either be a reference to the subtype or a handle to the subtype - int paramTypeId = func->GetParamTypeId(0, &flags); + int paramTypeId; + func->GetParam(0, ¶mTypeId, &flags); if( (paramTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) != (subTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) ) continue; @@ -1552,6 +1647,9 @@ void CScriptArray::Precache() // GC behaviour void CScriptArray::EnumReferences(asIScriptEngine *engine) { + // TODO: If garbage collection can be done from a separate thread, then this method must be + // protected so that it doesn't get lost during the iteration if the array is modified + // If the array is holding handles, then we need to notify the GC of them if( subTypeId & asTYPEID_MASK_OBJECT ) { @@ -1565,7 +1663,7 @@ void CScriptArray::EnumReferences(asIScriptEngine *engine) } // GC behaviour -void CScriptArray::ReleaseAllHandles(asIScriptEngine *engine) +void CScriptArray::ReleaseAllHandles(asIScriptEngine *) { // Resizing to zero will release everything Resize(0); @@ -1586,7 +1684,8 @@ void CScriptArray::Release() const { // When reaching 0 no more references to this instance // exists and the object should be destroyed - delete this; + this->~CScriptArray(); + userFree(const_cast(this)); } } @@ -1615,7 +1714,7 @@ static void ScriptArrayFactory_Generic(asIScriptGeneric *gen) { asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); - *(CScriptArray**)gen->GetAddressOfReturnLocation() = ScriptArrayFactory(ot); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray::Create(ot); } static void ScriptArrayFactory2_Generic(asIScriptGeneric *gen) @@ -1623,7 +1722,7 @@ static void ScriptArrayFactory2_Generic(asIScriptGeneric *gen) asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); asUINT length = gen->GetArgDWord(1); - *(CScriptArray**)gen->GetAddressOfReturnLocation() = ScriptArrayFactory2(ot, length); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray::Create(ot, length); } static void ScriptArrayListFactory_Generic(asIScriptGeneric *gen) @@ -1631,7 +1730,7 @@ static void ScriptArrayListFactory_Generic(asIScriptGeneric *gen) asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); void *buf = gen->GetArgAddress(1); - *(CScriptArray**)gen->GetAddressOfReturnLocation() = ScriptArrayListFactory(ot, buf); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray::Create(ot, buf); } static void ScriptArrayFactoryDefVal_Generic(asIScriptGeneric *gen) @@ -1640,14 +1739,14 @@ static void ScriptArrayFactoryDefVal_Generic(asIScriptGeneric *gen) asUINT length = gen->GetArgDWord(1); void *defVal = gen->GetArgAddress(2); - *(CScriptArray**)gen->GetAddressOfReturnLocation() = ScriptArrayFactoryDefVal(ot, length, defVal); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray::Create(ot, length, defVal); } static void ScriptArrayTemplateCallback_Generic(asIScriptGeneric *gen) { asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); bool *dontGarbageCollect = *(bool**)gen->GetAddressOfArg(1); - *(bool*)gen->GetAddressOfReturnLocation() = ScriptArrayTemplateCallback(ot, *dontGarbageCollect); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = ScriptArrayTemplateCallback(ot, *dontGarbageCollect); } static void ScriptArrayAssignment_Generic(asIScriptGeneric *gen) @@ -1768,7 +1867,7 @@ static void ScriptArrayReverse_Generic(asIScriptGeneric *gen) static void ScriptArrayIsEmpty_Generic(asIScriptGeneric *gen) { CScriptArray *self = (CScriptArray*)gen->GetObject(); - self->IsEmpty(); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = self->IsEmpty(); } static void ScriptArraySortAsc2_Generic(asIScriptGeneric *gen) @@ -1808,7 +1907,7 @@ static void ScriptArrayRelease_Generic(asIScriptGeneric *gen) static void ScriptArrayGetRefCount_Generic(asIScriptGeneric *gen) { CScriptArray *self = (CScriptArray*)gen->GetObject(); - *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = self->GetRefCount(); } static void ScriptArraySetFlag_Generic(asIScriptGeneric *gen) @@ -1820,7 +1919,7 @@ static void ScriptArraySetFlag_Generic(asIScriptGeneric *gen) static void ScriptArrayGetFlag_Generic(asIScriptGeneric *gen) { CScriptArray *self = (CScriptArray*)gen->GetObject(); - *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag(); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = self->GetFlag(); } static void ScriptArrayEnumReferences_Generic(asIScriptGeneric *gen) @@ -1839,7 +1938,8 @@ static void ScriptArrayReleaseAllHandles_Generic(asIScriptGeneric *gen) static void RegisterScriptArray_Generic(asIScriptEngine *engine) { - int r; + int r = 0; + UNUSED_VAR(r); engine->SetObjectTypeUserDataCleanupCallback(CleanupObjectTypeArrayCache, ARRAY_CACHE); diff --git a/src/scriptengine/scriptarray.hpp b/src/scriptengine/scriptarray.hpp index 42a13510f..a1c0a0a1d 100644 --- a/src/scriptengine/scriptarray.hpp +++ b/src/scriptengine/scriptarray.hpp @@ -1,26 +1,9 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2014-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - #ifndef SCRIPTARRAY_H #define SCRIPTARRAY_H #ifndef ANGELSCRIPT_H // Avoid having to inform include path if header is already include before -#include "angelscript.h" +#include #endif // Sometimes it may be desired to use the same method names as used by C++ STL. @@ -42,12 +25,16 @@ struct SArrayCache; class CScriptArray { public: - CScriptArray(asIObjectType *ot, void *initBuf); // Called from script when initialized with list - CScriptArray(asUINT length, asIObjectType *ot); - CScriptArray(asUINT length, void *defVal, asIObjectType *ot); - CScriptArray(const CScriptArray &other); - virtual ~CScriptArray(); + // Set the memory functions that should be used by all CScriptArrays + static void SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc); + // Factory functions + static CScriptArray *Create(asIObjectType *ot); + static CScriptArray *Create(asIObjectType *ot, asUINT length); + static CScriptArray *Create(asIObjectType *ot, asUINT length, void *defaultValue); + static CScriptArray *Create(asIObjectType *ot, void *listBuffer); + + // Memory management void AddRef() const; void Release() const; @@ -56,21 +43,35 @@ public: int GetArrayTypeId() const; int GetElementTypeId() const; - void Reserve(asUINT maxElements); - void Resize(asUINT numElements); + // Get the current size asUINT GetSize() const; + + // Returns true if the array is empty bool IsEmpty() const; + // Pre-allocates memory for elements + void Reserve(asUINT maxElements); + + // Resize the array + void Resize(asUINT numElements); + // Get a pointer to an element. Returns 0 if out of bounds void *At(asUINT index); const void *At(asUINT index) const; - // Set value of an element + // Set value of an element. + // The value arg should be a pointer to the value that will be copied to the element. + // Remember, if the array holds handles the value parameter should be the + // address of the handle. The refCount of the object will also be incremented void SetValue(asUINT index, void *value); + // Copy the contents of one array to another (only if the types are the same) CScriptArray &operator=(const CScriptArray&); + + // Compare two arrays bool operator==(const CScriptArray &) const; + // Array manipulation void InsertAt(asUINT index, void *value); void RemoveAt(asUINT index); void InsertLast(void *value); @@ -101,6 +102,13 @@ protected: int elementSize; int subTypeId; + // Constructors + CScriptArray(asIObjectType *ot, void *initBuf); // Called from script when initialized with list + CScriptArray(asUINT length, asIObjectType *ot); + CScriptArray(asUINT length, void *defVal, asIObjectType *ot); + CScriptArray(const CScriptArray &other); + virtual ~CScriptArray(); + bool Less(const void *a, const void *b, bool asc, asIScriptContext *ctx, SArrayCache *cache); void *GetArrayItemPointer(int index); void *GetDataPointer(void *buffer); diff --git a/src/scriptengine/scriptstdstring.cpp b/src/scriptengine/scriptstdstring.cpp index 1cd595867..381a78cc1 100644 --- a/src/scriptengine/scriptstdstring.cpp +++ b/src/scriptengine/scriptstdstring.cpp @@ -1,19 +1,32 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2014-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/* +AngelCode Scripting Library +Copyright (c) 2003-2015 Andreas Jonsson + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +The original version of this library can be located at: +http://www.angelcode.com/angelscript/ + +Andreas Jonsson +andreas@angelcode.com +*/ #include "scriptstdstring.hpp" #include // assert() @@ -21,13 +34,19 @@ #include // strstr() #include // sprintf() #include // strtod() -#include // setlocale() +#ifndef __psp2__ + #include // setlocale() +#endif #include // std::map using namespace std; BEGIN_AS_NAMESPACE +// This macro is used to avoid warnings about unused variables. +// Usually where the variables are only used in debug mode. +#define UNUSED_VAR(x) (void)(x) + #if AS_USE_STRINGPOOL == 1 // By keeping the literal strings in a pool the application @@ -168,7 +187,7 @@ static bool StringIsEmpty(const string &str) return str.empty(); } -static string &AssignUIntToString(unsigned int i, string &dest) +static string &AssignUInt64ToString(asQWORD i, string &dest) { ostringstream stream; stream << i; @@ -176,7 +195,7 @@ static string &AssignUIntToString(unsigned int i, string &dest) return dest; } -static string &AddAssignUIntToString(unsigned int i, string &dest) +static string &AddAssignUInt64ToString(asQWORD i, string &dest) { ostringstream stream; stream << i; @@ -184,21 +203,21 @@ static string &AddAssignUIntToString(unsigned int i, string &dest) return dest; } -static string AddStringUInt(const string &str, unsigned int i) +static string AddStringUInt64(const string &str, asQWORD i) { ostringstream stream; stream << i; return str + stream.str(); } -static string AddIntString(int i, const string &str) +static string AddInt64String(asINT64 i, const string &str) { ostringstream stream; stream << i; return stream.str() + str; } -static string &AssignIntToString(int i, string &dest) +static string &AssignInt64ToString(asINT64 i, string &dest) { ostringstream stream; stream << i; @@ -206,7 +225,7 @@ static string &AssignIntToString(int i, string &dest) return dest; } -static string &AddAssignIntToString(int i, string &dest) +static string &AddAssignInt64ToString(asINT64 i, string &dest) { ostringstream stream; stream << i; @@ -214,14 +233,14 @@ static string &AddAssignIntToString(int i, string &dest) return dest; } -static string AddStringInt(const string &str, int i) +static string AddStringInt64(const string &str, asINT64 i) { ostringstream stream; stream << i; return str + stream.str(); } -static string AddUIntString(unsigned int i, const string &str) +static string AddUInt64String(asQWORD i, const string &str) { ostringstream stream; stream << i; @@ -244,6 +263,22 @@ static string &AddAssignDoubleToString(double f, string &dest) return dest; } +static string &AssignFloatToString(float f, string &dest) +{ + ostringstream stream; + stream << f; + dest = stream.str(); + return dest; +} + +static string &AddAssignFloatToString(float f, string &dest) +{ + ostringstream stream; + stream << f; + dest += stream.str(); + return dest; +} + static string &AssignBoolToString(bool b, string &dest) { ostringstream stream; @@ -274,6 +309,20 @@ static string AddDoubleString(double f, const string &str) return stream.str() + str; } +static string AddStringFloat(const string &str, float f) +{ + ostringstream stream; + stream << f; + return str + stream.str(); +} + +static string AddFloatString(float f, const string &str) +{ + ostringstream stream; + stream << f; + return stream.str() + str; +} + static string AddStringBool(const string &str, bool b) { ostringstream stream; @@ -371,14 +420,14 @@ static string formatInt(asINT64 value, const string &options, asUINT width) if( spaceOnSign ) fmt += " "; if( padWithZero ) fmt += "0"; -#ifdef __GNUC__ +#ifdef _WIN32 + fmt += "*I64"; +#else #ifdef _LP64 fmt += "*l"; #else fmt += "*ll"; #endif -#else - fmt += "*I64"; #endif if( hexSmall ) fmt += "x"; @@ -386,7 +435,7 @@ static string formatInt(asINT64 value, const string &options, asUINT width) else fmt += "d"; string buf; - buf.resize(width+20); + buf.resize(width+30); #if _MSC_VER >= 1400 && !defined(__S3E__) // MSVC 8.0 / 2005 or newer sprintf_s(&buf[0], buf.size(), fmt.c_str(), width, value); @@ -500,7 +549,7 @@ double parseFloat(const string &val, asUINT *byteCount) // WinCE doesn't have setlocale. Some quick testing on my current platform // still manages to parse the numbers such as "3.14" even if the decimal for the // locale is ",". -#if !defined(_WIN32_WCE) && !defined(ANDROID) +#if !defined(_WIN32_WCE) && !defined(ANDROID) && !defined(__psp2__) // Set the locale to C so that we are guaranteed to parse the float value correctly char *orig = setlocale(LC_NUMERIC, 0); setlocale(LC_NUMERIC, "C"); @@ -508,7 +557,7 @@ double parseFloat(const string &val, asUINT *byteCount) double res = strtod(val.c_str(), &end); -#if !defined(_WIN32_WCE) && !defined(ANDROID) +#if !defined(_WIN32_WCE) && !defined(ANDROID) && !defined(__psp2__) // Restore the locale setlocale(LC_NUMERIC, orig); #endif @@ -547,11 +596,16 @@ static bool StringEquals(const std::string& lhs, const std::string& rhs) void RegisterStdString_Native(asIScriptEngine *engine) { - int r; - + int r = 0; + UNUSED_VAR(r); // Register the string type +#if AS_CAN_USE_CPP11 + // With C++11 it is possible to use asGetTypeTraits to automatically determine the correct flags to use + r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asGetTypeTraits()); assert( r >= 0 ); +#else r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 ); +#endif #if AS_USE_STRINGPOOL == 1 // Register the string factory @@ -598,15 +652,20 @@ void RegisterStdString_Native(asIScriptEngine *engine) r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddStringDouble), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDoubleString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string &opAssign(int)", asFUNCTION(AssignIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string &opAddAssign(int)", asFUNCTION(AddAssignIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string opAdd(int) const", asFUNCTION(AddStringInt), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string opAdd_r(int) const", asFUNCTION(AddIntString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(float) const", asFUNCTION(AddStringFloat), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(float) const", asFUNCTION(AddFloatString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string &opAssign(uint)", asFUNCTION(AssignUIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint)", asFUNCTION(AddAssignUIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string opAdd(uint) const", asFUNCTION(AddStringUInt), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string opAdd_r(uint) const", asFUNCTION(AddUIntString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAssign(int64)", asFUNCTION(AssignInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(int64)", asFUNCTION(AddAssignInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(int64) const", asFUNCTION(AddStringInt64), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(int64) const", asFUNCTION(AddInt64String), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(uint64)", asFUNCTION(AssignUInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint64)", asFUNCTION(AddAssignUInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(uint64) const", asFUNCTION(AddStringUInt64), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(uint64) const", asFUNCTION(AddUInt64String), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBoolToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBoolToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); @@ -812,7 +871,7 @@ static void StringCharAtGeneric(asIScriptGeneric * gen) static void AssignInt2StringGeneric(asIScriptGeneric *gen) { - int *a = static_cast(gen->GetAddressOfArg(0)); + asINT64 *a = static_cast(gen->GetAddressOfArg(0)); string *self = static_cast(gen->GetObject()); std::stringstream sstr; sstr << *a; @@ -822,7 +881,7 @@ static void AssignInt2StringGeneric(asIScriptGeneric *gen) static void AssignUInt2StringGeneric(asIScriptGeneric *gen) { - unsigned int *a = static_cast(gen->GetAddressOfArg(0)); + asQWORD *a = static_cast(gen->GetAddressOfArg(0)); string *self = static_cast(gen->GetObject()); std::stringstream sstr; sstr << *a; @@ -840,6 +899,16 @@ static void AssignDouble2StringGeneric(asIScriptGeneric *gen) gen->SetReturnAddress(self); } +static void AssignFloat2StringGeneric(asIScriptGeneric *gen) +{ + float *a = static_cast(gen->GetAddressOfArg(0)); + string *self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self = sstr.str(); + gen->SetReturnAddress(self); +} + static void AssignBool2StringGeneric(asIScriptGeneric *gen) { bool *a = static_cast(gen->GetAddressOfArg(0)); @@ -860,9 +929,19 @@ static void AddAssignDouble2StringGeneric(asIScriptGeneric * gen) gen->SetReturnAddress(self); } +static void AddAssignFloat2StringGeneric(asIScriptGeneric * gen) +{ + float * a = static_cast(gen->GetAddressOfArg(0)); + string * self = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a; + *self += sstr.str(); + gen->SetReturnAddress(self); +} + static void AddAssignInt2StringGeneric(asIScriptGeneric * gen) { - int * a = static_cast(gen->GetAddressOfArg(0)); + asINT64 * a = static_cast(gen->GetAddressOfArg(0)); string * self = static_cast(gen->GetObject()); std::stringstream sstr; sstr << *a; @@ -872,7 +951,7 @@ static void AddAssignInt2StringGeneric(asIScriptGeneric * gen) static void AddAssignUInt2StringGeneric(asIScriptGeneric * gen) { - unsigned int * a = static_cast(gen->GetAddressOfArg(0)); + asQWORD * a = static_cast(gen->GetAddressOfArg(0)); string * self = static_cast(gen->GetObject()); std::stringstream sstr; sstr << *a; @@ -900,10 +979,20 @@ static void AddString2DoubleGeneric(asIScriptGeneric * gen) gen->SetReturnObject(&ret_val); } +static void AddString2FloatGeneric(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + float * b = static_cast(gen->GetAddressOfArg(0)); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + static void AddString2IntGeneric(asIScriptGeneric * gen) { string * a = static_cast(gen->GetObject()); - int * b = static_cast(gen->GetAddressOfArg(0)); + asINT64 * b = static_cast(gen->GetAddressOfArg(0)); std::stringstream sstr; sstr << *a << *b; std::string ret_val = sstr.str(); @@ -913,7 +1002,7 @@ static void AddString2IntGeneric(asIScriptGeneric * gen) static void AddString2UIntGeneric(asIScriptGeneric * gen) { string * a = static_cast(gen->GetObject()); - unsigned int * b = static_cast(gen->GetAddressOfArg(0)); + asQWORD * b = static_cast(gen->GetAddressOfArg(0)); std::stringstream sstr; sstr << *a << *b; std::string ret_val = sstr.str(); @@ -940,9 +1029,19 @@ static void AddDouble2StringGeneric(asIScriptGeneric * gen) gen->SetReturnObject(&ret_val); } +static void AddFloat2StringGeneric(asIScriptGeneric * gen) +{ + float* a = static_cast(gen->GetAddressOfArg(0)); + string * b = static_cast(gen->GetObject()); + std::stringstream sstr; + sstr << *a << *b; + std::string ret_val = sstr.str(); + gen->SetReturnObject(&ret_val); +} + static void AddInt2StringGeneric(asIScriptGeneric * gen) { - int* a = static_cast(gen->GetAddressOfArg(0)); + asINT64* a = static_cast(gen->GetAddressOfArg(0)); string * b = static_cast(gen->GetObject()); std::stringstream sstr; sstr << *a << *b; @@ -952,7 +1051,7 @@ static void AddInt2StringGeneric(asIScriptGeneric * gen) static void AddUInt2StringGeneric(asIScriptGeneric * gen) { - unsigned int* a = static_cast(gen->GetAddressOfArg(0)); + asQWORD* a = static_cast(gen->GetAddressOfArg(0)); string * b = static_cast(gen->GetObject()); std::stringstream sstr; sstr << *a << *b; @@ -983,7 +1082,8 @@ static void StringSubString_Generic(asIScriptGeneric *gen) void RegisterStdString_Generic(asIScriptEngine *engine) { - int r; + int r = 0; + UNUSED_VAR(r); // Register the string type r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 ); @@ -1027,15 +1127,20 @@ void RegisterStdString_Generic(asIScriptEngine *engine) r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddString2DoubleGeneric), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string &opAssign(int)", asFUNCTION(AssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string &opAddAssign(int)", asFUNCTION(AddAssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string opAdd(int) const", asFUNCTION(AddString2IntGeneric), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string opAdd_r(int) const", asFUNCTION(AddInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(float) const", asFUNCTION(AddString2FloatGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(float) const", asFUNCTION(AddFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string &opAssign(uint)", asFUNCTION(AssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint)", asFUNCTION(AddAssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string opAdd(uint) const", asFUNCTION(AddString2UIntGeneric), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("string", "string opAdd_r(uint) const", asFUNCTION(AddUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAssign(int64)", asFUNCTION(AssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(int64)", asFUNCTION(AddAssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(int64) const", asFUNCTION(AddString2IntGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(int64) const", asFUNCTION(AddInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(uint64)", asFUNCTION(AssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint64)", asFUNCTION(AddAssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd(uint64) const", asFUNCTION(AddString2UIntGeneric), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string opAdd_r(uint64) const", asFUNCTION(AddUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 ); diff --git a/src/scriptengine/scriptstdstring.hpp b/src/scriptengine/scriptstdstring.hpp index 5bf26ae8f..11181a0fb 100644 --- a/src/scriptengine/scriptstdstring.hpp +++ b/src/scriptengine/scriptstdstring.hpp @@ -1,20 +1,3 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2014-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - // // Script std::string // @@ -30,7 +13,7 @@ #ifndef ANGELSCRIPT_H // Avoid having to inform include path if header is already include before -#include "angelscript.h" +#include #endif #include diff --git a/src/scriptengine/scriptstdstring_utils.cpp b/src/scriptengine/scriptstdstring_utils.cpp index fe8986b31..d46fea751 100644 --- a/src/scriptengine/scriptstdstring_utils.cpp +++ b/src/scriptengine/scriptstdstring_utils.cpp @@ -1,20 +1,3 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2014-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - #include #include "scriptstdstring.hpp" #include "scriptarray.hpp" @@ -39,45 +22,45 @@ BEGIN_AS_NAMESPACE // array@ string::split(const string &in delim) const static CScriptArray *StringSplit(const string &delim, const string &str) { - // Obtain a pointer to the engine - asIScriptContext *ctx = asGetActiveContext(); - asIScriptEngine *engine = ctx->GetEngine(); + // Obtain a pointer to the engine + asIScriptContext *ctx = asGetActiveContext(); + asIScriptEngine *engine = ctx->GetEngine(); - // TODO: This should only be done once - // TODO: This assumes that CScriptArray was already registered - asIObjectType *arrayType = engine->GetObjectTypeById(engine->GetTypeIdByDecl("array")); + // TODO: This should only be done once + // TODO: This assumes that CScriptArray was already registered + asIObjectType *arrayType = engine->GetObjectTypeByDecl("array"); - // Create the array object - CScriptArray *array = new CScriptArray(0, arrayType); + // Create the array object + CScriptArray *array = CScriptArray::Create(arrayType); - // Find the existence of the delimiter in the input string - int pos = 0, prev = 0, count = 0; - while ((pos = (int)str.find(delim, prev)) != (int)string::npos) - { - // Add the part to the array - array->Resize(array->GetSize() + 1); - ((string*)array->At(count))->assign(&str[prev], pos - prev); + // Find the existence of the delimiter in the input string + int pos = 0, prev = 0, count = 0; + while( (pos = (int)str.find(delim, prev)) != (int)string::npos ) + { + // Add the part to the array + array->Resize(array->GetSize()+1); + ((string*)array->At(count))->assign(&str[prev], pos-prev); - // Find the next part - count++; - prev = pos + (int)delim.length(); - } + // Find the next part + count++; + prev = pos + (int)delim.length(); + } - // Add the remaining part - array->Resize(array->GetSize() + 1); - ((string*)array->At(count))->assign(&str[prev]); + // Add the remaining part + array->Resize(array->GetSize()+1); + ((string*)array->At(count))->assign(&str[prev]); - return array; + return array; } static void StringSplit_Generic(asIScriptGeneric *gen) { - // Get the arguments - string *str = (string*)gen->GetObject(); - string *delim = *(string**)gen->GetAddressOfArg(0); + // Get the arguments + string *str = (string*)gen->GetObject(); + string *delim = *(string**)gen->GetAddressOfArg(0); - // Return the array by handle - *(CScriptArray**)gen->GetAddressOfReturnLocation() = StringSplit(*delim, *str); + // Return the array by handle + *(CScriptArray**)gen->GetAddressOfReturnLocation() = StringSplit(*delim, *str); } @@ -97,50 +80,50 @@ static void StringSplit_Generic(asIScriptGeneric *gen) // string join(const array &in array, const string &in delim) static string StringJoin(const CScriptArray &array, const string &delim) { - // Create the new string - string str = ""; - if (array.GetSize()) - { - int n; - for (n = 0; n < (int)array.GetSize() - 1; n++) - { - str += *(string*)array.At(n); - str += delim; - } + // Create the new string + string str = ""; + if( array.GetSize() ) + { + int n; + for( n = 0; n < (int)array.GetSize() - 1; n++ ) + { + str += *(string*)array.At(n); + str += delim; + } - // Add the last part - str += *(string*)array.At(n); - } + // Add the last part + str += *(string*)array.At(n); + } - return str; + return str; } static void StringJoin_Generic(asIScriptGeneric *gen) { - // Get the arguments - CScriptArray *array = *(CScriptArray**)gen->GetAddressOfArg(0); - string *delim = *(string**)gen->GetAddressOfArg(1); + // Get the arguments + CScriptArray *array = *(CScriptArray**)gen->GetAddressOfArg(0); + string *delim = *(string**)gen->GetAddressOfArg(1); - // Return the string - new(gen->GetAddressOfReturnLocation()) string(StringJoin(*array, *delim)); + // Return the string + new(gen->GetAddressOfReturnLocation()) string(StringJoin(*array, *delim)); } // This is where the utility functions are registered. // The string type must have been registered first. void RegisterStdStringUtils(asIScriptEngine *engine) { - int r; + int r; - if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")) - { - r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0); - } - else - { - r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0); - } + if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) + { + r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0); + } + else + { + r = engine->RegisterObjectMethod("string", "array@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterGlobalFunction("string join(const array &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0); + } } -END_AS_NAMESPACE \ No newline at end of file +END_AS_NAMESPACE From 6ccebd09b62417f5b69734b4d8154c76569315f9 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Mon, 11 May 2015 21:49:13 -0400 Subject: [PATCH 26/44] Improve scripting compatibility with Linux/GCC --- src/scriptengine/script_kart.cpp | 13 +++++++------ src/scriptengine/scriptvec3.cpp | 19 ++++++++++--------- src/scriptengine/scriptvec3.hpp | 16 +++++++++++++++- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/scriptengine/script_kart.cpp b/src/scriptengine/script_kart.cpp index d950b2ccf..2b2a8aacd 100644 --- a/src/scriptengine/script_kart.cpp +++ b/src/scriptengine/script_kart.cpp @@ -21,6 +21,7 @@ #include "karts/kart.hpp" #include "modes/world.hpp" #include "script_kart.hpp" +#include "scriptvec3.hpp" //debug #include @@ -47,12 +48,11 @@ namespace Scripting } /** Teleports the kart to the specified Vec3 location */ - void teleport(int idKart, Vec3* position) + void teleport(int idKart, SimpleVec3* position) { - // TODO: must we delete "position" ? - AbstractKart* kart = World::getWorld()->getKart(idKart); - kart->setXYZ(*position); + Vec3 v(position->getX(), position->getY(), position->getZ()); + kart->setXYZ(v); unsigned int index = World::getWorld()->getRescuePositionIndex(kart); btTransform s = World::getWorld()->getRescueTransform(index); const btVector3 &xyz = s.getOrigin(); @@ -92,10 +92,11 @@ namespace Scripting //} /** Returns the location of the corresponding kart. */ - Vec3 getLocation(int idKart) + SimpleVec3 getLocation(int idKart) { AbstractKart* kart = World::getWorld()->getKart(idKart); - return kart->getXYZ(); + Vec3 v = kart->getXYZ(); + return SimpleVec3(v.getX(), v.getY(), v.getZ()); } /** Sets the kart's velocity to the specified value. */ diff --git a/src/scriptengine/scriptvec3.cpp b/src/scriptengine/scriptvec3.cpp index 1be851d6f..b67c82a39 100644 --- a/src/scriptengine/scriptvec3.cpp +++ b/src/scriptengine/scriptvec3.cpp @@ -20,6 +20,7 @@ #include #include "karts/kart.hpp" #include "script_kart.hpp" +#include "scriptvec3.hpp" //debug #include @@ -31,40 +32,40 @@ namespace Scripting { // Initialize the pre-allocated memory by calling the // object constructor with the placement-new operator - new(memory)Vec3(); + new(memory)SimpleVec3(); } void Destructor(void *memory) { // Uninitialize the memory by calling the object destructor - ((Vec3*)memory)->~Vec3(); + ((SimpleVec3*)memory)->~SimpleVec3(); } void ConstructVector3FromFloats(float a, float b, float c, void *memory) { //Constructor using 3 floats - new (memory)(Vec3)(Vec3(a, b, c)); + new (memory)(SimpleVec3)(SimpleVec3(a, b, c)); } //Print for debugging purposes void printVec3(asIScriptGeneric *gen) { - Vec3 *script_vec3 = (Vec3*)gen->GetArgObject(0); + SimpleVec3 *script_vec3 = (SimpleVec3*)gen->GetArgObject(0); std::cout << script_vec3->getX() << "," << script_vec3->getY() << "," << script_vec3->getZ() << std::endl; } void RegisterVec3(asIScriptEngine *engine) { int r; - r = engine->RegisterObjectType("Vec3", sizeof(Vec3), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert(r >= 0); + r = engine->RegisterObjectType("Vec3", sizeof(SimpleVec3), asOBJ_VALUE | asOBJ_APP_CLASS_ALLFLOATS | asGetTypeTraits()); assert(r >= 0); // Register the behaviours r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Constructor), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Destructor), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("Vec3", "Vec3 &opAssign(const Vec3 &in)", asMETHODPR(Vec3, operator =, (const Vec3&), Vec3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "Vec3 &opAssign(const Vec3 &in)", asMETHODPR(SimpleVec3, operator =, (const SimpleVec3&), SimpleVec3&), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTION(ConstructVector3FromFloats), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterGlobalFunction("void printVec3(Vec3 a)", asFUNCTION(printVec3), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterObjectMethod("Vec3", "float &getX()", asMETHOD(Vec3, getX), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("Vec3", "float &getY()", asMETHOD(Vec3, getY), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("Vec3", "float &getZ()", asMETHOD(Vec3, getZ), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float &getX()", asMETHOD(SimpleVec3, getX), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float &getY()", asMETHOD(SimpleVec3, getY), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float &getZ()", asMETHOD(SimpleVec3, getZ), asCALL_THISCALL); assert(r >= 0); } } diff --git a/src/scriptengine/scriptvec3.hpp b/src/scriptengine/scriptvec3.hpp index 0aae113e9..5de506dd3 100644 --- a/src/scriptengine/scriptvec3.hpp +++ b/src/scriptengine/scriptvec3.hpp @@ -23,8 +23,22 @@ namespace Scripting { - void RegisterVec3(asIScriptEngine *engine); + struct SimpleVec3 + { + float x; + float y; + float z; + + float getX() { return x; } + float getY() { return y; } + float getZ() { return z; } + + SimpleVec3() : x(0), y(0), z(0) { } + SimpleVec3(float p_x, float p_y, float p_z) : x(p_x), y(p_y), z(p_z) { } + SimpleVec3(const SimpleVec3& other) : x(other.x), y(other.y), z(other.z) { } + ~SimpleVec3() { } + }; } #endif From 465130a4a27160656834ca065909c68a41c7089c Mon Sep 17 00:00:00 2001 From: Karl Ove Hufthammer Date: Tue, 12 May 2015 18:04:15 +0200 Subject: [PATCH 27/44] =?UTF-8?q?Added=20=E2=80=98=C3=86=E2=80=99=20glyph?= =?UTF-8?q?=20to=20the=20title=20font?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The glyph was created by combining the existing ‘A’ glyph and ‘E’ glyph, and then retouching the result to make it look good (both the glyph heights and the gradients differed for the two letters). --- data/fonts/title_font.xml | Bin 17520 -> 17754 bytes data/fonts/title_font_2.png | Bin 164907 -> 157745 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/fonts/title_font.xml b/data/fonts/title_font.xml index f7abb3e2ed2371861cf140305a8f0d7a3b08736d..c183b565f2aa836b5ec7d8bafdd179782addc098 100644 GIT binary patch delta 123 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 literal 164907 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 + diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 5481f763e..c7a25fdd0 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -171,7 +171,7 @@ void Physics::update(float dt) Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); int kartid1 = p->getUserPointer(0)->getPointerKart()->getWorldKartId(); int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); - script_engine->runScript("void onKartKartCollision(int, int)", + script_engine->runFunction("void onKartKartCollision(int, int)", [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartid1); ctx->SetArgDWord(1, kartid2); @@ -189,7 +189,7 @@ void Physics::update(float dt) std::string obj_id = p->getUserPointer(0)->getPointerPhysicalObject()->getID(); // TODO: pass obj_id as arguent - script_engine->runScript("void onKartObjectCollision(int)", + script_engine->runFunction("void onKartObjectCollision(int)", [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, kartId); }); @@ -260,7 +260,7 @@ void Physics::update(float dt) // p->getUserPointer(1)->getPointerPhysicalObject()->getID(), // "item" //); - script_engine->runScript("void onItemObjectCollision()"); + script_engine->runFunction("void onItemObjectCollision()"); p->getUserPointer(0)->getPointerFlyable() ->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject()); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index eb3a7af6b..8d20e6361 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -38,308 +38,372 @@ using namespace Scripting; namespace Scripting { + const char* MODULE_ID_MAIN_SCRIPT_FILE = "main"; + const char* MODULE_ID_EVAL = "eval"; - -void AngelScript_ErrorCallback (const asSMessageInfo *msg, void *param) -{ - const char *type = "ERR "; - if (msg->type == asMSGTYPE_WARNING) - type = "WARN"; - else if (msg->type == asMSGTYPE_INFORMATION) - type = "INFO"; - - Log::warn("Scripting", "%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message); -} - - -//Constructor, creates a new Scripting Engine using AngelScript -ScriptEngine::ScriptEngine() -{ - // Create the script engine - m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); - if (m_engine == NULL) + void AngelScript_ErrorCallback (const asSMessageInfo *msg, void *param) { - Log::fatal("Scripting", "Failed to create script engine."); + const char *type = "ERR "; + if (msg->type == asMSGTYPE_WARNING) + type = "WARN"; + else if (msg->type == asMSGTYPE_INFORMATION) + type = "INFO"; + + Log::warn("Scripting", "%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message); } - // The script compiler will write any compiler messages to the callback. - m_engine->SetMessageCallback(asFUNCTION(AngelScript_ErrorCallback), 0, asCALL_CDECL); - // Configure the script engine with all the functions, - // and variables that the script should be able to use. - configureEngine(m_engine); -} - -ScriptEngine::~ScriptEngine() -{ - // Release the engine - m_engine->Release(); -} - - - -/** Get Script By it's file name -* \param string scriptname = name of script to get -* \return The corresponding script -*/ -std::string getScript(std::string scriptName) -{ - std::string script_dir = file_manager->getAsset(FileManager::SCRIPT, ""); - script_dir += World::getWorld()->getTrack()->getIdent() + "/"; - - // TODO: allow splitting in multiple files - script_dir += "scripting.as"; - FILE *f = fopen(script_dir.c_str(), "rb"); - if (f == NULL) + //Constructor, creates a new Scripting Engine using AngelScript + ScriptEngine::ScriptEngine() { - Log::debug("Scripting", "File does not exist : {0}.as", scriptName.c_str()); - return ""; + // Create the script engine + m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); + if (m_engine == NULL) + { + Log::fatal("Scripting", "Failed to create script engine."); + } + + // The script compiler will write any compiler messages to the callback. + m_engine->SetMessageCallback(asFUNCTION(AngelScript_ErrorCallback), 0, asCALL_CDECL); + + // Configure the script engine with all the functions, + // and variables that the script should be able to use. + configureEngine(m_engine); } - // Determine the size of the file - fseek(f, 0, SEEK_END); - int len = ftell(f); - fseek(f, 0, SEEK_SET); - - // Read the entire file - std::string script; - script.resize(len); - int c = fread(&script[0], len, 1, f); - fclose(f); - if (c == NULL) + ScriptEngine::~ScriptEngine() { - Log::error("Scripting", "Failed to load script file."); - return ""; + // Release the engine + m_engine->Release(); } - return script; -} -//----------------------------------------------------------------------------- -/** runs the specified script -* \param string scriptName = name of script to run -*/ -void ScriptEngine::runScript(std::string scriptName) -{ - std::function callback; - runScript(scriptName, callback); -} -//----------------------------------------------------------------------------- - -/** runs the specified script -* \param string scriptName = name of script to run -*/ -void ScriptEngine::runScript(std::string scriptName, std::function callback) -{ - int r; //int for error checking - - asIScriptFunction *func; - auto cached_script = m_script_cache.find(scriptName); - if (cached_script == m_script_cache.end()) + /** Get Script By it's file name + * \param string scriptname = name of script to get + * \return The corresponding script + */ + std::string getScript(std::string fileName) { - // Compile the script code - Log::debug("Scripting", "Compiling script '%s' (was not in cache)", scriptName.c_str()); - r = compileScript(m_engine, scriptName); + std::string script_dir = file_manager->getAsset(FileManager::SCRIPT, ""); + script_dir += World::getWorld()->getTrack()->getIdent() + "/"; + + script_dir += fileName; + FILE *f = fopen(script_dir.c_str(), "rb"); + if (f == NULL) + { + Log::debug("Scripting", "File does not exist : {0}.as", fileName.c_str()); + return ""; + } + + // Determine the size of the file + fseek(f, 0, SEEK_END); + int len = ftell(f); + fseek(f, 0, SEEK_SET); + + // Read the entire file + std::string script; + script.resize(len); + int c = fread(&script[0], len, 1, f); + fclose(f); + if (c == NULL) + { + Log::error("Scripting", "Failed to load script file."); + return ""; + } + return script; + } + + //----------------------------------------------------------------------------- + + void ScriptEngine::evalScript(std::string script_fragment) + { + script_fragment = "void evalScript_main() { \n" + script_fragment + "\n}"; + + asIScriptModule* mod = m_engine->GetModule(MODULE_ID_EVAL, asGM_ALWAYS_CREATE); + int r = mod->AddScriptSection("script", &script_fragment[0], script_fragment.size()); if (r < 0) { - Log::debug("Scripting", "Script '%s' is not available", scriptName.c_str()); - m_script_cache[scriptName] = NULL; // remember that this script is unavailable + Log::error("Scripting", "evalScript: AddScriptSection() failed"); return; } - // Find the function for the function we want to execute. - //This is how you call a normal function with arguments - //asIScriptFunction *func = engine->GetModule(0)->GetFunctionByDecl("void func(arg1Type, arg2Type)"); - func = registerScriptCallbacks(m_engine, scriptName); - - if (func == NULL) + r = mod->Build(); + if (r < 0) { - Log::debug("Scripting", "Scripting function was not found : %s", scriptName.c_str()); - m_script_cache[scriptName] = NULL; // remember that this script is unavailable + Log::error("Scripting", "evalScript: Build() failed"); return; } - //CACHE UPDATE - m_script_cache[scriptName] = func; - func->AddRef(); - } - else - { - // Script present in cache - func = cached_script->second; - } + asIScriptFunction* func = m_engine->GetModule(MODULE_ID_EVAL) + ->GetFunctionByDecl("void evalScript_main()"); - if (func == NULL) - { - return; // script unavailable - } + asIScriptContext *ctx = m_engine->CreateContext(); + if (ctx == NULL) + { + Log::error("Scripting", "evalScript: Failed to create the context."); + //m_engine->Release(); + return; + } + r = ctx->Prepare(func); + if (r < 0) + { + Log::error("Scripting", "evalScript: Failed to prepare the context."); + ctx->Release(); + return; + } - // Create a context that will execute the script. - asIScriptContext *ctx = m_engine->CreateContext(); - if (ctx == NULL) - { - Log::error("Scripting", "Failed to create the context."); - //m_engine->Release(); - return; - } + // Execute the function + r = ctx->Execute(); + if (r != asEXECUTION_FINISHED) + { + // The execution didn't finish as we had planned. Determine why. + if (r == asEXECUTION_ABORTED) + { + Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out."); + } + else if (r == asEXECUTION_EXCEPTION) + { + Log::error("Scripting", "The script ended with an exception."); + } + else + { + Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); + } + } - // Prepare the script context with the function we wish to execute. Prepare() - // must be called on the context before each new script function that will be - // executed. Note, that if because we intend to execute the same function - // several times, we will store the function returned by - // GetFunctionByDecl(), so that this relatively slow call can be skipped. - r = ctx->Prepare(func); - if (r < 0) - { - Log::error("Scripting", "Failed to prepare the context."); ctx->Release(); - //m_engine->Release(); - return; } - // Here, we can pass parameters to the script functions. - //ctx->setArgType(index, value); - //for example : ctx->SetArgFloat(0, 3.14159265359f); + //----------------------------------------------------------------------------- - if (callback) - callback(ctx); - - // Execute the function - r = ctx->Execute(); - if (r != asEXECUTION_FINISHED) + /** runs the specified script + * \param string scriptName = name of script to run + */ + void ScriptEngine::runFunction(std::string function_name) { - // The execution didn't finish as we had planned. Determine why. - if (r == asEXECUTION_ABORTED) - { - Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out."); - } - else if (r == asEXECUTION_EXCEPTION) - { - Log::error("Scripting", "The script ended with an exception."); + std::function callback; + runFunction(function_name, callback); + } - // Write some information about the script exception - asIScriptFunction *func = ctx->GetExceptionFunction(); - //std::cout << "func: " << func->GetDeclaration() << std::endl; - //std::cout << "modl: " << func->GetModuleName() << std::endl; - //std::cout << "sect: " << func->GetScriptSectionName() << std::endl; - //std::cout << "line: " << ctx->GetExceptionLineNumber() << std::endl; - //std::cout << "desc: " << ctx->GetExceptionString() << std::endl; + //----------------------------------------------------------------------------- + + /** runs the specified script + * \param string scriptName = name of script to run + */ + void ScriptEngine::runFunction(std::string function_name, std::function callback) + { + int r; //int for error checking + + asIScriptFunction *func; + + // TODO: allow splitting in multiple files + std::string script_filename = "scripting.as"; + auto cached_script = m_loaded_files.find(script_filename); + auto cached_function = m_functions_cache.find(function_name); + + if (cached_script == m_loaded_files.end()) + { + // Compile the script code + Log::debug("Scripting", "Compiling script '%s' (was not in cache)", script_filename.c_str()); + r = compileScript(m_engine, script_filename); + if (r < 0) + { + Log::debug("Scripting", "Script '%s' is not available", script_filename.c_str()); + m_loaded_files[script_filename] = false; + m_functions_cache[function_name] = NULL; // remember that this script is unavailable + return; + } + + m_loaded_files[script_filename] = true; + } + else if (cached_script->second == false) + { + return; // script file unavailable + } + + if (cached_function == m_functions_cache.end()) + { + // Find the function for the function we want to execute. + // This is how you call a normal function with arguments + // asIScriptFunction *func = engine->GetModule(0)->GetFunctionByDecl("void func(arg1Type, arg2Type)"); + func = m_engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE) + ->GetFunctionByDecl(function_name.c_str()); + + if (func == NULL) + { + Log::debug("Scripting", "Scripting function was not found : %s", function_name.c_str()); + m_loaded_files[function_name] = NULL; // remember that this script is unavailable + return; + } + + m_functions_cache[function_name] = func; + func->AddRef(); } else { - Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); + // Script present in cache + func = cached_function->second; } + + if (func == NULL) + { + return; // function unavailable + } + + // Create a context that will execute the script. + asIScriptContext *ctx = m_engine->CreateContext(); + if (ctx == NULL) + { + Log::error("Scripting", "Failed to create the context."); + //m_engine->Release(); + return; + } + + // Prepare the script context with the function we wish to execute. Prepare() + // must be called on the context before each new script function that will be + // executed. Note, that if because we intend to execute the same function + // several times, we will store the function returned by + // GetFunctionByDecl(), so that this relatively slow call can be skipped. + r = ctx->Prepare(func); + if (r < 0) + { + Log::error("Scripting", "Failed to prepare the context."); + ctx->Release(); + //m_engine->Release(); + return; + } + + // Here, we can pass parameters to the script functions. + //ctx->setArgType(index, value); + //for example : ctx->SetArgFloat(0, 3.14159265359f); + + if (callback) + callback(ctx); + + // Execute the function + r = ctx->Execute(); + if (r != asEXECUTION_FINISHED) + { + // The execution didn't finish as we had planned. Determine why. + if (r == asEXECUTION_ABORTED) + { + Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out."); + } + else if (r == asEXECUTION_EXCEPTION) + { + Log::error("Scripting", "The script ended with an exception."); + + // Write some information about the script exception + asIScriptFunction *func = ctx->GetExceptionFunction(); + //std::cout << "func: " << func->GetDeclaration() << std::endl; + //std::cout << "modl: " << func->GetModuleName() << std::endl; + //std::cout << "sect: " << func->GetScriptSectionName() << std::endl; + //std::cout << "line: " << ctx->GetExceptionLineNumber() << std::endl; + //std::cout << "desc: " << ctx->GetExceptionString() << std::endl; + } + else + { + Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); + } + } + else + { + // Retrieve the return value from the context here (for scripts that return values) + // returnValue = ctx->getReturnType(); for example + //float returnValue = ctx->GetReturnFloat(); + } + + // We must release the contexts when no longer using them + ctx->Release(); } - else + + //----------------------------------------------------------------------------- + + void ScriptEngine::cleanupCache() { - // Retrieve the return value from the context here (for scripts that return values) - // returnValue = ctx->getReturnType(); for example - //float returnValue = ctx->GetReturnFloat(); + for (auto curr : m_functions_cache) + { + if (curr.second != NULL) + curr.second->Release(); + } + m_functions_cache.clear(); + m_loaded_files.clear(); } - // We must release the contexts when no longer using them - ctx->Release(); -} - -//----------------------------------------------------------------------------- - -asIScriptFunction* ScriptEngine::registerScriptCallbacks(asIScriptEngine *engine, - std::string function_name) -{ - asIScriptFunction *func; - func = engine->GetModule(0)->GetFunctionByDecl(function_name.c_str()); - return func; -} - -//----------------------------------------------------------------------------- - -void ScriptEngine::cleanupCache() -{ - for (auto curr : m_script_cache) + //----------------------------------------------------------------------------- + /** Configures the script engine by binding functions, enums + * \param asIScriptEngine engine = engine to configure + */ + void ScriptEngine::configureEngine(asIScriptEngine *engine) { - if (curr.second != NULL) - curr.second->Release(); - } - m_script_cache.clear(); -} + // Register the script string type + RegisterStdString(engine); //register std::string + RegisterVec3(engine); //register Vec3 -//----------------------------------------------------------------------------- -/** 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::Kart::registerScriptFunctions(m_engine); - Scripting::Physics::registerScriptFunctions(m_engine); - Scripting::Utils::registerScriptFunctions(m_engine); - Scripting::GUI::registerScriptFunctions(m_engine); - Scripting::GUI::registerScriptEnums(m_engine); + Scripting::Track::registerScriptFunctions(m_engine); + Scripting::Kart::registerScriptFunctions(m_engine); + Scripting::Physics::registerScriptFunctions(m_engine); + Scripting::Utils::registerScriptFunctions(m_engine); + Scripting::GUI::registerScriptFunctions(m_engine); + Scripting::GUI::registerScriptEnums(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. -} - -//----------------------------------------------------------------------------- - -int ScriptEngine::compileScript(asIScriptEngine *engine, std::string scriptName) -{ - int r; - - std::string script = getScript(scriptName); - if (script.size() == 0) - { - // No such file - return -1; + // 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. } - // Add the script sections that will be compiled into executable code. - // If we want to combine more than one file into the same script, then - // we can call AddScriptSection() several times for the same module and - // the script engine will treat them all as if they were one. The script - // section name, will allow us to localize any errors in the script code. - asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); - r = mod->AddScriptSection("script", &script[0], script.size()); - if (r < 0) + //----------------------------------------------------------------------------- + + int ScriptEngine::compileScript(asIScriptEngine *engine, std::string scriptName) { - Log::error("Scripting", "AddScriptSection() failed"); - return -1; - } + int r; + + std::string script = getScript(scriptName); + if (script.size() == 0) + { + // No such file + return -1; + } + + // Add the script sections that will be compiled into executable code. + // If we want to combine more than one file into the same script, then + // we can call AddScriptSection() several times for the same module and + // the script engine will treat them all as if they were one. The script + // section name, will allow us to localize any errors in the script code. + asIScriptModule *mod = engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE, asGM_ALWAYS_CREATE); + r = mod->AddScriptSection("script", &script[0], script.size()); + if (r < 0) + { + Log::error("Scripting", "AddScriptSection() failed"); + return -1; + } - // Compile the script. If there are any compiler messages they will - // be written to the message stream that we set right after creating the - // script engine. If there are no errors, and no warnings, nothing will - // be written to the stream. - r = mod->Build(); - if (r < 0) - { - Log::error("Scripting", "Build() failed"); - return -1; + // Compile the script. If there are any compiler messages they will + // be written to the message stream that we set right after creating the + // script engine. If there are no errors, and no warnings, nothing will + // be written to the stream. + r = mod->Build(); + if (r < 0) + { + Log::error("Scripting", "Build() failed"); + return -1; + } + + // The engine doesn't keep a copy of the script sections after Build() has + // returned. So if the script needs to be recompiled, then all the script + // sections must be added again. + + // If we want to have several scripts executing at different times but + // that have no direct relation with each other, then we can compile them + // into separate script modules. Each module uses their own namespace and + // scope, so function names, and global variables will not conflict with + // each other. + + return 0; } - - // The engine doesn't keep a copy of the script sections after Build() has - // returned. So if the script needs to be recompiled, then all the script - // sections must be added again. - - // If we want to have several scripts executing at different times but - // that have no direct relation with each other, then we can compile them - // into separate script modules. Each module uses their own namespace and - // scope, so function names, and global variables will not conflict with - // each other. - - return 0; -} - - - } diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index 9dca59770..5a7e01e88 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -51,16 +51,16 @@ namespace Scripting ScriptEngine(); ~ScriptEngine(); - - void runScript(std::string scriptName); - void runScript(std::string scriptName, std::function callback); + + void runFunction(std::string function_name); + void runFunction(std::string function_name, std::function callback); + void evalScript(std::string script_fragment); void cleanupCache(); - asIScriptFunction* - registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName); private: asIScriptEngine *m_engine; - std::map m_script_cache; + std::map m_loaded_files; + std::map m_functions_cache; void configureEngine(asIScriptEngine *engine); int compileScript(asIScriptEngine *engine,std::string scriptName); diff --git a/src/scriptengine/script_kart.cpp b/src/scriptengine/script_kart.cpp index 2b2a8aacd..a13b05039 100644 --- a/src/scriptengine/script_kart.cpp +++ b/src/scriptengine/script_kart.cpp @@ -115,7 +115,7 @@ namespace Scripting void registerScriptFunctions(asIScriptEngine *engine) { - int r; + int r; // of type asERetCodes engine->SetDefaultNamespace("Kart"); r = engine->RegisterGlobalFunction("void squash(int id, float time)", asFUNCTION(squash), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void teleport(int id, const Vec3 &in)", asFUNCTION(teleport), asCALL_CDECL); assert(r >= 0); diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index e27fc64c7..921e5f440 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -227,7 +227,7 @@ namespace Scripting void registerScriptFunctions(asIScriptEngine *engine) { - int r; + int r; // of type asERetCodes engine->SetDefaultNamespace("Track"); @@ -243,7 +243,7 @@ namespace Scripting r = engine->RegisterGlobalFunction("void enableTrigger(const string &in)", asFUNCTION(enableTrigger), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void disableTrigger(const string &in)", asFUNCTION(disableTrigger), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void createTrigger(const string &in, const Vec3 &in, float distance)", - asFUNCTION(createTrigger), asCALL_GENERIC); assert(r >= 0); + asFUNCTION(createTrigger), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0); // TrackObject diff --git a/src/scriptengine/script_utils.cpp b/src/scriptengine/script_utils.cpp index ca057b936..1768d9c20 100644 --- a/src/scriptengine/script_utils.cpp +++ b/src/scriptengine/script_utils.cpp @@ -107,7 +107,7 @@ namespace Scripting { std::string *str = (std::string*)gen->GetArgAddress(0); ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - script_engine->runScript(*str); + script_engine->runFunction(*str); } /** Log to the console */ diff --git a/src/states_screens/dialogs/scripting_console.cpp b/src/states_screens/dialogs/scripting_console.cpp new file mode 100644 index 000000000..f7b7228f6 --- /dev/null +++ b/src/states_screens/dialogs/scripting_console.cpp @@ -0,0 +1,86 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014-2015 Marc Coll +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/dialogs/scripting_console.hpp" + +#include "guiengine/engine.hpp" +#include "guiengine/widgets/button_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/text_box_widget.hpp" +#include "modes/world.hpp" +#include "scriptengine/script_engine.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" + +#include + + +using namespace GUIEngine; +using namespace irr::core; + +// ----------------------------------------------------------------------------- + +ScriptingConsole::ScriptingConsole() : + ModalDialog(0.95f, 0.2f, GUIEngine::MODAL_DIALOG_LOCATION_BOTTOM) +{ + m_fade_background = false; + loadFromFile("scripting_console.stkgui"); + + TextBoxWidget* textCtrl = getWidget("textfield"); + assert(textCtrl != NULL); + textCtrl->setFocusForPlayer(PLAYER_ID_GAME_MASTER); +} + +// ----------------------------------------------------------------------------- + +ScriptingConsole::~ScriptingConsole() +{ + // FIXME: what is this code for? + TextBoxWidget* textCtrl = getWidget("textfield"); + textCtrl->getIrrlichtElement()->remove(); + textCtrl->clearListeners(); +} + +// ----------------------------------------------------------------------------- + +GUIEngine::EventPropagation ScriptingConsole::processEvent(const std::string& eventSource) +{ + if (eventSource == "close") + { + dismiss(); + return GUIEngine::EVENT_BLOCK; + } + else if (eventSource == "run") + { + runScript(); + return GUIEngine::EVENT_BLOCK; + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- + +void ScriptingConsole::runScript() +{ + TextBoxWidget* textCtrl = getWidget("textfield"); + core::stringw script = textCtrl->getText(); + textCtrl->setText(L""); + + World::getWorld()->getScriptEngine()->evalScript(core::stringc(script.c_str()).c_str()); +} + +// ----------------------------------------------------------------------------- diff --git a/src/states_screens/dialogs/scripting_console.hpp b/src/states_screens/dialogs/scripting_console.hpp new file mode 100644 index 000000000..f4200a715 --- /dev/null +++ b/src/states_screens/dialogs/scripting_console.hpp @@ -0,0 +1,51 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014-2015 Marc Coll +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +#ifndef HEADER_SCRIPTING_CONSOLE_DIALOG_HPP +#define HEADER_SCRIPTING_CONSOLE_DIALOG_HPP + +#include "guiengine/modaldialog.hpp" +#include "utils/cpp2011.hpp" + +#include + + +namespace GUIEngine +{ + class TextBoxWidget; + class ButtonWidget; + class LabelWidget; +} + +/** + * \brief Dialog that allows the player to enter the name for a new grand prix + * \ingroup states_screens + */ +class ScriptingConsole : public GUIEngine::ModalDialog +{ +public: + + ScriptingConsole(); + ~ScriptingConsole(); + + virtual void onEnterPressedInternal() OVERRIDE{ runScript(); } + void runScript(); + GUIEngine::EventPropagation processEvent(const std::string& eventSource); +}; + +#endif diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index deaf53440..b5d447bb1 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -1486,7 +1486,7 @@ void Track::update(float dt) if (!m_startup_run) // first time running update = good point to run startup script { Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - script_engine->runScript("void onStart()"); + script_engine->runFunction("void onStart()"); m_startup_run = true; } m_track_object_manager->update(dt); diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 8008cc94e..cf4be74ef 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -955,7 +955,7 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) Camera* camera = Camera::getActiveCamera(); if (camera != NULL && camera->getKart() != NULL) idKart = camera->getKart()->getWorldKartId(); - script_engine->runScript("void " + m_action + "(int)", + script_engine->runFunction("void " + m_action + "(int)", [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, idKart); }); } } // onTriggerItemApproached diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index b40481a27..f82bdddb4 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -33,6 +33,7 @@ #include "main_loop.hpp" #include "replay/replay_recorder.hpp" #include "states_screens/dialogs/debug_slider.hpp" +#include "states_screens/dialogs/scripting_console.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" #include "utils/profiler.hpp" @@ -103,6 +104,7 @@ enum DebugMenuCommand DEBUG_VISUAL_VALUES, DEBUG_PRINT_START_POS, DEBUG_ADJUST_LIGHTS, + DEBUG_SCRIPT_CONSOLE }; // DebugMenuCommand // ----------------------------------------------------------------------------- @@ -265,6 +267,7 @@ bool onEvent(const SEvent &event) mnu->addItem(L"Save history", DEBUG_SAVE_HISTORY); mnu->addItem(L"Print position", DEBUG_PRINT_START_POS); mnu->addItem(L"Adjust Lights", DEBUG_ADJUST_LIGHTS); + mnu->addItem(L"Scripting console", DEBUG_SCRIPT_CONSOLE); g_debug_menu_visible = true; irr_driver->showPointer(); @@ -650,6 +653,10 @@ bool onEvent(const SEvent &event) dsd->changeLabel("SSAO Sigma", "[None]"); #endif } + else if (cmdID == DEBUG_SCRIPT_CONSOLE) + { + ScriptingConsole* console = new ScriptingConsole(); + } } return false; // event has been handled From db4c6f479976c0ed78cb585ffd6f5929bb6c6710 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 17 May 2015 20:03:53 -0400 Subject: [PATCH 33/44] Work on scripting console --- src/scriptengine/script_engine.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 8d20e6361..5542bbba2 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -120,24 +120,16 @@ namespace Scripting { script_fragment = "void evalScript_main() { \n" + script_fragment + "\n}"; - asIScriptModule* mod = m_engine->GetModule(MODULE_ID_EVAL, asGM_ALWAYS_CREATE); - int r = mod->AddScriptSection("script", &script_fragment[0], script_fragment.size()); + asIScriptModule* mod = m_engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE, asGM_ONLY_IF_EXISTS); + + asIScriptFunction* func; + int r = mod->CompileFunction("eval", script_fragment.c_str(), 0, 0, &func); if (r < 0) { - Log::error("Scripting", "evalScript: AddScriptSection() failed"); + Log::error("Scripting", "evalScript: CompileFunction() failed"); return; } - r = mod->Build(); - if (r < 0) - { - Log::error("Scripting", "evalScript: Build() failed"); - return; - } - - asIScriptFunction* func = m_engine->GetModule(MODULE_ID_EVAL) - ->GetFunctionByDecl("void evalScript_main()"); - asIScriptContext *ctx = m_engine->CreateContext(); if (ctx == NULL) { @@ -174,6 +166,7 @@ namespace Scripting } ctx->Release(); + func->Release(); } //----------------------------------------------------------------------------- From e290e9003d80b76a7aaedf58186e3982b6579fb9 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 17 May 2015 20:12:39 -0400 Subject: [PATCH 34/44] Tweak for scripting documentation --- src/physics/physical_object.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index 1b7dbce87..7cebcec6d 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -208,13 +208,13 @@ public: /** Returns true if this object should cause a kart that touches it to * be flattened. */ bool isFlattenKartObject() const { return m_flatten_kart; } - void disable(void *memory) + void disable(/** \cond DOXYGEN_IGNORE */void *memory/** \endcond */) { ((PhysicalObject*)(memory))->removeBody(); } //enables track object passed from the script - void enable(void *memory) + void enable(/** \cond DOXYGEN_IGNORE */void *memory/** \endcond */) { ((PhysicalObject*)(memory))->addBody(); } From 65c25065ee99494ebc8d775c2c270bb123cce3da Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Mon, 18 May 2015 17:17:19 +0300 Subject: [PATCH 35/44] angelscript: don't use deprecated GetParamTypeId Signed-off-by: Igor Gnatenko --- src/scriptengine/scriptarray.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scriptengine/scriptarray.cpp b/src/scriptengine/scriptarray.cpp index edb2bcd1c..c04b3f98c 100644 --- a/src/scriptengine/scriptarray.cpp +++ b/src/scriptengine/scriptarray.cpp @@ -1496,7 +1496,8 @@ void CScriptArray::Precache() continue; // The parameter must either be a reference to the subtype or a handle to the subtype - int paramTypeId = func->GetParamTypeId(0, &flags); + int paramTypeId; + func->GetParam(0, ¶mTypeId, &flags, NULL, NULL); if( (paramTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) != (subTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) ) continue; From 7005f3b069ead45bf7e5f6eb99c2a4c6a6197508 Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Mon, 18 May 2015 17:23:28 +0300 Subject: [PATCH 36/44] build: allow to use system angelscript Signed-off-by: Igor Gnatenko --- CMakeLists.txt | 14 ++++++++++---- cmake/FindAngelscript.cmake | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 cmake/FindAngelscript.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c2f3f5c7..52a06913f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,9 +121,15 @@ elseif(MSVC) endif() -# Build the angelscript library -add_subdirectory("${PROJECT_SOURCE_DIR}/lib/angelscript/projects/cmake") -include_directories("${PROJECT_SOURCE_DIR}/lib/angelscript/include") +# Build the angelscript library if not in system +find_package(Angelscript) +if(ANGELSCRIPT_FOUND) + include_directories(${Angelscript_INCLUDE_DIRS}) +else() + add_subdirectory("${PROJECT_SOURCE_DIR}/lib/angelscript/projects/cmake") + include_directories("${PROJECT_SOURCE_DIR}/lib/angelscript/include") + set(Angelscript_LIBRARIES angelscript) +endif() # OpenAL if(APPLE) @@ -310,7 +316,7 @@ target_link_libraries(supertuxkart enet glew stkirrlicht - angelscript + ${Angelscript_LIBRARIES} ${CURL_LIBRARIES} ${OGGVORBIS_LIBRARIES} ${OPENAL_LIBRARY} diff --git a/cmake/FindAngelscript.cmake b/cmake/FindAngelscript.cmake new file mode 100644 index 000000000..09020c319 --- /dev/null +++ b/cmake/FindAngelscript.cmake @@ -0,0 +1,33 @@ +# - Try to find angelscript +# Once done this will define +# +# ANGELSCRIPT_FOUND - system has angelscript +# Angelscript_INCLUDE_DIRS - the angelscript include directory +# Angelscript_LIBRARIES - the libraries needed to use angelscript +# + +FIND_PATH(Angelscript_INCLUDE_DIRS angelscript.h + PATHS + /usr/local + /usr + PATH_SUFFIXES include + ) + +FIND_LIBRARY(Angelscript_LIBRARY + NAMES angelscript + PATHS + /usr/local + /usr + PATH_SUFFIXES lib + ) + +# handle the QUIETLY and REQUIRED arguments and set ANGELSCRIPT_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Angelscript DEFAULT_MSG Angelscript_LIBRARY Angelscript_INCLUDE_DIRS) + +IF (ANGELSCRIPT_FOUND) + SET(Angelscript_LIBRARIES ${Angelscript_LIBRARY}) +ENDIF (ANGELSCRIPT_FOUND) + +MARK_AS_ADVANCED(Angelscript_LIBRARY Angelscript_LIBRARIES Angelscript_INCLUDE_DIRS) From 8113ea128b2aada24b5cab0f7617d8ed6539e6ee Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Wed, 20 May 2015 20:17:00 -0400 Subject: [PATCH 37/44] Minor scripting work --- src/scriptengine/script_engine.cpp | 1 - src/scriptengine/script_utils.cpp | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 5542bbba2..6601d0072 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -39,7 +39,6 @@ using namespace Scripting; namespace Scripting { const char* MODULE_ID_MAIN_SCRIPT_FILE = "main"; - const char* MODULE_ID_EVAL = "eval"; void AngelScript_ErrorCallback (const asSMessageInfo *msg, void *param) { diff --git a/src/scriptengine/script_utils.cpp b/src/scriptengine/script_utils.cpp index 1768d9c20..5ab527efa 100644 --- a/src/scriptengine/script_utils.cpp +++ b/src/scriptengine/script_utils.cpp @@ -102,10 +102,8 @@ namespace Scripting } /** Runs the script specified by the given string */ - // TODO: type arguments - void runScript(asIScriptGeneric *gen) + void runScript(const std::string* str) { - std::string *str = (std::string*)gen->GetArgAddress(0); ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); script_engine->runFunction(*str); } @@ -163,7 +161,7 @@ namespace Scripting r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues3), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues4), asCALL_CDECL); assert(r >= 0); - r = engine->RegisterGlobalFunction("void runScript(string &in)", asFUNCTION(runScript), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("void runScript(string &in)", asFUNCTION(runScript), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void logInfo(const string &in)", asFUNCTION(logInfo), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void logWarning(const string &in)", asFUNCTION(logWarning), asCALL_CDECL); assert(r >= 0); From 0a3daf9484c55fa5dfa5053593d24341b04824eb Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Thu, 21 May 2015 20:26:28 -0400 Subject: [PATCH 38/44] Scripting work : the tutorial is now completely scripting-powered, nothing hardcoded anymore --- src/scriptengine/script_track.cpp | 9 ++++++++- src/tracks/track_object_presentation.cpp | 6 ------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index 921e5f440..b99589ac6 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -101,6 +101,12 @@ namespace Scripting tobj->setID(*triggerID); World::getWorld()->getTrack()->getTrackObjectManager()->insertObject(tobj); } + + /** Exits the race to the main menu */ + void exitRace() + { + World::getWorld()->scheduleExitRace(); + } } /** \cond DOXYGEN_IGNORE */ @@ -245,7 +251,8 @@ namespace Scripting r = engine->RegisterGlobalFunction("void createTrigger(const string &in, const Vec3 &in, float distance)", asFUNCTION(createTrigger), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0); - + r = engine->RegisterGlobalFunction("void exitRace()", asFUNCTION(exitRace), asCALL_CDECL); assert(r >= 0); + // TrackObject r = engine->RegisterObjectMethod("TrackObject", "void setEnable(bool status)", asMETHOD(TrackObject, setEnable), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("TrackObject", "SoundEmitter@ getSoundEmitter()", asMETHOD(TrackObject, getSoundEmitter), asCALL_THISCALL); assert(r >= 0); diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index cf4be74ef..35bb110a1 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -940,12 +940,6 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) _("Complete all challenges to unlock the big door!"), true); } } - else if (m_action == "tutorial_exit") - { - // TODO: move to scripting - World::getWorld()->scheduleExitRace(); - return; - } else { Scripting::ScriptEngine* script_engine = From 4c02204eff726e104d8029cd8fb0fe62ca44f618 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 24 May 2015 18:34:01 -0400 Subject: [PATCH 39/44] Start porting the overworld to scripting --- src/scriptengine/script_challenges.cpp | 76 ++++++++++++++++++++++++ src/scriptengine/script_challenges.hpp | 35 +++++++++++ src/scriptengine/script_engine.cpp | 2 + src/scriptengine/script_track.cpp | 7 +++ src/scriptengine/script_track.hpp | 2 - src/tracks/track_object_presentation.cpp | 48 +++------------ 6 files changed, 129 insertions(+), 41 deletions(-) create mode 100644 src/scriptengine/script_challenges.cpp create mode 100644 src/scriptengine/script_challenges.hpp diff --git a/src/scriptengine/script_challenges.cpp b/src/scriptengine/script_challenges.cpp new file mode 100644 index 000000000..3c619a318 --- /dev/null +++ b/src/scriptengine/script_challenges.cpp @@ -0,0 +1,76 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014-2015 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "script_track.hpp" + +#include "animations/three_d_animation.hpp" +#include "input/device_manager.hpp" +#include "input/input_device.hpp" +#include "input/input_manager.hpp" +#include "modes/world.hpp" +#include "states_screens/dialogs/tutorial_message_dialog.hpp" +#include "tracks/track.hpp" +#include "tracks/track_object.hpp" +#include "tracks/track_object_manager.hpp" + +#include +#include + +/** \cond DOXYGEN_IGNORE */ +namespace Scripting +{ + /** \endcond */ + namespace Challenges + { + /** \addtogroup Scripting + * @{ + */ + /** \addtogroup Scripting_Challenges Challenges + * @{ + */ + + int getCompletedChallengesCount() + { + ::Track* track = World::getWorld()->getTrack(); + return track->getNumOfCompletedChallenges(); + } + + int getChallengeCount() + { + ::Track* track = World::getWorld()->getTrack(); + return track->getChallengeList().size(); + } + + /** @}*/ + /** @}*/ + + void registerScriptFunctions(asIScriptEngine *engine) + { + int r; // of type asERetCodes + + engine->SetDefaultNamespace("Challenges"); + + r = engine->RegisterGlobalFunction("int getCompletedChallengesCount()", asFUNCTION(getCompletedChallengesCount), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("int getChallengeCount()", asFUNCTION(getChallengeCount), asCALL_CDECL); assert(r >= 0); + } + +/** \cond DOXYGEN_IGNORE */ + } +} +/** \endcond */ + diff --git a/src/scriptengine/script_challenges.hpp b/src/scriptengine/script_challenges.hpp new file mode 100644 index 000000000..6f872b0aa --- /dev/null +++ b/src/scriptengine/script_challenges.hpp @@ -0,0 +1,35 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014-2015 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_SCRIPT_CHALLENGES_HPP +#define HEADER_SCRIPT_CHALLENGES_HPP + +#include + +#include + +namespace Scripting +{ + namespace Challenges + { + //script engine functions + void registerScriptFunctions(asIScriptEngine *engine); + } + +} +#endif diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 6601d0072..250204d5e 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -21,6 +21,7 @@ #include "io/file_manager.hpp" #include "karts/kart.hpp" #include "modes/world.hpp" +#include "scriptengine/script_challenges.hpp" #include "scriptengine/script_engine.hpp" #include "scriptengine/script_gui.hpp" #include "scriptengine/script_track.hpp" @@ -335,6 +336,7 @@ namespace Scripting RegisterVec3(engine); //register Vec3 Scripting::Track::registerScriptFunctions(m_engine); + Scripting::Challenges::registerScriptFunctions(m_engine); Scripting::Kart::registerScriptFunctions(m_engine); Scripting::Physics::registerScriptFunctions(m_engine); Scripting::Utils::registerScriptFunctions(m_engine); diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index b99589ac6..8f69ec709 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -24,6 +24,7 @@ #include "input/input_manager.hpp" #include "modes/world.hpp" #include "states_screens/dialogs/tutorial_message_dialog.hpp" +#include "states_screens/dialogs/race_paused_dialog.hpp" #include "tracks/track.hpp" #include "tracks/track_object.hpp" #include "tracks/track_object_manager.hpp" @@ -107,6 +108,11 @@ namespace Scripting { World::getWorld()->scheduleExitRace(); } + + void pauseRace() + { + new RacePausedDialog(0.8f, 0.6f); + } } /** \cond DOXYGEN_IGNORE */ @@ -252,6 +258,7 @@ namespace Scripting asFUNCTION(createTrigger), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void exitRace()", asFUNCTION(exitRace), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void pauseRace()", asFUNCTION(pauseRace), asCALL_CDECL); assert(r >= 0); // TrackObject r = engine->RegisterObjectMethod("TrackObject", "void setEnable(bool status)", asMETHOD(TrackObject, setEnable), asCALL_THISCALL); assert(r >= 0); diff --git a/src/scriptengine/script_track.hpp b/src/scriptengine/script_track.hpp index 4f33d4fe8..69824b7d1 100644 --- a/src/scriptengine/script_track.hpp +++ b/src/scriptengine/script_track.hpp @@ -29,8 +29,6 @@ namespace Scripting { //script engine functions void registerScriptFunctions(asIScriptEngine *engine); - asIScriptFunction* - registerScriptCallbacks(asIScriptEngine *engine , std::string scriptName); } } diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 35bb110a1..3fcbeee1f 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -38,7 +38,6 @@ #include "karts/abstract_kart.hpp" #include "modes/world.hpp" #include "scriptengine/script_engine.hpp" -#include "states_screens/dialogs/race_paused_dialog.hpp" #include "states_screens/dialogs/tutorial_message_dialog.hpp" #include "tracks/model_definition_loader.hpp" #include "tracks/track.hpp" @@ -914,42 +913,13 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) { if (!m_action_active) return; - // TODO: replace all of these hardcoded actions with scripting - - if (m_action == "garage") - { - m_action_active = false; - - new RacePausedDialog(0.8f, 0.6f); - //dynamic_cast(World::getWorld())->scheduleSelectKart(); - } - //action trigger near big doors in the overword to notify players that - // they'll open once they finish all the challenges - else if (m_action == "big_door") - { - m_action_active = false; - - Track* m_track = World::getWorld()->getTrack(); - unsigned int unlocked_challenges = m_track->getNumOfCompletedChallenges(); - std::vector m_challenges = m_track->getChallengeList(); - - // allow ONE unsolved challenge : the last one - if (unlocked_challenges < m_challenges.size() - 1) - { - new TutorialMessageDialog( - _("Complete all challenges to unlock the big door!"), true); - } - } - else - { - Scripting::ScriptEngine* script_engine = - World::getWorld()->getScriptEngine(); - m_action_active = false; - int idKart = 0; - Camera* camera = Camera::getActiveCamera(); - if (camera != NULL && camera->getKart() != NULL) - idKart = camera->getKart()->getWorldKartId(); - script_engine->runFunction("void " + m_action + "(int)", - [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, idKart); }); - } + Scripting::ScriptEngine* script_engine = + World::getWorld()->getScriptEngine(); + m_action_active = false; // TODO: allow auto re-activating? + int idKart = 0; + Camera* camera = Camera::getActiveCamera(); + if (camera != NULL && camera->getKart() != NULL) + idKart = camera->getKart()->getWorldKartId(); + script_engine->runFunction("void " + m_action + "(int)", + [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, idKart); }); } // onTriggerItemApproached From 1ada542fa5a74da7a6020bb03677e9299eb1a86c Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 24 May 2015 19:15:50 -0400 Subject: [PATCH 40/44] Continue porting overworld to scripting --- src/scriptengine/script_engine.cpp | 23 +++++++++++++++++---- src/scriptengine/script_engine.hpp | 6 +++++- src/tracks/track.cpp | 32 ++++++++++++++++-------------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 250204d5e..a851dbe67 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -177,7 +177,17 @@ namespace Scripting void ScriptEngine::runFunction(std::string function_name) { std::function callback; - runFunction(function_name, callback); + std::function get_return_value; + runFunction(function_name, callback, get_return_value); + } + + //----------------------------------------------------------------------------- + + void ScriptEngine::runFunction(std::string function_name, + std::function callback) + { + std::function get_return_value; + runFunction(function_name, callback, get_return_value); } //----------------------------------------------------------------------------- @@ -185,7 +195,9 @@ namespace Scripting /** runs the specified script * \param string scriptName = name of script to run */ - void ScriptEngine::runFunction(std::string function_name, std::function callback) + void ScriptEngine::runFunction(std::string function_name, + std::function callback, + std::function get_return_value) { int r; //int for error checking @@ -199,11 +211,11 @@ namespace Scripting if (cached_script == m_loaded_files.end()) { // Compile the script code - Log::debug("Scripting", "Compiling script '%s' (was not in cache)", script_filename.c_str()); + Log::info("Scripting", "Checking for script file '%s'", script_filename.c_str()); r = compileScript(m_engine, script_filename); if (r < 0) { - Log::debug("Scripting", "Script '%s' is not available", script_filename.c_str()); + Log::info("Scripting", "Script '%s' is not available", script_filename.c_str()); m_loaded_files[script_filename] = false; m_functions_cache[function_name] = NULL; // remember that this script is unavailable return; @@ -306,6 +318,9 @@ namespace Scripting // Retrieve the return value from the context here (for scripts that return values) // returnValue = ctx->getReturnType(); for example //float returnValue = ctx->GetReturnFloat(); + + if (get_return_value) + get_return_value(ctx); } // We must release the contexts when no longer using them diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index 5a7e01e88..d3b304178 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -53,7 +53,11 @@ namespace Scripting ~ScriptEngine(); void runFunction(std::string function_name); - void runFunction(std::string function_name, std::function callback); + void runFunction(std::string function_name, + std::function callback); + void runFunction(std::string function_name, + std::function callback, + std::function get_return_value); void evalScript(std::string script_fragment); void cleanupCache(); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index b5d447bb1..42b9c87b8 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -1116,6 +1116,8 @@ bool Track::loadMainTrack(const XMLNode &root) // some static meshes are conditional std::string condition; n->get("if", &condition); + + // TODO: convert "if" and "ifnot" to scripting. if (condition == "splatting") { if (!irr_driver->supportsSplatting()) continue; @@ -1193,15 +1195,15 @@ bool Track::loadMainTrack(const XMLNode &root) if (!shown) continue; } - else if (condition == "allchallenges") - { - // allow ONE unsolved challenge : the last one - if (getNumOfCompletedChallenges() < m_challenges.size() - 1) - continue; - } else if (condition.size() > 0) { - Log::error("track", "Unknown condition <%s>\n", condition.c_str()); + unsigned char result = -1; + Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); + std::function null_callback; + script_engine->runFunction("bool " + condition + "()", null_callback, + [&](asIScriptContext* ctx) { result = ctx->GetReturnByte(); }); + if (result == 0) + continue; } std::string neg_condition; @@ -1210,18 +1212,18 @@ bool Track::loadMainTrack(const XMLNode &root) { if (irr_driver->supportsSplatting()) continue; } - else if (neg_condition == "allchallenges") - { - // allow ONE unsolved challenge : the last one - if (getNumOfCompletedChallenges() >= m_challenges.size() - 1) - continue; - } else if (neg_condition.size() > 0) { - Log::error("track", "Unknown condition <%s>\n", - neg_condition.c_str()); + unsigned char result = -1; + Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); + std::function null_callback; + script_engine->runFunction("bool " + neg_condition + "()", null_callback, + [&](asIScriptContext* ctx) { result = ctx->GetReturnByte(); }); + if (result != 0) + continue; } + bool tangent = false; n->get("tangents", &tangent); From 40ea7f94f0e46da406bbad00db508c7659415df0 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Thu, 28 May 2015 19:57:36 -0400 Subject: [PATCH 41/44] Scripting work : improve object-kart collision callbacks --- src/physics/physical_object.cpp | 2 ++ src/physics/physical_object.hpp | 10 ++++++++++ src/physics/physics.cpp | 20 +++++++++++--------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp index 7ed9c7d49..67dd705a5 100644 --- a/src/physics/physical_object.cpp +++ b/src/physics/physical_object.cpp @@ -63,6 +63,7 @@ PhysicalObject::Settings::Settings(const XMLNode &xml_node) xml_node.get("reset", &m_crash_reset ); xml_node.get("explode", &m_knock_kart ); xml_node.get("flatten", &m_flatten_kart); + xml_node.get("on-kart-collision", &m_on_kart_collision); m_reset_when_too_low = xml_node.get("reset-when-below", &m_reset_height) == 1; @@ -140,6 +141,7 @@ PhysicalObject::PhysicalObject(bool is_dynamic, m_flatten_kart = settings.m_flatten_kart; m_reset_when_too_low = settings.m_reset_when_too_low; m_reset_height = settings.m_reset_height; + m_on_kart_collision = settings.m_on_kart_collision; m_init_pos.setIdentity(); Vec3 radHpr(m_init_hpr); diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index 7cebcec6d..ff5f3711d 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -67,6 +67,10 @@ public: /** If the item is below that height, it is reset (when * m_reset_when_too_low is true). */ float m_reset_height; + /** If non-empty, the name of the scripting function to call + * when a kart collides with this object + */ + std::string m_on_kart_collision; private: void init(); public: @@ -140,6 +144,10 @@ private: /** If m_reset_when_too_low this object is set back to its start * position if its height is below this value. */ float m_reset_height; + /** If non-empty, the name of the scripting function to call + * when a kart collides with this object + */ + std::string m_on_kart_collision; /** If this body is a bullet dynamic body, i.e. affected by physics * or not (static (not moving) or kinematic (animated outside @@ -194,6 +202,8 @@ public: /** Add body to dynamic world */ void addBody(); // ------------------------------------------------------------------------ + const std::string& getOnKartCollisionFunction() const { return m_on_kart_collision; } + // ------------------------------------------------------------------------ // Methods usable by scripts /** diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index d399a1727..1c2a04a9a 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -186,15 +186,17 @@ void Physics::update(float dt) Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); AbstractKart *kart = p->getUserPointer(1)->getPointerKart(); int kartId = kart->getWorldKartId(); - std::string obj_id = p->getUserPointer(0)->getPointerPhysicalObject()->getID(); - - // TODO: pass obj_id as arguent - script_engine->runFunction("void onKartObjectCollision(int)", - [=](asIScriptContext* ctx) { - ctx->SetArgDWord(0, kartId); - }); - PhysicalObject *obj = p->getUserPointer(0) - ->getPointerPhysicalObject(); + PhysicalObject* obj = p->getUserPointer(0)->getPointerPhysicalObject(); + std::string obj_id = obj->getID(); + std::string scripting_function = obj->getOnKartCollisionFunction(); + if (scripting_function.size() > 0) + { + script_engine->runFunction("void " + scripting_function + "(int, const string)", + [&](asIScriptContext* ctx) { + ctx->SetArgDWord(0, kartId); + ctx->SetArgObject(1, &obj_id); + }); + } if (obj->isCrashReset()) { new RescueAnimation(kart); From 8ef16c146e8262890a78f81a2aef6ae93bafe5ee Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 31 May 2015 18:09:51 -0400 Subject: [PATCH 42/44] Scripting work : improve flyable vs object collision callbacks. More work to do still. --- src/physics/physics.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 1c2a04a9a..1563b77bb 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -257,15 +257,17 @@ void Physics::update(float dt) // Projectile hits physical object // ------------------------------- Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine(); - // TODO: pass arguments - //Scripting::Physics::setColl0ision( - // p->getUserPointer(1)->getPointerPhysicalObject()->getID(), - // "item" - //); - script_engine->runFunction("void onItemObjectCollision()"); - p->getUserPointer(0)->getPointerFlyable() - ->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject()); + Flyable* flyable = p->getUserPointer(0)->getPointerFlyable(); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); + std::string obj_id = obj->getID(); + // TODO: attach this callback directly to a specific track object + script_engine->runFunction("void onItemObjectCollision(int, int, const string)", + [&](asIScriptContext* ctx) { + ctx->SetArgDWord(0, (int)flyable->getType()); + ctx->SetArgDWord(1, flyable->getOwnerId()); + ctx->SetArgObject(2, &obj_id); + }); + flyable->hit(NULL, obj); if (obj->isSoccerBall() && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) From 501dd14bb69cdb8b3772eba82d87d6ac2dee4235 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 31 May 2015 18:20:52 -0400 Subject: [PATCH 43/44] More scripting work on item-object collisions --- src/physics/physical_object.cpp | 2 ++ src/physics/physical_object.hpp | 11 ++++++++++- src/physics/physics.cpp | 17 ++++++++++------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp index 67dd705a5..181167cfb 100644 --- a/src/physics/physical_object.cpp +++ b/src/physics/physical_object.cpp @@ -64,6 +64,7 @@ PhysicalObject::Settings::Settings(const XMLNode &xml_node) xml_node.get("explode", &m_knock_kart ); xml_node.get("flatten", &m_flatten_kart); xml_node.get("on-kart-collision", &m_on_kart_collision); + xml_node.get("on-item-collision", &m_on_item_collision); m_reset_when_too_low = xml_node.get("reset-when-below", &m_reset_height) == 1; @@ -142,6 +143,7 @@ PhysicalObject::PhysicalObject(bool is_dynamic, m_reset_when_too_low = settings.m_reset_when_too_low; m_reset_height = settings.m_reset_height; m_on_kart_collision = settings.m_on_kart_collision; + m_on_item_collision = settings.m_on_item_collision; m_init_pos.setIdentity(); Vec3 radHpr(m_init_hpr); diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index ff5f3711d..41a8b8823 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -71,6 +71,10 @@ public: * when a kart collides with this object */ std::string m_on_kart_collision; + /** If non-empty, the name of the scripting function to call + * when a (flyable) item collides with this object + */ + std::string m_on_item_collision; private: void init(); public: @@ -148,7 +152,10 @@ private: * when a kart collides with this object */ std::string m_on_kart_collision; - + /** If non-empty, the name of the scripting function to call + * when a (flyable) item collides with this object + */ + std::string m_on_item_collision; /** If this body is a bullet dynamic body, i.e. affected by physics * or not (static (not moving) or kinematic (animated outside * of physics). */ @@ -204,6 +211,8 @@ public: // ------------------------------------------------------------------------ const std::string& getOnKartCollisionFunction() const { return m_on_kart_collision; } // ------------------------------------------------------------------------ + const std::string& getOnItemCollisionFunction() const { return m_on_item_collision; } + // ------------------------------------------------------------------------ // Methods usable by scripts /** diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 1563b77bb..ee2549e12 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -260,13 +260,16 @@ void Physics::update(float dt) Flyable* flyable = p->getUserPointer(0)->getPointerFlyable(); PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); std::string obj_id = obj->getID(); - // TODO: attach this callback directly to a specific track object - script_engine->runFunction("void onItemObjectCollision(int, int, const string)", - [&](asIScriptContext* ctx) { - ctx->SetArgDWord(0, (int)flyable->getType()); - ctx->SetArgDWord(1, flyable->getOwnerId()); - ctx->SetArgObject(2, &obj_id); - }); + std::string scripting_function = obj->getOnItemCollisionFunction(); + if (scripting_function.size() > 0) + { + script_engine->runFunction("void " + scripting_function + "(int, int, const string)", + [&](asIScriptContext* ctx) { + ctx->SetArgDWord(0, (int)flyable->getType()); + ctx->SetArgDWord(1, flyable->getOwnerId()); + ctx->SetArgObject(2, &obj_id); + }); + } flyable->hit(NULL, obj); if (obj->isSoccerBall() && From b3d97241d77ea63be4b765447793c6f48cf04b55 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 31 May 2015 18:39:40 -0400 Subject: [PATCH 44/44] Scripting : cleanup, add poweruptype enum --- src/scriptengine/script_engine.cpp | 3 +++ src/scriptengine/script_engine.hpp | 18 ------------------ src/scriptengine/script_kart.cpp | 17 +++++++++++++++++ src/scriptengine/script_kart.hpp | 5 ++--- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index a851dbe67..dfd215edb 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -22,8 +22,10 @@ #include "karts/kart.hpp" #include "modes/world.hpp" #include "scriptengine/script_challenges.hpp" +#include "scriptengine/script_kart.hpp" #include "scriptengine/script_engine.hpp" #include "scriptengine/script_gui.hpp" +#include "scriptengine/script_physics.hpp" #include "scriptengine/script_track.hpp" #include "scriptengine/script_utils.hpp" #include "scriptengine/scriptstdstring.hpp" @@ -353,6 +355,7 @@ namespace Scripting Scripting::Track::registerScriptFunctions(m_engine); Scripting::Challenges::registerScriptFunctions(m_engine); Scripting::Kart::registerScriptFunctions(m_engine); + Scripting::Kart::registerScriptEnums(m_engine); Scripting::Physics::registerScriptFunctions(m_engine); Scripting::Utils::registerScriptFunctions(m_engine); Scripting::GUI::registerScriptFunctions(m_engine); diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index d3b304178..961d16ecb 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -27,24 +27,6 @@ class TrackObjectPresentation; namespace Scripting { - - namespace Physics - { - void registerScriptFunctions(asIScriptEngine *engine); - } - - namespace Kart - { - void registerScriptFunctions(asIScriptEngine *engine); - } - - namespace Track - { - void registerScriptFunctions(asIScriptEngine *engine); - - void registerScriptEnums(asIScriptEngine *engine); - } - class ScriptEngine { public: diff --git a/src/scriptengine/script_kart.cpp b/src/scriptengine/script_kart.cpp index a13b05039..9e827c65a 100644 --- a/src/scriptengine/script_kart.cpp +++ b/src/scriptengine/script_kart.cpp @@ -123,6 +123,23 @@ namespace Scripting //r = engine->RegisterGlobalFunction("void jumpTo(int id, float x, float y)", asFUNCTION(jumpTo), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("Vec3 getLocation(int id)", asFUNCTION(getLocation), asCALL_CDECL); assert(r >= 0); } + + void registerScriptEnums(asIScriptEngine *engine) + { + // TODO: document enum in doxygen-generated scripting docs + engine->SetDefaultNamespace("Kart"); + engine->RegisterEnum("PowerupType"); + engine->RegisterEnumValue("PowerupType", "ANVIL", PowerupManager::PowerupType::POWERUP_ANVIL); + engine->RegisterEnumValue("PowerupType", "BOWLING", PowerupManager::PowerupType::POWERUP_BOWLING); + engine->RegisterEnumValue("PowerupType", "BUBBLEGUM", PowerupManager::PowerupType::POWERUP_BUBBLEGUM); + engine->RegisterEnumValue("PowerupType", "CAKE", PowerupManager::PowerupType::POWERUP_CAKE); + engine->RegisterEnumValue("PowerupType", "PARACHUTE", PowerupManager::PowerupType::POWERUP_PARACHUTE); + engine->RegisterEnumValue("PowerupType", "PLUNGER", PowerupManager::PowerupType::POWERUP_PLUNGER); + engine->RegisterEnumValue("PowerupType", "RUBBERBALL", PowerupManager::PowerupType::POWERUP_RUBBERBALL); + engine->RegisterEnumValue("PowerupType", "SWATTER", PowerupManager::PowerupType::POWERUP_SWATTER); + engine->RegisterEnumValue("PowerupType", "SWITCH", PowerupManager::PowerupType::POWERUP_SWITCH); + engine->RegisterEnumValue("PowerupType", "ZIPPER", PowerupManager::PowerupType::POWERUP_ZIPPER); + } } /** \cond DOXYGEN_IGNORE */ diff --git a/src/scriptengine/script_kart.hpp b/src/scriptengine/script_kart.hpp index 5ddca0e09..f2fbad653 100644 --- a/src/scriptengine/script_kart.hpp +++ b/src/scriptengine/script_kart.hpp @@ -23,13 +23,12 @@ namespace Scripting { - namespace Kart { - void registerScriptFunctions(asIScriptEngine *engine); - + void registerScriptEnums(asIScriptEngine *engine); } } + #endif

o8UqSyO)CRclf`)%) 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 From 6106ed3ec6b3308ed9cc509d3df242dc996f090d Mon Sep 17 00:00:00 2001 From: Karl Ove Hufthammer Date: Sun, 10 May 2015 12:09:01 +0200 Subject: [PATCH 28/44] =?UTF-8?q?Don=E2=80=99t=20sort=20strings=20alphabet?= =?UTF-8?q?ically=20(makes=20the=20translators=E2=80=99=20work=20much=20ea?= =?UTF-8?q?sier).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/po/update_pot.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/po/update_pot.sh b/data/po/update_pot.sh index 848ee1ede..e1e75a72d 100755 --- a/data/po/update_pot.sh +++ b/data/po/update_pot.sh @@ -37,13 +37,13 @@ echo "---------------------------" echo " Generating .pot file..." # XML Files -xgettext -d supertuxkart -s --keyword=_ --add-comments="I18N:" \ +xgettext -d supertuxkart --keyword=_ --add-comments="I18N:" \ -p ./data/po -o supertuxkart.pot \ --no-location --from-code=UTF-8 ./data/po/gui_strings.h \ --package-name=supertuxkart # C++ Files -xgettext -j -d supertuxkart -s --keyword=_ --keyword=N_ --keyword=_LTR \ +xgettext -j -d supertuxkart --keyword=_ --keyword=N_ --keyword=_LTR \ --keyword=_C:1c,2 --keyword=_P:1,2 \ --keyword=_CP:1c,2,3 --add-comments="I18N:" \ -p ./data/po -o supertuxkart.pot $CPP_FILE_LIST \ From 0ea991adee4ce24401310e9ae6d16506de4cc3ce Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Tue, 12 May 2015 18:31:08 -0400 Subject: [PATCH 29/44] More work for scripting compatibility on Linux/GCC --- src/scriptengine/scriptvec3.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/scriptengine/scriptvec3.cpp b/src/scriptengine/scriptvec3.cpp index b67c82a39..c5c96a0de 100644 --- a/src/scriptengine/scriptvec3.cpp +++ b/src/scriptengine/scriptvec3.cpp @@ -54,6 +54,10 @@ namespace Scripting std::cout << script_vec3->getX() << "," << script_vec3->getY() << "," << script_vec3->getZ() << std::endl; } + float getX(SimpleVec3* v) { return v->getX(); } + float getY(SimpleVec3* v) { return v->getY(); } + float getZ(SimpleVec3* v) { return v->getZ(); } + void RegisterVec3(asIScriptEngine *engine) { int r; @@ -64,8 +68,8 @@ namespace Scripting r = engine->RegisterObjectMethod("Vec3", "Vec3 &opAssign(const Vec3 &in)", asMETHODPR(SimpleVec3, operator =, (const SimpleVec3&), SimpleVec3&), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTION(ConstructVector3FromFloats), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterGlobalFunction("void printVec3(Vec3 a)", asFUNCTION(printVec3), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterObjectMethod("Vec3", "float &getX()", asMETHOD(SimpleVec3, getX), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("Vec3", "float &getY()", asMETHOD(SimpleVec3, getY), asCALL_THISCALL); assert(r >= 0); - r = engine->RegisterObjectMethod("Vec3", "float &getZ()", asMETHOD(SimpleVec3, getZ), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float getX()", asFUNCTION(getX), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float getY()", asFUNCTION(getY), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Vec3", "float getZ()", asFUNCTION(getZ), asCALL_CDECL_OBJLAST); assert(r >= 0); } } From 7d66dfec0e41aa4e98a77e8fc4e161234574b545 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Tue, 12 May 2015 19:49:14 -0400 Subject: [PATCH 30/44] Make scripting work on OS X, hoping this doesn't break windows or linux --- src/scriptengine/scriptvec3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scriptengine/scriptvec3.cpp b/src/scriptengine/scriptvec3.cpp index c5c96a0de..29e4d1818 100644 --- a/src/scriptengine/scriptvec3.cpp +++ b/src/scriptengine/scriptvec3.cpp @@ -61,7 +61,7 @@ namespace Scripting void RegisterVec3(asIScriptEngine *engine) { int r; - r = engine->RegisterObjectType("Vec3", sizeof(SimpleVec3), asOBJ_VALUE | asOBJ_APP_CLASS_ALLFLOATS | asGetTypeTraits()); assert(r >= 0); + r = engine->RegisterObjectType("Vec3", sizeof(SimpleVec3), asOBJ_VALUE | asOBJ_APP_CLASS_ALLFLOATS | asOBJ_APP_CLASS_CDA); assert(r >= 0); // Register the behaviours r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Constructor), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectBehaviour("Vec3", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Destructor), asCALL_CDECL_OBJLAST); assert(r >= 0); From e23f854845d68d21d35b0bb52c86f10846cefdbe Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Thu, 14 May 2015 19:27:32 -0400 Subject: [PATCH 31/44] Scripting work --- src/scriptengine/script_track.cpp | 44 +++++++++++++------------------ src/scriptengine/script_track.hpp | 7 ----- 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index d1107deb8..e27fc64c7 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -61,52 +61,44 @@ namespace Scripting return World::getWorld()->getTrack()->getTrackObjectManager()->getTrackObject(*objID); } - // TODO: assign types for documentation - /** Generic disable method for track objects */ - void disableTrackObject(asIScriptGeneric *gen) + /** Hide/disable a track object */ + void disableTrackObject(std::string* objID) { - std::string *str = (std::string*)gen->GetArgAddress(0); - World::getWorld()->getTrack()->getTrackObjectManager()->disable(*str); + World::getWorld()->getTrack()->getTrackObjectManager()->disable(*objID); } - /** Generic enable method for track objects */ - void enableTrackObject(asIScriptGeneric *gen) + /** Show/enable a track objects */ + void enableTrackObject(std::string* objID) { - std::string *str = (std::string*)gen->GetArgAddress(0); - World::getWorld()->getTrack()->getTrackObjectManager()->enable(*str); + World::getWorld()->getTrack()->getTrackObjectManager()->enable(*objID); } /** Disables an action trigger of specified ID */ - void disableTrigger(asIScriptGeneric *gen) + void disableTrigger(std::string* triggerID) { - std::string *str = (std::string*)gen->GetArgAddress(0); - World::getWorld()->getTrack()->getTrackObjectManager()->disable(*str); + World::getWorld()->getTrack()->getTrackObjectManager()->disable(*triggerID); } /** Enables an action trigger of specified ID */ - void enableTrigger(asIScriptGeneric *gen) + void enableTrigger(std::string* triggerID) { - std::string *str = (std::string*)gen->GetArgAddress(0); - World::getWorld()->getTrack()->getTrackObjectManager()->enable(*str); + World::getWorld()->getTrack()->getTrackObjectManager()->enable(*triggerID); } /** Creates a trigger at the specified location */ - void createTrigger(asIScriptGeneric *gen) + void createTrigger(std::string* triggerID, Vec3* creation_loc, float distance) { - std::string *script_name = (std::string*)gen->GetArgAddress(0); - Vec3 *creation_loc = (Vec3*)gen->GetArgAddress(1); float x = creation_loc->getX(); float y = creation_loc->getY(); float z = creation_loc->getZ(); - float distance = gen->GetArgFloat(2); //triggering distance core::vector3df posi(x, y, z); core::vector3df hpr(0, 0, 0); core::vector3df scale(1.0f, 1.0f, 1.0f); TrackObjectPresentationActionTrigger* newtrigger = - new TrackObjectPresentationActionTrigger(posi, *script_name, distance); + new TrackObjectPresentationActionTrigger(posi, *triggerID, distance); TrackObject* tobj = new TrackObject(posi, hpr, scale, "none", newtrigger, false /* isDynamic */, NULL /* physics settings */); - tobj->setID(*script_name); + tobj->setID(*triggerID); World::getWorld()->getTrack()->getTrackObjectManager()->insertObject(tobj); } } @@ -246,11 +238,11 @@ namespace Scripting r = engine->RegisterObjectType("SoundEmitter", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); r = engine->RegisterObjectType("Animator", 0, asOBJ_REF | asOBJ_NOCOUNT); assert(r >= 0); - r = engine->RegisterGlobalFunction("void disableTrackObject(const string &in)", asFUNCTION(disableTrackObject), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void enableTrackObject(const string &in)", asFUNCTION(enableTrackObject), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void enableTrigger(const string &in)", asFUNCTION(enableTrigger), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void disableTrigger(const string &in)", asFUNCTION(disableTrigger), asCALL_GENERIC); assert(r >= 0); - r = engine->RegisterGlobalFunction("void createTrigger(const string &in,Vec3 &in, float distance)", + r = engine->RegisterGlobalFunction("void disableTrackObject(const string &in)", asFUNCTION(disableTrackObject), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void enableTrackObject(const string &in)", asFUNCTION(enableTrackObject), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void enableTrigger(const string &in)", asFUNCTION(enableTrigger), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void disableTrigger(const string &in)", asFUNCTION(disableTrigger), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void createTrigger(const string &in, const Vec3 &in, float distance)", asFUNCTION(createTrigger), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0); diff --git a/src/scriptengine/script_track.hpp b/src/scriptengine/script_track.hpp index 9b2b77c70..4f33d4fe8 100644 --- a/src/scriptengine/script_track.hpp +++ b/src/scriptengine/script_track.hpp @@ -31,13 +31,6 @@ namespace Scripting void registerScriptFunctions(asIScriptEngine *engine); asIScriptFunction* registerScriptCallbacks(asIScriptEngine *engine , std::string scriptName); - - //script-bound functions - void disableAnimation(asIScriptGeneric *gen); - void enableAnimation(asIScriptGeneric *gen); - void enableTrigger(asIScriptGeneric *gen); - void disableTrigger(asIScriptGeneric *gen); - void createTrigger(asIScriptGeneric *gen); } } From 080936f144bd5acc772d03ad488f9c1e38be6af9 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 17 May 2015 19:27:25 -0400 Subject: [PATCH 32/44] Work on scripting --- data/gui/scripting_console.stkgui | 17 + src/physics/physics.cpp | 6 +- src/scriptengine/script_engine.cpp | 574 ++++++++++-------- src/scriptengine/script_engine.hpp | 12 +- src/scriptengine/script_kart.cpp | 2 +- src/scriptengine/script_track.cpp | 4 +- src/scriptengine/script_utils.cpp | 2 +- .../dialogs/scripting_console.cpp | 86 +++ .../dialogs/scripting_console.hpp | 51 ++ src/tracks/track.cpp | 2 +- src/tracks/track_object_presentation.cpp | 2 +- src/utils/debug.cpp | 7 + 12 files changed, 495 insertions(+), 270 deletions(-) create mode 100644 data/gui/scripting_console.stkgui create mode 100644 src/states_screens/dialogs/scripting_console.cpp create mode 100644 src/states_screens/dialogs/scripting_console.hpp diff --git a/data/gui/scripting_console.stkgui b/data/gui/scripting_console.stkgui new file mode 100644 index 000000000..f900753a9 --- /dev/null +++ b/data/gui/scripting_console.stkgui @@ -0,0 +1,17 @@ + + +