diff --git a/lib/angelscript/include/angelscript.h b/lib/angelscript/include/angelscript.h index 25a0d7402..98d33c516 100644 --- a/lib/angelscript/include/angelscript.h +++ b/lib/angelscript/include/angelscript.h @@ -58,8 +58,8 @@ BEGIN_AS_NAMESPACE // AngelScript version -#define ANGELSCRIPT_VERSION 23000 -#define ANGELSCRIPT_VERSION_STRING "2.30.0 WIP" +#define ANGELSCRIPT_VERSION 23002 +#define ANGELSCRIPT_VERSION_STRING "2.30.2" // Data types @@ -543,7 +543,7 @@ struct asSMessageInfo extern "C" { // Engine - AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version); + AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version = ANGELSCRIPT_VERSION); AS_API const char *asGetLibraryVersion(); AS_API const char *asGetLibraryOptions(); @@ -585,20 +585,20 @@ template asUINT asGetTypeTraits() { #if defined(_MSC_VER) || defined(_LIBCPP_TYPE_TRAITS) || (__GNUC__ >= 5) - // MSVC & XCode/Clang, and gnuc 5+ + // MSVC, XCode/Clang, and gnuc 5+ // 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 + // gnuc 4.8 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 + // All other compilers and versions are assumed to use non C++11 compliant code until proven otherwise // 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 @@ -1556,8 +1556,8 @@ enum asEBCInstr asBC_POWdi = 197, asBC_POWi64 = 198, asBC_POWu64 = 199, - - asBC_MAXBYTECODE = 200, + asBC_Thiscall1 = 200, + asBC_MAXBYTECODE = 201, // Temporary tokens. Can't be output to the final program asBC_VarDecl = 251, @@ -1851,8 +1851,8 @@ const asSBCInfo asBCInfo[256] = asBCINFO(POWdi, wW_rW_rW_ARG, 0), asBCINFO(POWi64, wW_rW_rW_ARG, 0), asBCINFO(POWu64, wW_rW_rW_ARG, 0), + asBCINFO(Thiscall1, DW_ARG, -AS_PTR_SIZE-1), - asBCINFO_DUMMY(200), asBCINFO_DUMMY(201), asBCINFO_DUMMY(202), asBCINFO_DUMMY(203), diff --git a/lib/angelscript/projects/cmake/CMakeLists.txt b/lib/angelscript/projects/cmake/CMakeLists.txt index 9ee6fbf0c..e93971315 100644 --- a/lib/angelscript/projects/cmake/CMakeLists.txt +++ b/lib/angelscript/projects/cmake/CMakeLists.txt @@ -14,11 +14,15 @@ 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) +file(READ ../../include/angelscript.h ANGELSCRIPT_H) +string(REGEX MATCH "#define ANGELSCRIPT_VERSION_STRING \"([0-9]*).([0-9]*).([0-9]*)" ANGELSCRIPT_VERSION_REGEX ${ANGELSCRIPT_H}) +set(ANGELSCRIPT_VERSION_MAJOR ${CMAKE_MATCH_1}) +set(ANGELSCRIPT_VERSION_MINOR ${CMAKE_MATCH_2}) +set(ANGELSCRIPT_VERSION_PATCH ${CMAKE_MATCH_3}) set(PROJECT_VERSION ${ANGELSCRIPT_VERSION_MAJOR}.${ANGELSCRIPT_VERSION_MINOR}.${ANGELSCRIPT_VERSION_PATCH}) +message(STATUS "Configuring angelscript ${PROJECT_VERSION}") + find_package(Threads) set(ANGELSCRIPT_HEADERS @@ -102,12 +106,13 @@ if(MSVC AND CMAKE_CL_64) endif() endif() -if(ANDROID) +if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm") enable_language(ASM) if(CMAKE_ASM_COMPILER_WORKS) set(ANGELSCRIPT_SOURCE ${ANGELSCRIPT_SOURCE} ../../source/as_callfunc_arm.cpp ../../source/as_callfunc_arm_gcc.S) + set_property(SOURCE ../../source/as_callfunc_arm_gcc.S APPEND PROPERTY COMPILE_FLAGS " -Wa,-mimplicit-it=always") else() - message(FATAL ERROR "Android target requires a working assembler") + message(FATAL ERROR "ARM target requires a working assembler") endif() endif() @@ -156,4 +161,5 @@ endif() #set(RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../bin) +#add_subdirectory(../../../samples/game/projects/cmake/ ./game) diff --git a/lib/angelscript/source/as_builder.cpp b/lib/angelscript/source/as_builder.cpp index a7c630411..2ff191c3a 100644 --- a/lib/angelscript/source/as_builder.cpp +++ b/lib/angelscript/source/as_builder.cpp @@ -363,9 +363,35 @@ int asCBuilder::CompileGlobalVar(const char *sectionName, const char *code, int CompileGlobalVariables(); + // It is possible that the global variable initialization included anonymous functions that must be compiled too + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCCompiler compiler(engine); + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + int r = compiler.CompileFunction(this, functions[n]->script, func->parameterNames, functions[n]->node, func, 0); + if( r < 0 ) + break; + } + if( numWarnings > 0 && engine->ep.compilerWarnings == 2 ) WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0); + // None of the functions should be added to the module if any error occurred, + // or it was requested that the functions wouldn't be added to the scope + if( numErrors > 0 ) + { + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + if( module->globalFunctions.GetIndex(func) >= 0 ) + { + module->globalFunctions.Erase(module->globalFunctions.GetIndex(func)); + module->scriptFunctions.RemoveValue(func); + func->ReleaseInternal(); + } + } + } + if( numErrors > 0 ) { // Remove the variable from the module, if it was registered @@ -549,22 +575,38 @@ int asCBuilder::CompileFunction(const char *sectionName, const char *code, int l funcDesc->paramNames = func->parameterNames; funcDesc->isExistingShared = false; - asCCompiler compiler(engine); - compiler.CompileFunction(this, functions[0]->script, func->parameterNames, functions[0]->node, func, 0); + // This must be done in a loop, as it is possible that additional functions get declared as lambda's in the code + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCCompiler compiler(engine); + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + int r = compiler.CompileFunction(this, functions[n]->script, func->parameterNames, functions[n]->node, func, 0); + if( r < 0 ) + break; + } if( numWarnings > 0 && engine->ep.compilerWarnings == 2 ) WriteError(TXT_WARNINGS_TREATED_AS_ERROR, 0, 0); + // None of the functions should be added to the module if any error occurred, + // or it was requested that the functions wouldn't be added to the scope + if( !(compileFlags & asCOMP_ADD_TO_MODULE) || numErrors > 0 ) + { + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + if( module->globalFunctions.GetIndex(func) >= 0 ) + { + module->globalFunctions.Erase(module->globalFunctions.GetIndex(func)); + module->scriptFunctions.RemoveValue(func); + func->ReleaseInternal(); + } + } + } + if( numErrors > 0 ) { - // If the function was added to the module then remove it again - if( compileFlags & asCOMP_ADD_TO_MODULE ) - { - module->globalFunctions.Erase(module->globalFunctions.GetIndex(func)); - module->scriptFunctions.RemoveValue(func); - func->ReleaseInternal(); - } - + // Release the function pointer that would otherwise be returned if no errors occured func->ReleaseInternal(); return asERROR; @@ -965,15 +1007,20 @@ int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &nam if( r < 0 ) return asINVALID_DECLARATION; - // Get data type and property name + // Get data type asCScriptNode *dataType = parser.GetScriptNode()->firstChild; - asCScriptNode *nameNode = dataType->next; + // Check if the property is declared 'by reference' + bool isReference = (dataType->next->tokenType == ttAmp); + + // Get the name of the property + asCScriptNode *nameNode = isReference ? dataType->next->next : dataType->next; // If an object property is registered, then use the // object's namespace, otherwise use the specified namespace type = CreateDataTypeFromNode(dataType, &source, dt ? dt->GetObjectType()->nameSpace : ns); name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength); + type.MakeReference(isReference); // Validate that the type really can be a registered property // We cannot use CanBeInstantiated, as it is allowed to register @@ -1458,9 +1505,9 @@ 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. + // TODO: 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 ); @@ -1516,6 +1563,7 @@ void asCBuilder::CompleteFuncDef(sFuncDef *funcDef) asCScriptFunction *func = module->funcDefs[funcDef->idx]; asASSERT( func ); + // TODO: It should be possible to declare funcdef as shared. In this case a compiler error will be given if any of the types it uses are not shared 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 @@ -1523,27 +1571,47 @@ void asCBuilder::CompleteFuncDef(sFuncDef *funcDef) if( defaultArgs[n] ) asDELETE(defaultArgs[n], asCString); - // TODO: Should we force the use of 'shared' for this check to be done? - // Check if there is another identical funcdef from another module and if so reuse that instead - for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) + // All funcdefs are shared, unless one of the parameter types or return type is not shared + isShared = true; + if( func->returnType.GetObjectType() && !func->returnType.GetObjectType()->IsShared() ) + isShared = false; + if( func->returnType.GetFuncDef() && !func->returnType.GetFuncDef()->IsShared() ) + isShared = false; + for( asUINT n = 0; isShared && n < func->parameterTypes.GetLength(); n++ ) { - asCScriptFunction *f2 = engine->funcDefs[n]; - if( f2 == 0 || func == f2 ) - continue; + if( func->parameterTypes[n].GetObjectType() && !func->parameterTypes[n].GetObjectType()->IsShared() ) + isShared = false; + if( func->parameterTypes[n].GetFuncDef() && !func->parameterTypes[n].GetFuncDef()->IsShared() ) + isShared = false; + } + func->isShared = isShared; - if( f2->name == func->name && - f2->nameSpace == func->nameSpace && - f2->IsSignatureExceptNameEqual(func) ) + // Check if there is another identical funcdef from another module and if so reuse that instead + if( func->isShared ) + { + for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) { - // Replace our funcdef for the existing one - funcDef->idx = f2->id; - module->funcDefs[module->funcDefs.IndexOf(func)] = f2; - f2->AddRefInternal(); + asCScriptFunction *f2 = engine->funcDefs[n]; + if( f2 == 0 || func == f2 ) + continue; - engine->funcDefs.RemoveValue(func); + if( !f2->isShared ) + continue; - func->ReleaseInternal(); - break; + if( f2->name == func->name && + f2->nameSpace == func->nameSpace && + f2->IsSignatureExceptNameEqual(func) ) + { + // Replace our funcdef for the existing one + funcDef->idx = f2->id; + module->funcDefs[module->funcDefs.IndexOf(func)] = f2; + f2->AddRefInternal(); + + engine->funcDefs.RemoveValue(func); + + func->ReleaseInternal(); + break; + } } } } @@ -2463,7 +2531,7 @@ void asCBuilder::CompileInterfaces() sClassDeclaration *intfDecl = interfaceDeclarations[n]; asCObjectType *intfType = intfDecl->objType; - // TODO: 2.28.1: Is this really at the correct place? Hasn't the vfTableIdx already been set here? + // TODO: Is this really at the correct place? Hasn't the vfTableIdx already been set here? // Co-opt the vfTableIdx value in our own methods to indicate the // index the function should have in the table chunk for this interface. for( asUINT d = 0; d < intfType->methods.GetLength(); d++ ) @@ -4124,6 +4192,34 @@ int asCBuilder::RegisterScriptFunctionFromNode(asCScriptNode *node, asCScriptCod return RegisterScriptFunction(node, file, objType, isInterface, isGlobalFunction, ns, isExistingShared, isMixin, name, returnType, parameterNames, parameterTypes, inOutFlags, defaultArgs, isConstMethod, isConstructor, isDestructor, isPrivate, isProtected, isOverride, isFinal, isShared); } +asCScriptFunction *asCBuilder::RegisterLambda(asCScriptNode *node, asCScriptCode *file, asCScriptFunction *funcDef, const asCString &name, asSNameSpace *ns) +{ + // Get the parameter names from the node + asCArray parameterNames; + asCArray defaultArgs; + asCScriptNode *args = node->firstChild; + while( args && args->nodeType == snIdentifier ) + { + asCString argName; + argName.Assign(&file->code[args->tokenPos], args->tokenLength); + parameterNames.PushLast(argName); + defaultArgs.PushLast(0); + args = args->next; + } + + // The statement block for the function must be disconnected, as the builder is going to be the owner of it + args->DisconnectParent(); + + // Get the return and parameter types from the funcDef + asCString funcName = name; + int r = RegisterScriptFunction(args, file, 0, 0, true, ns, false, false, funcName, funcDef->returnType, parameterNames, funcDef->parameterTypes, funcDef->inOutFlags, defaultArgs, false, false, false, false, false, false, false, false); + if( r < 0 ) + return 0; + + // Return the function that was just created (but that will be compiled later) + return engine->scriptFunctions[functions[functions.GetLength()-1]->funcId]; +} + 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 @@ -4734,11 +4830,15 @@ void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *ob // 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 + // If the scope contains ::identifier, then use the last identifier as the class name and the rest of it 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); + + // Check if the namespace actually exist, if not return silently as this cannot be the referring to a base class + asSNameSpace *ns = GetNameSpaceByString(nsName, objectType->nameSpace, errNode, script, false); + if( ns == 0 ) + return; // Find the base class with the specified scope while( objectType && (objectType->name != className || objectType->nameSpace != ns) ) @@ -4894,7 +4994,7 @@ asSNameSpace *asCBuilder::GetNameSpaceFromNode(asCScriptNode *node, asCScriptCod return GetNameSpaceByString(scope, implicitNs, node, script); } -asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script) +asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script, bool isRequired) { asSNameSpace *ns = implicitNs; if( nsName == "::" ) @@ -4902,7 +5002,7 @@ asSNameSpace *asCBuilder::GetNameSpaceByString(const asCString &nsName, asSNameS else if( nsName != "" ) { ns = engine->FindNameSpace(nsName.AddressOf()); - if( ns == 0 ) + if( ns == 0 && isRequired ) { asCString msg; msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, nsName.AddressOf()); @@ -5073,6 +5173,12 @@ asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCod ot = otInstance; } } + else if( n && n->next && n->next->nodeType == snDataType ) + { + asCString str; + str.Format(TXT_TYPE_s_NOT_TEMPLATE, ot->name.AddressOf()); + WriteError(str, file, n); + } // Create object data type if( ot ) diff --git a/lib/angelscript/source/as_builder.h b/lib/angelscript/source/as_builder.h index 3a9851fec..11c9ed917 100644 --- a/lib/angelscript/source/as_builder.h +++ b/lib/angelscript/source/as_builder.h @@ -174,7 +174,7 @@ protected: 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); + asSNameSpace *GetNameSpaceByString(const asCString &nsName, asSNameSpace *implicitNs, asCScriptNode *errNode, asCScriptCode *script, bool isRequired = true); asCString GetScopeFromNode(asCScriptNode *n, asCScriptCode *script, asCScriptNode **next = 0); asCObjectType *GetObjectType(const char *type, asSNameSpace *ns); @@ -213,6 +213,7 @@ protected: int RegisterEnum(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); int RegisterTypedef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); int RegisterFuncDef(asCScriptNode *node, asCScriptCode *file, asSNameSpace *ns); + asCScriptFunction *RegisterLambda(asCScriptNode *node, asCScriptCode *file, asCScriptFunction *funcDef, const asCString &name, asSNameSpace *ns); void CompleteFuncDef(sFuncDef *funcDef); void CompileInterfaces(); void CompileClasses(asUINT originalNumTempl); diff --git a/lib/angelscript/source/as_bytecode.cpp b/lib/angelscript/source/as_bytecode.cpp index 32564f2a0..b0777d4f8 100644 --- a/lib/angelscript/source/as_bytecode.cpp +++ b/lib/angelscript/source/as_bytecode.cpp @@ -1015,6 +1015,21 @@ void asCByteCode::OptimizeLocally(const asCArray &tempVariableOffsets) ChangeFirstDeleteNext(curr, asBC_PSF); instr = GoForward(curr); } + // VAR a, GETOBJREF 0 -> PshVPtr a + else if( curr->next && curr->next->op == asBC_GETOBJREF && curr->next->wArg[0] == 0 ) + { + ChangeFirstDeleteNext(curr, asBC_PshVPtr); + instr = GoForward(curr); + } + // VAR, PSF, GETREF {PTR_SIZE} -> PSF, PSF + if( curr->next && curr->next->op == asBC_PSF && + curr->next->next && curr->next->next->op == asBC_GETREF && + curr->next->next->wArg[0] == AS_PTR_SIZE ) + { + curr->op = asBC_PSF; + DeleteInstruction(curr->next->next); + instr = GoForward(curr); + } } } @@ -2220,6 +2235,13 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri } break; + case asBC_FuncPtr: + { + asCScriptFunction *func = *(asCScriptFunction**)ARG_DW(instr->arg); + fprintf(file, " %-8s 0x%x (func:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), func->GetDeclaration()); + } + break; + case asBC_PshC4: case asBC_Cast: fprintf(file, " %-8s 0x%x (i:%d, f:%g)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), *((int*) ARG_DW(instr->arg)), *((float*) ARG_DW(instr->arg))); @@ -2233,6 +2255,7 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri case asBC_CALLSYS: case asBC_CALLBND: case asBC_CALLINTF: + case asBC_Thiscall1: { int funcID = *(int*)ARG_DW(instr->arg); asCString decl = engine->GetFunctionDeclaration(funcID); @@ -2268,10 +2291,17 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri { case asBC_OBJTYPE: { - asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg); + asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); fprintf(file, " %-8s 0x%x (type:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), ot->GetName()); } break; + + case asBC_FuncPtr: + { + asCScriptFunction *func = *(asCScriptFunction**)ARG_QW(instr->arg); + fprintf(file, " %-8s 0x%x (func:%s)\n", asBCInfo[instr->op].name, (asUINT)*ARG_QW(instr->arg), func->GetDeclaration()); + } + break; default: #ifdef __GNUC__ @@ -2288,15 +2318,27 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri case asBCTYPE_wW_QW_ARG: case asBCTYPE_rW_QW_ARG: + switch( instr->op ) + { + case asBC_RefCpyV: + case asBC_FREE: + { + asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); + fprintf(file, " %-8s v%d, 0x%x (type:%s)\n", asBCInfo[instr->op].name, instr->wArg[0], (asUINT)*ARG_QW(instr->arg), ot->GetName()); + } + break; + + default: #ifdef __GNUC__ #ifdef _LP64 - fprintf(file, " %-8s v%d, 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); + fprintf(file, " %-8s v%d, 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); #else - fprintf(file, " %-8s v%d, 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); + fprintf(file, " %-8s v%d, 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); #endif #else - fprintf(file, " %-8s v%d, 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); + fprintf(file, " %-8s v%d, 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); #endif + } break; case asBCTYPE_DW_DW_ARG: diff --git a/lib/angelscript/source/as_bytecode.h b/lib/angelscript/source/as_bytecode.h index 59d8557b5..b1bb1127b 100644 --- a/lib/angelscript/source/as_bytecode.h +++ b/lib/angelscript/source/as_bytecode.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 @@ -130,6 +130,8 @@ public: int InstrW_W(asEBCInstr bc, int w, int b); int InstrSHORT_DW_DW(asEBCInstr bc, short a, asDWORD b, asDWORD c); + asCScriptEngine *GetEngine() const { return engine; }; + asCArray lineNumbers; asCArray sectionIdxs; int largestStackUsed; diff --git a/lib/angelscript/source/as_callfunc.cpp b/lib/angelscript/source/as_callfunc.cpp index 723ba1c28..81095ab8b 100644 --- a/lib/angelscript/source/as_callfunc.cpp +++ b/lib/angelscript/source/as_callfunc.cpp @@ -130,10 +130,11 @@ int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, internal->callConv = (internalCallConv)(thisCallConv + 2); #endif internal->baseOffset = ( int )MULTI_BASE_OFFSET(ptr); -#if defined(AS_ARM) && (defined(__GNUC__) || defined(AS_PSVITA)) +#if (defined(AS_ARM) || defined(AS_MIPS)) && (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. + // MIPS also appear to use the base offset to indicate virtual method. if( (size_t(internal->baseOffset) & 1) ) internal->callConv = (internalCallConv)(thisCallConv + 2); #endif @@ -577,10 +578,11 @@ int CallSystemFunction(int id, asCContext *context) } // Add the base offset for multiple inheritance -#if (defined(__GNUC__) && defined(AS_ARM)) || defined(AS_PSVITA) +#if (defined(__GNUC__) && (defined(AS_ARM) || defined(AS_MIPS))) || 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 + // MIPS also work like ARM in this regard obj = (void*)(asPWORD(obj) + (sysFunc->baseOffset>>1)); #else obj = (void*)(asPWORD(obj) + sysFunc->baseOffset); @@ -634,10 +636,11 @@ int CallSystemFunction(int id, asCContext *context) } // Add the base offset for multiple inheritance -#if (defined(__GNUC__) && defined(AS_ARM)) || defined(AS_PSVITA) +#if (defined(__GNUC__) && (defined(AS_ARM) || defined(AS_MIPS))) || 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 + // MIPS also work like ARM in this regard tempPtr = (void*)(asPWORD(tempPtr) + (sysFunc->baseOffset>>1)); #else tempPtr = (void*)(asPWORD(tempPtr) + sysFunc->baseOffset); @@ -822,7 +825,15 @@ int CallSystemFunction(int id, asCContext *context) if( cleanCount ) { args = context->m_regs.stackPointer; - if( callConv >= ICC_THISCALL ) + + // Skip the hidden argument for the return pointer + // TODO: runtime optimize: This check and increment should have been done in PrepareSystemFunction + if( descr->DoesReturnOnStack() ) + args += AS_PTR_SIZE; + + // Skip the object pointer on the stack + // TODO: runtime optimize: This check and increment should have been done in PrepareSystemFunction + if( callConv >= ICC_THISCALL && sysFunc->objForThiscall == 0 ) args += AS_PTR_SIZE; asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf(); diff --git a/lib/angelscript/source/as_callfunc_arm_gcc.S b/lib/angelscript/source/as_callfunc_arm_gcc.S index 38299d453..3ce5566a4 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-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 @@ -39,11 +39,13 @@ The assembler routines for Linux were written by Carlos Luna in December 2012 */ +#if !defined(AS_MAX_PORTABILITY) + #if defined(__arm__) || defined(__ARM__) || defined(I3D_ARCH_ARM) #if !defined(__linux__) || defined(__ANDROID__) || defined(ANDROID) || defined(__SOFTFP__) -/* iOS, Android, and Marmalade goes here */ +/* iOS, Android, Marmalade, and Linux with soft-float ABI goes here */ .global armFunc .global armFuncR0 @@ -85,7 +87,12 @@ stackargsloop: bne stackargsloop mov sp, r12 nomoreargs: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} @@ -133,7 +140,12 @@ stackargslooparmFuncObjLast: bne stackargslooparmFuncObjLast mov sp, r12 nomoreargsarmFuncObjLast: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} @@ -180,7 +192,12 @@ stackargslooparmFuncR0ObjLast: bne stackargslooparmFuncR0ObjLast mov sp, r12 nomoreargsarmFuncR0ObjLast: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} @@ -218,7 +235,12 @@ stackargslooparmFuncR0: bne stackargslooparmFuncR0 mov sp, r12 nomoreargsarmFuncR0: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} @@ -255,14 +277,19 @@ stackargslooparmFuncR0R1: bne stackargslooparmFuncR0R1 mov sp, r12 nomoreargsarmFuncR0R1: +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ + mov pc, r4 +#else blx r4 +#endif add sp, sp, r8 ldmia sp!, {r4-r8, pc} /* --------------------------------------------------------------------------------------------*/ #elif defined(__linux__) && !defined(__SOFTFP__) -/* The Linux code goes here */ +/* The Linux with hard-float ABI code goes here */ /* These codes are suitable for armeabi + vfp / armeabihf */ @@ -340,7 +367,7 @@ stackargsloop: mov sp, r12 nomoreargs: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else @@ -427,8 +454,8 @@ stackargslooparmFuncObjLast: mov sp, r12 nomoreargsarmFuncObjLast: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) - mov lr, pc +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else blx r4 @@ -521,8 +548,8 @@ stackargslooparmFuncR0ObjLast: mov sp, r12 nomoreargsarmFuncR0ObjLast: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) - mov lr, pc +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else blx r4 @@ -597,8 +624,8 @@ stackargslooparmFuncR0: mov sp, r12 nomoreargsarmFuncR0: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) - mov lr, pc +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else blx r4 @@ -677,8 +704,8 @@ stackargslooparmFuncR0R1: mov sp, r12 nomoreargsarmFuncR0R1: -#if defined (___ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) - mov lr, pc +#if defined (__ARM_ARCH_4T__) || defined (__ARM_ARCH_4__) + mov lr, pc /* older ARM didn't support blx */ mov pc, r4 #else blx r4 @@ -697,3 +724,6 @@ nomoreargsarmFuncR0R1: #endif /* arm */ +#endif /* !AS_MAX_PORTABILITY */ + + diff --git a/lib/angelscript/source/as_callfunc_arm_vita.S b/lib/angelscript/source/as_callfunc_arm_vita.S index 3281f46f5..406db039b 100644 --- a/lib/angelscript/source/as_callfunc_arm_vita.S +++ b/lib/angelscript/source/as_callfunc_arm_vita.S @@ -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 @@ -34,6 +34,8 @@ This code was adapted from as_callfunc_arm_gcc (ARM, Linux hard float) by Brandon Bare on October 2014. */ +#if !defined(AS_MAX_PORTABILITY) + #ifdef __psp2__ .syntax unified @@ -61,7 +63,7 @@ /* --------------------------------------------------------------------------------------------*/ armFunc: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} /* sp must be 8-byte alignment for ABI compliance, so the pushed registers must be even */ @@ -81,19 +83,19 @@ armFunc: /* Load the first 4 arguments into r0-r3 */ cmp r7, #4 - it ge + it ge ldrge r0, [r6] cmp r7, #8 - it ge + it ge ldrge r1, [r6, #4] cmp r7, #12 - it ge + it ge ldrge r2, [r6, #8] cmp r7, #16 - it ge + it ge ldrge r3, [r6, #12] stackargs: @@ -128,11 +130,11 @@ nomoreargs: pop {r4-r8, r10, r11, pc} - .fnend + .fnend /* --------------------------------------------------------------------------------------------*/ armFuncObjLast: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} /* We´re storing r11 just to keep the stack aligned to an 8 byte boundary */ @@ -157,28 +159,28 @@ armFuncObjLast: /* Load the first 4 arguments into r0-r3 */ cmp r7, #4 - it ge + it ge ldrge r0, [r6] cmp r7, #8 - it ge + it ge ldrge r1, [r6,#4] - it lt + it lt movlt r1, r5 cmp r7, #12 - it ge + it ge ldrge r2, [r6,#8] - it lt + it lt movlt r2, r5 cmp r7, #16 - it ge + it ge ldrge r3, [r6,#12] - ittt lt + 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 */ @@ -220,11 +222,11 @@ nomoreargsarmFuncObjLast: pop {r4-r8, r10,r11, pc} - .fnend + .fnend /* --------------------------------------------------------------------------------------------*/ armFuncR0ObjLast: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} @@ -252,35 +254,35 @@ armFuncR0ObjLast: /* Load the first 3 arguments into r1-r3 */ cmp r7, #4 - it ge + it ge ldrge r1, [r6] cmp r7, #8 - it ge + it ge ldrge r2, [r6,#4] - it lt + it lt movlt r2, r5 cmp r7, #12 - it ge + it ge ldrge r3, [r6,#8] - ittt lt + 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 + itt ge ldrge r7, [r6, #12] strge r7, [r6, #8] str r5, [r6, #12] /* Put objlast in r6 + 12 */ - mov r5, #0 + mov r5, #0 - it ge + 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 */ @@ -318,11 +320,11 @@ nomoreargsarmFuncR0ObjLast: pop {r4-r8, r10, r11, pc} - .fnend + .fnend /* --------------------------------------------------------------------------------------------*/ armFuncR0: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} @@ -344,19 +346,19 @@ armFuncR0: /* Load the first 3 arguments into r1-r3 */ cmp r7, #4 - it ge + it ge ldrge r1, [r6] cmp r7, #8 - it ge + it ge ldrge r2, [r6, #4] cmp r7, #12 - it ge + it ge ldrge r3, [r6, #8] cmp r7, #16 - it ge + it ge movge r11, #4 /* If there is still one arg to be placed, set the offset in r11 to #4 */ stackargsarmFuncR0: @@ -393,11 +395,11 @@ nomoreargsarmFuncR0: pop {r4-r8, r10, r11, pc} - .fnend + .fnend /* --------------------------------------------------------------------------------------------*/ armFuncR0R1: - .fnstart + .fnstart push {r4-r8, r10, r11, lr} @@ -421,23 +423,23 @@ armFuncR0R1: /* Load the first 2 arguments into r2-r3 */ cmp r7, #4 - it ge + it ge ldrge r2, [r6] cmp r7, #8 - it ge + it ge ldrge r3, [r6, #4] cmp r7, #12 - it ge + 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 + it ge movge r11, #8 /* If there is a fourth arg to be placed, set the offset in r11 to #8 */ - itt lt + itt lt ldrlt r7, [r6, #8] /* Else copy the third arg to the correct place in the array */ strlt r7, [r6, #12] @@ -475,6 +477,9 @@ nomoreargsarmFuncR0R1: pop {r4-r8, r10, r11, pc} - .fnend + .fnend + +#endif + +#endif /* !AS_MAX_PORTABILITY */ -#endif \ No newline at end of file diff --git a/lib/angelscript/source/as_callfunc_arm_xcode.S b/lib/angelscript/source/as_callfunc_arm_xcode.S index 60c4e18e6..235e242f3 100644 --- a/lib/angelscript/source/as_callfunc_arm_xcode.S +++ b/lib/angelscript/source/as_callfunc_arm_xcode.S @@ -1,6 +1,6 @@ /* AngelCode Scripting Library - Copyright (c) 2003-2009 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,6 +35,8 @@ // Adapted to GNUC by darktemplar216 in September 2009 // Small fixed to work under XCode GCC by Gilad Novik in October 2009 +#if !defined(AS_MAX_PORTABILITY) + #if defined(__arm__) || defined(__ARM__) .align 2 @@ -235,3 +237,6 @@ nomoreargsarmFuncR0R1: ldmia sp!, {r4-r8, pc} #endif + +#endif /* !AS_MAX_PORTABILITY */ + diff --git a/lib/angelscript/source/as_callfunc_mips.cpp b/lib/angelscript/source/as_callfunc_mips.cpp index 2dc8e0078..0b2e3b4fc 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-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,7 +35,9 @@ // These functions handle the actual calling of system functions // // This version is MIPS specific and was originally written -// by Manu Evans in April, 2006 +// by Manu Evans in April, 2006 for Playstation Portable (PSP) +// +// Support for Linux with MIPS was added by Andreas Jonsson in April, 2015 // @@ -52,10 +54,320 @@ #include #include +#if !defined(AS_ANDROID) #include +#endif BEGIN_AS_NAMESPACE +#if defined(__linux__) && defined(_ABIO32) + +// The MIPS ABI used by Linux is implemented here +// (Tested on CI20 MIPS Creator with Debian Linux) +// +// ref: SYSTEM V +// APPLICATION BINARY INTERFACE +// MIPS RISC Processor +// http://math-atlas.sourceforge.net/devel/assembly/mipsabi32.pdf +// +// ref: MIPS Instruction Reference +// http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html + +union SFloatRegs +{ + union { double d0; struct { float f0; asDWORD dummy0; };}; + union { double d1; struct { float f1; asDWORD dummy1; };}; +} ; + +extern "C" asQWORD mipsFunc(asUINT argSize, asDWORD *argBuffer, void *func, SFloatRegs &floatRegs); +asDWORD GetReturnedFloat(); +asQWORD GetReturnedDouble(); + +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; + + asQWORD retQW = 0; + + void *func = (void*)sysFunc->func; + void **vftable; + + asDWORD argBuffer[128]; // Ought to be big enough + asASSERT( sysFunc->paramSize < 128 ); + + asDWORD argOffset = 0; + + SFloatRegs floatRegs; + asDWORD floatOffset = 0; + + // If the application function returns the value in memory then + // the first argument must be the pointer to that memory + if( sysFunc->hostReturnInMemory ) + { + asASSERT( retPointer ); + argBuffer[argOffset++] = (asPWORD)retPointer; + } + + if( callConv == ICC_CDECL_OBJFIRST || callConv == ICC_CDECL_OBJFIRST_RETURNINMEM || + callConv == ICC_THISCALL || callConv == ICC_THISCALL_RETURNINMEM || + callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM || + callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM || + callConv == ICC_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the first argument + argBuffer[argOffset++] = (asPWORD)obj; + } + + if( callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || + callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ) + { + // Add the second object pointer + argBuffer[argOffset++] = (asPWORD)secondObject; + } + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + asCDataType ¶mType = descr->parameterTypes[n]; + if( paramType.IsObject() && !paramType.IsObjectHandle() && !paramType.IsReference() ) + { + if( paramType.GetObjectType()->flags & COMPLEX_MASK ) + { + // The object is passed by reference + argBuffer[argOffset++] = args[spos++]; + } + else + { + // Ensure 8byte alignment for classes that need it + if( (paramType.GetObjectType()->flags & asOBJ_APP_CLASS_ALIGN8) && (argOffset & 1) ) + argOffset++; + + // Copy the object's memory to the buffer + memcpy(&argBuffer[argOffset], *(void**)(args+spos), paramType.GetSizeInMemoryBytes()); + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + argOffset += paramType.GetSizeInMemoryDWords(); + } + } + else if( paramType.GetTokenType() == ttQuestion ) + { + // Copy both pointer and type id + argBuffer[argOffset++] = args[spos++]; + argBuffer[argOffset++] = args[spos++]; + } + else + { + // The first 2 floats or doubles are loaded into the float registers. + // Actually this is only done if they are the first arguments to the function, + // but it doesn't cause any harm to load them into the registers even if they + // won't be used so we don't need to check if they really are the first args. + if( floatOffset == 0 ) + { + if( paramType.GetTokenType() == ttFloat ) + floatRegs.f0 = *reinterpret_cast(&args[spos]); + else if( paramType.GetTokenType() == ttDouble ) + floatRegs.d0 = *reinterpret_cast(&args[spos]); + floatOffset++; + } + else if( floatOffset == 1 ) + { + if( paramType.GetTokenType() == ttFloat ) + floatRegs.f1 = *reinterpret_cast(&args[spos]); + else if( paramType.GetTokenType() == ttDouble ) + floatRegs.d1 = *reinterpret_cast(&args[spos]); + floatOffset++; + } + + // Copy the value directly + if( paramType.GetSizeOnStackDWords() > 1 ) + { + // Make sure the argument is 8byte aligned + if( argOffset & 1 ) + argOffset++; + *reinterpret_cast(&argBuffer[argOffset]) = *reinterpret_cast(&args[spos]); + argOffset += 2; + spos += 2; + } + else + argBuffer[argOffset++] = args[spos++]; + } + } + + if( callConv == ICC_CDECL_OBJLAST || callConv == ICC_CDECL_OBJLAST_RETURNINMEM ) + { + // Add the object pointer as the last argument + argBuffer[argOffset++] = (asPWORD)obj; + } + + if( callConv == ICC_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST || + callConv == ICC_THISCALL_OBJLAST_RETURNINMEM || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM ) + { + // Add the second object pointer + argBuffer[argOffset++] = (asPWORD)secondObject; + } + + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + case ICC_THISCALL_OBJFIRST: + case ICC_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_THISCALL_OBJLAST: + case ICC_THISCALL_OBJLAST_RETURNINMEM: + retQW = mipsFunc(argOffset*4, argBuffer, func, floatRegs); + break; + + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJFIRST: + case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_OBJLAST: + case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(void***)obj; + retQW = mipsFunc(argOffset*4, argBuffer, vftable[asPWORD(func)>>2], floatRegs); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + + // If the return is a float value we need to get the value from the FP register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&retQW = GetReturnedFloat(); + else + retQW = GetReturnedDouble(); + } + + return retQW; +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + + asm("swc1 $f0, %0\n" : "=m"(f)); + + return f; +} + +asQWORD GetReturnedDouble() +{ + asQWORD d = 0; + + asm("sdc1 $f0, %0\n" : "=m"(d)); + + return d; +} + +// asQWORD mipsFunc(asUINT argSize, asDWORD *argBuffer, void *func, SFloatRegs &floatRegs); +// $2,$3 $4 $5 $6 $7 +asm( +" .text\n" +//" .align 2\n" +" .cfi_startproc\n" +" .global mipsFunc\n" +" .ent mipsFunc\n" +"mipsFunc:\n" +//" .frame $fp,64,$31 # vars= 0, regs= 0/0, args= 0, gp= 0\n" +//" .mask 0x00000000,0\n" +//" .fmask 0x00000000,0\n" +" .set noreorder\n" +" .set nomacro\n" + +// align the stack frame to 8 bytes +" addiu $12, $4, 7\n" // t4 ($12) = argSize ($4) + 7 +" li $13, -8\n" // t5 ($13) = 0xfffffffffffffff8 +" and $12, $12, $13\n" // t4 ($12) &= t5 ($13). t4 holds the size of the argument block +// It is required that the caller reserves space for at least 16 bytes even if there are less than 4 arguments +// and add 8 bytes for the return pointer and s0 ($16) backup +" addiu $13, $12, 24\n" // t5 = t4 + 24. t5 ($13) holds the total size of the stack frame (including return pointer) +// save the s0 register (so we can use it to remember where our return pointer is lives) +" sw $16, -4($sp)\n" // store the s0 register (so we can use it to remember how big our stack frame is) +" .cfi_offset 16, -4\n" +// store the return pointer +" sw $31, -8($sp)\n" +" .cfi_offset 31, -8\n" +// keep original stack pointer +" move $16, $sp\n" +" .cfi_def_cfa_register 16\n" +// push the stack +" subu $sp, $sp, $13\n" + +// store the argument in temporary registers +" addiu $25, $6, 0\n" // t9 ($25) holds the function pointer (must be t9 for position independent code) +" addiu $3, $4, 0\n" // v1 ($3) holds the size of the argument buffer +" move $15, $5\n" // t7 ($15) holds the pointer to the argBuffer +" move $14, $7\n" // t6 ($14) holds the values for the float registers + +// load integer registers +" lw $4, 0($15)\n" // a0 ($4) +" lw $5, 4($15)\n" // a1 ($5) +" lw $6, 8($15)\n" // a2 ($6) +" lw $7, 12($15)\n" // a3 ($7) + +// load float registers +" ldc1 $f12, 8($14)\n" +" ldc1 $f14, 0($14)\n" + +// skip stack parameters if there are 4 or less as they are moved into the registers +" addi $14, $3, -16\n" // The first 4 args were already loaded into registers +" blez $14, andCall\n" +" nop\n" + +// push stack parameters +"pushArgs:\n" +" addi $3, -4\n" +// load from $15 + stack bytes ($3) +" addu $14, $15, $3\n" +" lw $14, 0($14)\n" +// store to $sp + stack bytes ($3) +" addu $13, $sp, $3\n" +" sw $14, 0($13)\n" +// if there are more, loop... +" bne $3, $0, pushArgs\n" +" nop\n" + +// and call the function +"andCall:\n" +" jalr $25\n" +" nop\n" + +// restore original stack pointer +" move $sp, $16\n" +// restore the return pointer +" lw $31, -8($sp)\n" +// restore the original value of $16 +" lw $16, -4($sp)\n" +// and return from the function +" jr $31\n" +" nop\n" + +" .set macro\n" +" .set reorder\n" +" .end mipsFunc\n" +" .cfi_endproc\n" +" .size mipsFunc, .-mipsFunc\n" +); + +#else // !(defined(__linux__) && defined(_ABIO32)) + +// The MIPS ABI used by PSP and PS2 is implemented here + #define AS_MIPS_MAX_ARGS 32 #define AS_NUM_REG_FLOATS 8 #define AS_NUM_REG_INTS 8 @@ -191,33 +503,12 @@ asDWORD GetReturnedFloat() return f; } -/* -asDWORD GetReturnedFloat(); - -asm( -" .align 4\n" -" .global GetReturnedFloat\n" -"GetReturnedFloat:\n" -" .set noreorder\n" -" .set nomacro\n" -" j $ra\n" -" mfc1 $v0, $f0\n" -" .set macro\n" -" .set reorder\n" -" .end Func\n" -*/ - - -// sizeof(double) == 4 with sh-elf-gcc (3.4.0) -m4 -// so this isn't really used... asQWORD GetReturnedDouble() { asQWORD d = 0; - printf("Broken!!!"); -/* - asm("sw $v0, %0\n" : "=m"(d)); -*/ + asm("sdc1 $f0, %0\n" : "=m"(d)); + return d; } @@ -352,7 +643,7 @@ asm( " .set nomacro\n" // align the stack frame to 8 bytes " addiu $12, $6, 7\n" -" li $13, -8\n" // 0xfffffffffffffffc +" li $13, -8\n" // 0xfffffffffffffff8 " and $12, $12, $13\n" // t4 holds the size of the argument block // and add 8 bytes for the return pointer and s0 backup " addiu $13, $12, 8\n" // t5 holds the total size of the stack frame (including return pointer) @@ -432,6 +723,8 @@ asm( " .size mipsFunc, .-mipsFunc\n" ); +#endif // PSP and PS2 MIPS ABI + END_AS_NAMESPACE #endif // AS_MIPS diff --git a/lib/angelscript/source/as_callfunc_x64_gcc.cpp b/lib/angelscript/source/as_callfunc_x64_gcc.cpp index 5e497dec8..e05ae986f 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-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 @@ -73,14 +73,18 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i __asm__ __volatile__ ( - " movq %0, %%rcx \n" // rcx = cnt + " movq %0, %%rcx \n" // rcx = cnt " movq %1, %%r10 \n" // r10 = args " movq %2, %%r11 \n" // r11 = func // Backup stack pointer in R15 that is guaranteed to maintain its value over function calls " movq %%rsp, %%r15 \n" +#ifdef __OPTIMIZE__ // Make sure the stack unwind logic knows we've backed up the stack pointer in register r15 + // This should only be done if any optimization is done. If no optimization (-O0) is used, + // then the compiler already backups the rsp before entering the inline assembler code " .cfi_def_cfa_register r15 \n" +#endif // Skip the first 128 bytes on the stack frame, called "red zone", // that might be used by the compiler to store temporary values @@ -100,7 +104,7 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i " jle endstack \n" " subl $1, %%esi \n" " xorl %%edx, %%edx \n" - " leaq 8(, %%rsi, 8), %%rcx \n" + " leaq 8(, %%rsi, 8), %%rcx \n" "loopstack: \n" " movq 112(%%r10, %%rdx), %%rax \n" " pushq %%rax \n" @@ -128,12 +132,16 @@ static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, i " movsd 56(%%rax), %%xmm7 \n" // Call the function - " call *%%r11 \n" + " call *%%r11 \n" // Restore stack pointer " mov %%r15, %%rsp \n" +#ifdef __OPTIMIZE__ // Inform the stack unwind logic that the stack pointer has been restored + // This should only be done if any optimization is done. If no optimization (-O0) is used, + // then the compiler already backups the rsp before entering the inline assembler code " .cfi_def_cfa_register rsp \n" +#endif // Put return value in retQW1 and retQW2, using either RAX:RDX or XMM0:XMM1 depending on type of return value " movl %5, %%ecx \n" diff --git a/lib/angelscript/source/as_callfunc_x86.cpp b/lib/angelscript/source/as_callfunc_x86.cpp index 21c5f40df..d6f50db81 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-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 @@ -1263,13 +1263,13 @@ endcopy: "subl $4, %%ecx \n" "jne copyloop3 \n" "endcopy3: \n" -#if defined(__MINGW32__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4) - // MinGW made some strange choices with 4.7, and the thiscall calling convention - // when returning an object in memory is completely different from when not returning - // in memory - "pushl 0(%%ebx) \n" // push obj on the stack - "movl 16(%%ebx), %%ecx \n" // move the return pointer into ECX - "call *12(%%ebx) \n" // call the function +#ifdef AS_MINGW47 + // MinGW made some strange choices with 4.7 and the thiscall calling convention, + // returning an object in memory is completely different from when not returning + // in memory + "pushl 0(%%ebx) \n" // push obj on the stack + "movl 16(%%ebx), %%ecx \n" // move the return pointer into ECX + "call *12(%%ebx) \n" // call the function #else "movl 0(%%ebx), %%ecx \n" // move obj into ECX #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK @@ -1286,7 +1286,7 @@ endcopy: "addl $4, %%esp \n" // pop the object pointer #endif #endif -#endif // MINGW +#endif // AS_MINGW47 // Pop the alignment bytes "popl %%esp \n" "popl %%ebx \n" diff --git a/lib/angelscript/source/as_callfunc_xenon.cpp b/lib/angelscript/source/as_callfunc_xenon.cpp index 34228216c..aaa4d8586 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-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 @@ -43,7 +43,9 @@ // various fixes in asm ppcFunc // fix for variable arguments // - +// Modified by Anthony Clark May 2015 +// Fixed the issue where int64 and uint64 could not be passed nativly +// few minor fixes within asm ppcFunc to handle int64 and uint64 // XBox 360 calling convention @@ -87,7 +89,6 @@ // References: // https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF77852569970071B0D6/$file/eabi_app.pdf // -// TODO: The code doesn't handle int64 and uint64 parameters // TODO: The code doesn't handle objects passed by value (unless they are max 4 bytes in size) @@ -128,7 +129,7 @@ enum argTypes // pArgs is the array of the argument values // pArgTypes is an array containing a byte indicating the type (enum argTypes) for each argument. // dwFunc is the address of the function that will be called -asQWORD __declspec( naked ) ppcFunc(const asDWORD* pArgs, asDWORD dwFunc, const asBYTE* pArgTypes) +asQWORD __declspec( naked ) ppcFunc(const asQWORD* pArgs, asDWORD dwFunc, const asBYTE* pArgTypes) { __asm { @@ -202,7 +203,7 @@ ppcNextArg: ////////////////////////////////////////////////////////////////////////// ppcArgIsInteger: // Get the arg from the stack - lwz r12, 0(r26) + ld r12, 0(r26) // r23 holds the integer arg count so far cmplwi cr6, r23, 0 @@ -251,11 +252,11 @@ ppcArgIsInteger: b ppcLoadIntRegUpd ppcLoadIntRegUpd: - stw r12, 0(r31) // push on the stack + std r12, 0(r31) // push on the stack addi r31, r31, 8 // inc stack by 1 reg addi r23, r23, 1 // Increment used int register count - addi r26, r26, 4 // Increment pArgs + addi r26, r26, 8 // Increment pArgs b ppcNextArg // Call next arg ////////////////////////////////////////////////////////////////////////// @@ -511,7 +512,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Pack the arguments into an array that ppcFunc() can use to load each CPU register properly asBYTE ppcArgsType[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG + AS_PPC_ENDOFARGS]; - asDWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG]; + asQWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG]; int argsCnt = 0; // If the function returns an object in memory, we allocate the memory and put the ptr to the front (will go to r3) @@ -625,35 +626,34 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, } else { - // TODO: How should int64 and uint64 be passed natively? - // Currently the code doesn't handle these types - // TODO: The code also ignore the fact that large objects // passed by value has been copied to the stack // in the above loop. *pCurArgType++ = ppcINTARG; - *((int*) pCurFixedArgValue) = *((int*) pCurStackArgValue); + *((asQWORD*) pCurFixedArgValue) = *((asUINT*) pCurStackArgValue); if( !descr->parameterTypes[n].IsReference() ) { - // If the arg is less that 4 bytes, then move the - // bytes to the higher bytes within the dword + // If the arg is not 4 bytes which we coppied, lets do it again the right way asUINT numBytes = descr->parameterTypes[n].GetSizeInMemoryBytes(); if( numBytes == 1 ) { - pCurFixedArgValue[3] = pCurFixedArgValue[0]; - pCurFixedArgValue[0] = 0; + *((asQWORD*) pCurFixedArgValue) = *((asBYTE*) pCurStackArgValue); } else if( numBytes == 2 ) { - *(asWORD*)&pCurFixedArgValue[2] = *(asWORD*)&pCurFixedArgValue[0]; - *(asWORD*)&pCurFixedArgValue[0] = 0; + *((asQWORD*) pCurFixedArgValue) = *((asWORD*) pCurStackArgValue); + } + else if( numBytes == 8 ) + { + *((asQWORD*) pCurFixedArgValue) = *((asQWORD*) pCurStackArgValue); + pCurStackArgValue += 4; // Increase our cur stack arg value by 4 bytes to = 8 total later } } - pCurFixedArgValue += 4; + pCurFixedArgValue += 8; pCurStackArgValue += 4; // if it is a variable argument, account for the typeId @@ -703,7 +703,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, // Add the object pointer as the last argument ppcArgsType[argsCnt++] = ppcINTARG; ppcArgsType[argsCnt] = ppcENDARG; - *((asPWORD*)pCurFixedArgValue) = (asPWORD)obj; + *((asQWORD*)pCurFixedArgValue) = (asPWORD)obj; retQW = ppcFunc( ppcArgs, (asDWORD)func, ppcArgsType ); break; } diff --git a/lib/angelscript/source/as_compiler.cpp b/lib/angelscript/source/as_compiler.cpp index 62b67315d..337e209df 100644 --- a/lib/angelscript/source/as_compiler.cpp +++ b/lib/angelscript/source/as_compiler.cpp @@ -110,11 +110,14 @@ void asCCompiler::Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFun m_isConstructor = false; m_isConstructorCalled = false; m_classDecl = 0; + m_globalVar = 0; nextLabel = 0; breakLabels.SetLength(0); continueLabels.SetLength(0); + numLambdas = 0; + byteCode.ClearAll(); } @@ -530,8 +533,8 @@ int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asC // We need to parse the statement block now asCScriptNode *blockBegin; - // If the function signature was implicit, e.g. virtual property - // accessor, then the received node already is the statement block + // If the function signature was implicit, e.g. virtual property accessor or + // lambda function, then the received node already is the statement block if( func->nodeType != snStatementBlock ) blockBegin = func->lastChild; else @@ -1187,6 +1190,7 @@ void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableSc int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *script, asCScriptNode *node, sGlobalVariableDescription *gvar, asCScriptFunction *outFunc) { Reset(builder, script, outFunc); + m_globalVar = gvar; // Add a variable scope (even though variables can't be declared) AddVariableScope(); @@ -1354,8 +1358,8 @@ void asCCompiler::CompileInitAsCopy(asCDataType &dt, int offset, asCByteCode *bc } else { - // TODO: 2.28.1: Need to reserve variables, as the default constructor may need - // to allocate temporary variables to compute default args + // TODO: 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); @@ -1423,7 +1427,7 @@ int asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, as 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() ) + if( ctx->IsVoidExpression() ) param = asCDataType::CreateNullHandle(); // If value assign is disabled for reference types, then make @@ -1484,6 +1488,17 @@ int asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, as } else if( ctx->type.dataType.IsNullHandle() ) { + // Make sure the argument type can support handles (or is itself a handle) + if( !dt.SupportHandles() && !dt.IsObjectHandle() ) + { + asCString str; + 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; + } + // Need to initialize a local temporary variable to // represent the null handle when passed as reference asASSERT( ctx->bc.GetLastInstr() == asBC_PshNull ); @@ -1599,42 +1614,53 @@ int asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, as ctx->bc.AddCode(&tmpBC); } - // Make sure the variable is not used in the expression - offset = AllocateVariableNotIn(dt, true, false, ctx); - - if( dt.IsPrimitive() ) + // If the expression is marked as clean, then it can be used directly + // without the need to allocate another temporary value as it is known + // that the argument has no other value than the default + if( ctx->isCleanArg ) { - ctx->type.SetVariable(dt, offset, true); - PushVariableOnStack(ctx, true); + // Must be a local variable + asASSERT( ctx->type.isVariable ); } else { - // 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); - // Allocate and construct the temporary object - asCByteCode tmpBC(engine); - CallDefaultConstructor(dt, offset, IsVariableOnHeap(offset), &tmpBC, node); + if( dt.IsPrimitive() ) + { + ctx->type.SetVariable(dt, offset, true); + PushVariableOnStack(ctx, true); + } + else + { + // TODO: Need to reserve variables, as the default constructor may need + // to allocate temporary variables to compute default args - // Insert the code before the expression code - tmpBC.AddCode(&ctx->bc); - ctx->bc.AddCode(&tmpBC); + // Allocate and construct the temporary object + asCByteCode tmpBC(engine); + CallDefaultConstructor(dt, offset, IsVariableOnHeap(offset), &tmpBC, node); - dt.MakeReference(!dt.IsObject() || dt.IsObjectHandle()); - asCTypeInfo type; - type.Set(dt); - type.isTemporary = true; - type.stackOffset = (short)offset; + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); - ctx->type = type; + dt.MakeReference(!dt.IsObject() || dt.IsObjectHandle()); + asCTypeInfo type; + type.Set(dt); + type.isTemporary = true; + type.stackOffset = (short)offset; - ctx->bc.InstrSHORT(asBC_PSF, (short)offset); - if( dt.IsObject() && !dt.IsObjectHandle() ) - ctx->bc.Instr(asBC_RDSPtr); + ctx->type = type; + + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if( dt.IsObject() && !dt.IsObjectHandle() ) + ctx->bc.Instr(asBC_RDSPtr); + } + + // After the function returns the temporary variable will + // be assigned to the expression, if it is a valid lvalue } - - // After the function returns the temporary variable will - // be assigned to the expression, if it is a valid lvalue } else if( refType == asTM_INOUTREF ) { @@ -1825,14 +1851,15 @@ void asCCompiler::PrepareFunctionCall(int funcId, asCByteCode *bc, asCArrayGetFunctionDescription(funcId); + asASSERT( descr->parameterTypes.GetLength() == args.GetLength() ); + // If the function being called is the opAssign or copy constructor for the same type // as the argument, then we should avoid making temporary copy of the argument - asASSERT( descr->parameterTypes.GetLength() == args.GetLength() ); bool makingCopy = false; if( descr->parameterTypes.GetLength() == 1 && descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) && - ((descr->name == "opAssign" && descr->objectType && descr->objectType == args[0]->type.dataType.GetObjectType()) || - (args[0]->type.dataType.GetObjectType() && descr->name == args[0]->type.dataType.GetObjectType()->name)) ) + (((descr->name == "opAssign" || descr->name == "$beh0") && descr->objectType && descr->objectType == args[0]->type.dataType.GetObjectType()) || + (descr->objectType == 0 && args[0]->type.dataType.GetObjectType() && descr->name == args[0]->type.dataType.GetObjectType()->name)) ) makingCopy = true; // Add code for arguments @@ -1870,8 +1897,8 @@ void asCCompiler::MoveArgsToStack(int funcId, asCByteCode *bc, asCArrayparameterTypes.GetLength() == 1 && descr->parameterTypes[0].IsEqualExceptRefAndConst(args[0]->type.dataType) && - ((descr->name == "opAssign" && descr->objectType && descr->objectType == args[0]->type.dataType.GetObjectType()) || - (args[0]->type.dataType.GetObjectType() && descr->name == args[0]->type.dataType.GetObjectType()->name)) ) + (((descr->name == "opAssign" || descr->name == "$beh0") && descr->objectType && descr->objectType == args[0]->type.dataType.GetObjectType()) || + (descr->objectType == 0 && args[0]->type.dataType.GetObjectType() && descr->name == args[0]->type.dataType.GetObjectType()->name)) ) makingCopy = true; #endif @@ -2266,7 +2293,7 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArrayfuncType == asFUNC_VIRTUAL ) desc = objectType->virtualFunctionTable[desc->vfTableIdx]; - //Match every named argument to an argument in the function + // Match every named argument to an argument in the function for( n = 0; n < namedArgs->GetLength(); ++n ) (*namedArgs)[n].match = asUINT(-1); @@ -2307,7 +2334,7 @@ asUINT asCCompiler::MatchFunctions(asCArray &funcs, asCArrayGetLength(); ++n ) @@ -2778,10 +2805,6 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as { asSExprContext ctx(engine); - // TODO: copy: Here we should look for the best matching constructor, instead of - // 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; @@ -2797,159 +2820,294 @@ bool asCCompiler::CompileInitialization(asCScriptNode *node, asCByteCode *bc, as r = CompileAssignment(node, expr); } - // Call the default constructor here - if( isVarGlobOrMem == 0 ) - CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), &ctx.bc, errNode); - else if( isVarGlobOrMem == 1 ) - CallDefaultConstructor(type, offset, true, &ctx.bc, errNode, isVarGlobOrMem); - else if( isVarGlobOrMem == 2 ) - CallDefaultConstructor(type, offset, type.IsReference(), &ctx.bc, errNode, isVarGlobOrMem); + // Look for appropriate constructor + asCArray funcs; + asCArray args; - if( r >= 0 ) + // Handles must use the handle assignment operation. + // Types that are ASHANDLE must not allow the use of the constructor in this case, + // because it is ambiguous whether a value assignment or handle assignment will be done. + // Only do this if the expression is of the same type, as the expression is an assignment + // and an initialization constructor may not have the same meaning. + // TODO: Should allow initialization constructor if it is declared as allowed for implicit conversions. + if( !type.IsObjectHandle() && !expr->type.isExplicitHandle && + !(type.GetObjectType() && (type.GetObjectType()->GetFlags() & asOBJ_ASHANDLE)) && + type.IsEqualExceptRefAndConst(expr->type.dataType) ) { - if( type.IsPrimitive() ) + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) { - if( type.IsReadOnly() && expr->type.isConstant ) - { - 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; - } - - asSExprContext lctx(engine); - if( isVarGlobOrMem == 0 ) - lctx.type.SetVariable(type, offset, false); - else if( isVarGlobOrMem == 1 ) - { - lctx.type.Set(type); - lctx.type.dataType.MakeReference(true); - - // If it is an enum value, i.e. offset is negative, that is being compiled then - // we skip this as the bytecode won't be used anyway, only the constant value - if( offset >= 0 ) - lctx.bc.InstrPTR(asBC_LDG, engine->globalProperties[offset]->GetAddressOfValue()); - } + if( type.GetObjectType()->flags & asOBJ_REF ) + funcs = beh->factories; else - { - asASSERT( isVarGlobOrMem == 2 ); - lctx.type.Set(type); - lctx.type.dataType.MakeReference(true); - - // Load the reference of the primitive member into the register - lctx.bc.InstrSHORT(asBC_PSF, 0); - lctx.bc.Instr(asBC_RDSPtr); - lctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); - lctx.bc.Instr(asBC_PopRPtr); - } - lctx.type.dataType.MakeReadOnly(false); - lctx.type.isLValue = true; - - DoAssignment(&ctx, &lctx, expr, node, node, ttAssignment, node); - ProcessDeferredParams(&ctx); + funcs = beh->constructors; } - else + + asCString str = type.Format(outFunc->nameSpace); + args.PushLast(expr); + MatchFunctions(funcs, args, node, str.AddressOf(), 0, 0, 0, true); + } + + if( funcs.GetLength() == 1 ) + { + // Use the constructor + + // TODO: clean-up: A large part of this is identical to the initalization with argList above + + // Add the default values for arguments not explicitly supplied + int r = CompileDefaultAndNamedArgs(node, args, funcs[0], type.GetObjectType()); + + if( r == asSUCCESS ) { - // TODO: runtime optimize: Here we should look for the best matching constructor, instead of - // just the copy constructor. Only if no appropriate constructor is - // available should the assignment operator be used. - - asSExprContext lexpr(engine); - lexpr.type.Set(type); - if( isVarGlobOrMem == 0 ) - lexpr.type.dataType.MakeReference(IsVariableOnHeap(offset)); - else if( isVarGlobOrMem == 1 ) - lexpr.type.dataType.MakeReference(true); - else if( isVarGlobOrMem == 2 ) + asSExprContext ctx(engine); + if( type.GetObjectType() && (type.GetObjectType()->flags & asOBJ_REF) ) { - if( !lexpr.type.dataType.IsObject() || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_REF) ) - lexpr.type.dataType.MakeReference(true); - } + if( isVarGlobOrMem == 0 ) + MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, offset); + else + { + MakeFunctionCall(&ctx, funcs[0], 0, args, node); + ctx.bc.Instr(asBC_RDSPtr); + if( isVarGlobOrMem == 1 ) + { + // Store the returned handle in the global variable + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Store the returned handle in the member + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + } + ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType()); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } - // Allow initialization of constant variables - lexpr.type.dataType.MakeReadOnly(false); - - if( type.IsObjectHandle() ) - lexpr.type.isExplicitHandle = true; - - if( isVarGlobOrMem == 0 ) - { - lexpr.bc.InstrSHORT(asBC_PSF, (short)offset); - lexpr.type.stackOffset = (short)offset; - lexpr.type.isVariable = true; - } - else if( isVarGlobOrMem == 1 ) - { - lexpr.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + // Pop the reference left by the function call + ctx.bc.Instr(asBC_PopPtr); } else { - lexpr.bc.InstrSHORT(asBC_PSF, 0); - lexpr.bc.Instr(asBC_RDSPtr); - lexpr.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); - lexpr.type.stackOffset = -1; - } - lexpr.type.isLValue = true; + bool onHeap = false; - - // If left expression resolves into a registered type - // check if the assignment operator is overloaded, and check - // the type of the right hand expression. If none is found - // the default action is a direct copy if it is the same type - // and a simple assignment. - bool assigned = false; - // 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)) ) - { - bool useHndlAssign = lexpr.type.dataType.IsHandleToAsHandleType(); - assigned = CompileOverloadedDualOperator(node, &lexpr, expr, &ctx, useHndlAssign); - if( assigned ) + if( isVarGlobOrMem == 0 ) { - // Pop the resulting value - if( !ctx.type.dataType.IsPrimitive() ) - ctx.bc.Instr(asBC_PopPtr); + // When the object is allocated on the heap, the address where the + // reference will be stored must be pushed on the stack before the + // arguments. This reference on the stack is safe, even if the script + // is suspended during the evaluation of the arguments. + onHeap = IsVariableOnHeap(offset); + if( onHeap ) + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + } + else if( isVarGlobOrMem == 1 ) + { + // Push the address of the location where the variable will be stored on the stack. + // This reference is safe, because the addresses of the global variables cannot change. + onHeap = true; + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + // Value types may be allocated inline if they are POD types + onHeap = !type.IsObject() || type.IsReference() || (type.GetObjectType()->flags & asOBJ_REF); + if( onHeap ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + } + } - // Release the argument - ProcessDeferredParams(&ctx); + PrepareFunctionCall(funcs[0], &ctx.bc, args); + MoveArgsToStack(funcs[0], &ctx.bc, args, false); - // Release temporary variable that may be allocated by the overloaded operator - ReleaseTemporaryVariable(ctx.type, &ctx.bc); + // When the object is allocated on the stack, the address to the + // object is pushed on the stack after the arguments as the object pointer + if( !onHeap ) + { + if( isVarGlobOrMem == 2 ) + { + ctx.bc.InstrSHORT(asBC_PSF, 0); + ctx.bc.Instr(asBC_RDSPtr); + ctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + } + else + { + ctx.bc.InstrSHORT(asBC_PSF, (short)offset); + } + } + + PerformFunctionCall(funcs[0], &ctx, onHeap, &args, type.GetObjectType()); + + if( isVarGlobOrMem == 0 ) + { + // Mark the object in the local variable as initialized + ctx.bc.ObjInfo(offset, asOBJ_INIT); } } + bc->AddCode(&ctx.bc); + } + } + else + { + // Call the default constructur, then call the assignment operator - if( !assigned ) + // Call the default constructor here + if( isVarGlobOrMem == 0 ) + CallDefaultConstructor(type, offset, IsVariableOnHeap(offset), &ctx.bc, errNode); + else if( isVarGlobOrMem == 1 ) + CallDefaultConstructor(type, offset, true, &ctx.bc, errNode, isVarGlobOrMem); + else if( isVarGlobOrMem == 2 ) + CallDefaultConstructor(type, offset, type.IsReference(), &ctx.bc, errNode, isVarGlobOrMem); + + if( r >= 0 ) + { + if( type.IsPrimitive() ) { - 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 ) { + 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; } - // Add expression code to bytecode - MergeExprBytecode(&ctx, expr); + asSExprContext lctx(engine); + if( isVarGlobOrMem == 0 ) + lctx.type.SetVariable(type, offset, false); + else if( isVarGlobOrMem == 1 ) + { + lctx.type.Set(type); + lctx.type.dataType.MakeReference(true); - // Add byte code for storing value of expression in variable - ctx.bc.AddCode(&lexpr.bc); + // If it is an enum value, i.e. offset is negative, that is being compiled then + // we skip this as the bytecode won't be used anyway, only the constant value + if( offset >= 0 ) + lctx.bc.InstrPTR(asBC_LDG, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + asASSERT( isVarGlobOrMem == 2 ); + lctx.type.Set(type); + lctx.type.dataType.MakeReference(true); - PerformAssignment(&lexpr.type, &expr->type, &ctx.bc, errNode); - - // Release temporary variables used by expression - ReleaseTemporaryVariable(expr->type, &ctx.bc); - - ctx.bc.Instr(asBC_PopPtr); + // Load the reference of the primitive member into the register + lctx.bc.InstrSHORT(asBC_PSF, 0); + lctx.bc.Instr(asBC_RDSPtr); + lctx.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + lctx.bc.Instr(asBC_PopRPtr); + } + lctx.type.dataType.MakeReadOnly(false); + lctx.type.isLValue = true; + DoAssignment(&ctx, &lctx, expr, node, node, ttAssignment, node); ProcessDeferredParams(&ctx); } - } - } + else + { + // TODO: runtime optimize: Here we should look for the best matching constructor, instead of + // just the copy constructor. Only if no appropriate constructor is + // available should the assignment operator be used. - bc->AddCode(&ctx.bc); + asSExprContext lexpr(engine); + lexpr.type.Set(type); + if( isVarGlobOrMem == 0 ) + lexpr.type.dataType.MakeReference(IsVariableOnHeap(offset)); + else if( isVarGlobOrMem == 1 ) + lexpr.type.dataType.MakeReference(true); + else if( isVarGlobOrMem == 2 ) + { + if( !lexpr.type.dataType.IsObject() || (lexpr.type.dataType.GetObjectType()->flags & asOBJ_REF) ) + lexpr.type.dataType.MakeReference(true); + } + + // Allow initialization of constant variables + lexpr.type.dataType.MakeReadOnly(false); + + if( type.IsObjectHandle() ) + lexpr.type.isExplicitHandle = true; + + if( isVarGlobOrMem == 0 ) + { + lexpr.bc.InstrSHORT(asBC_PSF, (short)offset); + lexpr.type.stackOffset = (short)offset; + lexpr.type.isVariable = true; + } + else if( isVarGlobOrMem == 1 ) + { + lexpr.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + } + else + { + lexpr.bc.InstrSHORT(asBC_PSF, 0); + lexpr.bc.Instr(asBC_RDSPtr); + lexpr.bc.InstrSHORT_DW(asBC_ADDSi, (short)offset, engine->GetTypeIdFromDataType(asCDataType::CreateObject(outFunc->objectType, false))); + lexpr.type.stackOffset = -1; + } + lexpr.type.isLValue = true; + + + // If left expression resolves into a registered type + // check if the assignment operator is overloaded, and check + // the type of the right hand expression. If none is found + // the default action is a direct copy if it is the same type + // and a simple assignment. + bool assigned = false; + // 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)) ) + { + bool useHndlAssign = lexpr.type.dataType.IsHandleToAsHandleType(); + assigned = CompileOverloadedDualOperator(node, &lexpr, expr, &ctx, useHndlAssign); + if( assigned ) + { + // Pop the resulting value + if( !ctx.type.dataType.IsPrimitive() ) + ctx.bc.Instr(asBC_PopPtr); + + // Release the argument + ProcessDeferredParams(&ctx); + + // Release temporary variable that may be allocated by the overloaded operator + ReleaseTemporaryVariable(ctx.type, &ctx.bc); + } + } + + if( !assigned ) + { + 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 ) + { + isConstantExpression = true; + *constantValue = expr->type.qwordValue; + } + + // Add expression code to bytecode + 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); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(expr->type, &ctx.bc); + + ctx.bc.Instr(asBC_PopPtr); + + ProcessDeferredParams(&ctx); + } + } + } + + bc->AddCode(&ctx.bc); + } } else { @@ -2976,8 +3134,7 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte { // Check if the type supports initialization lists if( var->dataType.GetObjectType() == 0 || - var->dataType.GetBehaviour()->listFactory == 0 || - var->dataType.IsObjectHandle() ) + var->dataType.GetBehaviour()->listFactory == 0 ) { asCString str; str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format(outFunc->nameSpace).AddressOf()); @@ -4098,13 +4255,14 @@ void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc) } //--------------------------- - // Compile the increment statement + // Compile the increment statement(s) asCByteCode nextBC(engine); - asCScriptNode *third = second->next; - if( third->nodeType == snExpressionStatement ) + asCScriptNode *cnode = second->next; + while( cnode && cnode->nodeType == snExpressionStatement && cnode != fnode->lastChild ) { - LineInstr(&nextBC, third->tokenPos); - CompileExpressionStatement(third, &nextBC); + LineInstr(&nextBC, cnode->tokenPos); + CompileExpressionStatement(cnode, &nextBC); + cnode = cnode->next; } //------------------------------ @@ -4367,6 +4525,10 @@ void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode * if( expr.IsClassMethod() || expr.IsGlobalFunc() ) Error(TXT_INVALID_EXPRESSION_AMBIGUOUS_NAME, enode); + // Must not have unused anonymous functions + if( expr.IsLambda() ) + Error(TXT_INVALID_EXPRESSION_LAMBDA, 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 @@ -5855,11 +6017,58 @@ asUINT asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const return cost; } +asUINT asCCompiler::ImplicitConvLambdaToFunc(asSExprContext *ctx, const asCDataType &to, asCScriptNode * /*node*/, EImplicitConv /*convType*/, bool generateCode) +{ + asASSERT( to.GetFuncDef() && ctx->IsLambda() ); + + // Check that the lambda has the correct amount of arguments + asUINT count = 0; + asCScriptNode *argNode = ctx->exprNode->firstChild; + while( argNode->nodeType == snIdentifier ) + { + count++; + argNode = argNode->next; + } + asASSERT( argNode->nodeType == snStatementBlock ); + + asCScriptFunction *funcDef = to.GetFuncDef(); + if( funcDef->parameterTypes.GetLength() != count ) + return asCC_NO_CONV; + + // The Lambda can be used as this funcdef + ctx->type.dataType = to; + + if( generateCode ) + { + // Build a unique name for the anonymous function + asCString name; + if( m_globalVar ) + name.Format("$%s$%d", m_globalVar->name.AddressOf(), numLambdas++); + else + name.Format("$%s$%d", outFunc->GetDeclaration(), numLambdas++); + + // Register the lambda with the builder for later compilation + asCScriptFunction *func = builder->RegisterLambda(ctx->exprNode, script, funcDef, name, outFunc->nameSpace); + asASSERT( func == 0 || funcDef->IsSignatureExceptNameEqual(func) ); + ctx->bc.InstrPTR(asBC_FuncPtr, func); + + // Clear the expression node as it is no longer valid + ctx->exprNode = 0; + } + + return asCC_CONST_CONV; +} + asUINT asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, bool allowObjectConstruct) { asASSERT( ctx->type.dataType.GetTokenType() != ttUnrecognizedToken || ctx->type.dataType.IsNullHandle() ); + if( to.GetFuncDef() && ctx->IsLambda() ) + { + return ImplicitConvLambdaToFunc(ctx, to, node, convType, generateCode); + } + // No conversion from void to any other type if( ctx->type.dataType.GetTokenType() == ttVoid ) return asCC_NO_CONV; @@ -6329,16 +6538,25 @@ asUINT asCCompiler::ImplicitConvObjectValue(asSExprContext *ctx, const asCDataTy // 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); + + // If this an object on the heap, the pointer must be dereferenced + if( IsVariableOnHeap(stackOffset) ) + arg.bc.Instr(asBC_RDSPtr); + // 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); + + // Mark the argument as clean, so that MakeFunctionCall knows it + // doesn't have to make a copy of it in order to protect the value + arg.isCleanArg = true; // Call the behaviour method + asCArray args; + args.PushLast(&arg); MakeFunctionCall(ctx, funcs[0], ctx->type.dataType.GetObjectType(), args, node); // Use the reference to the variable as the result of the expression @@ -7876,9 +8094,9 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx) // 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() ) + if( le.type.dataType.IsReference() && le.type.dataType.IsObject() && !le.type.dataType.IsObjectHandle() ) { - asASSERT( re.type.dataType.IsReference() && re.type.isVariable && re.type.dataType.IsObject() && !re.type.dataType.IsObjectHandle() ); + asASSERT( re.type.dataType.IsReference() && re.type.dataType.IsObject() && !re.type.dataType.IsObjectHandle() ); ctx->bc.Instr(asBC_RDSPtr); } @@ -8055,7 +8273,15 @@ int asCCompiler::CompileExpression(asCScriptNode *expr, asSExprContext *ctx) } // Convert to polish post fix, i.e: a+b => ab+ + asCArray postfix; + ConvertToPostFix(expr, postfix); + // Compile the postfix formatted expression + return CompilePostFixExpression(&postfix, ctx); +} + +void asCCompiler::ConvertToPostFix(asCScriptNode *expr, asCArray &postfix) +{ // The algorithm that I've implemented here is similar to // Djikstra's Shunting Yard algorithm, though I didn't know it at the time. // ref: http://en.wikipedia.org/wiki/Shunting-yard_algorithm @@ -8069,28 +8295,26 @@ int asCCompiler::CompileExpression(asCScriptNode *expr, asSExprContext *ctx) node = node->next; } - asCArray stack(count); - asCArray stack2(count); + asCArray stackA(count); + asCArray &stackB = postfix; + stackB.Allocate(count, false); node = expr->firstChild; while( node ) { int precedence = GetPrecedence(node); - while( stack.GetLength() > 0 && - precedence <= GetPrecedence(stack[stack.GetLength()-1]) ) - stack2.PushLast(stack.PopLast()); + while( stackA.GetLength() > 0 && + precedence <= GetPrecedence(stackA[stackA.GetLength()-1]) ) + stackB.PushLast(stackA.PopLast()); - stack.PushLast(node); + stackA.PushLast(node); node = node->next; } - while( stack.GetLength() > 0 ) - stack2.PushLast(stack.PopLast()); - - // Compile the postfix formatted expression - return CompilePostFixExpression(&stack2, ctx); + while( stackA.GetLength() > 0 ) + stackB.PushLast(stackA.PopLast()); } int asCCompiler::CompilePostFixExpression(asCArray *postfix, asSExprContext *ctx) @@ -8402,12 +8626,14 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s // Recursively search parent namespaces for global entities asCString currScope = scope; - if( scope == "" ) - currScope = outFunc->nameSpace->name; + + // Get the namespace for this scope. This may return null if the scope is an enum + asSNameSpace *ns = DetermineNameSpace(currScope); + if( ns && currScope != "::" ) + currScope = ns->name; + while( !found && !noGlobal && !objType ) { - asSNameSpace *ns = DetermineNameSpace(currScope); - // Is it a global property? if( !found && ns ) { @@ -8621,7 +8847,7 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s { ctx->type.SetDummy(); asCString str; - str.Format(TXT_UNKNOWN_SCOPE_s, currScope.AddressOf()); + str.Format(TXT_UNKNOWN_SCOPE_s, scope.AddressOf()); Error(str, errNode); return -1; } @@ -8639,6 +8865,9 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s currScope = currScope.SubString(0, pos); else currScope = "::"; + + if( ns ) + ns = engine->GetParentNameSpace(ns); } } @@ -8872,7 +9101,7 @@ int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx } else if( vnode->nodeType == snConstructCall ) { - CompileConstructCall(vnode, ctx); + return CompileConstructCall(vnode, ctx); } else if( vnode->nodeType == snAssignment ) { @@ -8888,12 +9117,19 @@ int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx else if( vnode->nodeType == snCast ) { // Implement the cast operator - CompileConversion(vnode, ctx); + return CompileConversion(vnode, ctx); } else if( vnode->nodeType == snUndefined && vnode->tokenType == ttVoid ) { // This is a void expression - ctx->type.SetVoidExpression(); + ctx->SetVoidExpression(); + } + else if( vnode->nodeType == snFunction ) + { + // This is an anonymous function + // Defer the evaluation of the function until it known where it + // will be used, which is where the signature will be defined + ctx->SetLambda(vnode); } else asASSERT(false); @@ -9149,7 +9385,7 @@ void asCCompiler::ProcessHeredocStringConstant(asCString &str, asCScriptNode *no str = tmp; } -void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) +int asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) { asSExprContext expr(engine); asCDataType to; @@ -9221,7 +9457,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) { // Assume that the error can be fixed and allow the compilation to continue ctx->type.SetConstantDW(to, 0); - return; + return -1; } ProcessPropertyGetAccessor(&expr, node); @@ -9230,7 +9466,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) if( expr.IsClassMethod() ) { Error(TXT_INVALID_OP_ON_METHOD, node); - return; + return -1; } // We don't want a reference for conversion casts @@ -9252,7 +9488,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) // This will keep information about constant type MergeExprBytecode(ctx, &expr); ctx->type = expr.type; - return; + return 0; } if( to.IsEqualExceptRefAndConst(expr.type.dataType) && to.IsPrimitive() ) @@ -9260,7 +9496,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) MergeExprBytecode(ctx, &expr); ctx->type = expr.type; ctx->type.dataType.MakeReadOnly(true); - return; + return 0; } // The implicit conversion already does most of the conversions permitted, @@ -9284,7 +9520,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) } if( conversionOK ) - return; + return 0; // Conversion not available ctx->type.SetDummy(); @@ -9298,6 +9534,7 @@ void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) msg.Format(TXT_NO_CONVERSION_s_TO_s, strFrom.AddressOf(), strTo.AddressOf()); Error(msg, node); + return -1; } void asCCompiler::AfterFunctionCall(int funcID, asCArray &args, asSExprContext *ctx, bool deferAll) @@ -9314,12 +9551,12 @@ void asCCompiler::AfterFunctionCall(int funcID, asCArray &args, int n = (int)descr->parameterTypes.GetLength() - 1; for( ; n >= 0; n-- ) { - // All &out arguments must be deferred + // All &out arguments must be deferred, except if the argument is clean, in which case the actual reference was passed in to the function // If deferAll is set all objects passed by reference or handle must be deferred - if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF)) || + if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF) && !args[n]->isCleanArg) || (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 ); + asASSERT( !(descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] == asTM_OUTREF) && !args[n]->isCleanArg) || args[n]->origExpr ); // For &inout, only store the argument if it is for a temporary variable if( engine->ep.allowUnsafeReferences || @@ -9408,11 +9645,11 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx) { // We must still evaluate the expression MergeExprBytecode(ctx, expr); - if( !expr->type.IsVoidExpression() && (!expr->type.isConstant || expr->type.IsNullConstant()) ) + if( !expr->IsVoidExpression() && (!expr->type.isConstant || expr->type.IsNullConstant()) ) ctx->bc.Instr(asBC_PopPtr); // 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) ) + if( !expr->IsVoidExpression() && !expr->type.IsNullConstant() && !(expr->type.isConstant && expr->type.qwordValue == 0) ) Error(TXT_ARG_NOT_LVALUE, outParam.argNode); ReleaseTemporaryVariable(outParam.argType, &ctx->bc); @@ -9446,13 +9683,14 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx) } -void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) +int asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) { // The first node is a datatype node asCString name; asCTypeInfo tempObj; bool onHeap = true; asCArray funcs; + bool error = false; // It is possible that the name is really a constructor asCDataType dt; @@ -9460,8 +9698,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) if( dt.IsPrimitive() ) { // This is a cast to a primitive type - CompileConversion(node, ctx); - return; + return CompileConversion(node, ctx); } if( dt.GetObjectType() && (dt.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) ) @@ -9477,7 +9714,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) str.Format(TXT_CANT_CONSTRUCT_s_USE_REF_CAST, dt.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.SetDummy(); - return; + return -1; } if( !dt.CanBeInstantiated() ) @@ -9492,7 +9729,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format(outFunc->nameSpace).AddressOf()); Error(str, node); ctx->type.SetDummy(); - return; + return -1; } // Do not allow constructing non-shared types in shared functions @@ -9502,6 +9739,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) asCString msg; msg.Format(TXT_SHARED_CANNOT_USE_NON_SHARED_TYPE_s, dt.GetObjectType()->name.AddressOf()); Error(msg, node); + return -1; } // Compile the arguments @@ -9528,7 +9766,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) asDELETE(args[0],asSExprContext); - return; + return 0; } } @@ -9586,7 +9824,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) // Push the reference on the stack ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); - return; + return 0; } } @@ -9603,7 +9841,10 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) // The delegate must be able to hold on to a reference to the object if( !args[0]->type.dataType.SupportHandles() ) + { Error(TXT_CANNOT_CREATE_DELEGATE_FOR_NOREF_TYPES, node); + error = true; + } else { // Filter the available object methods to find the one that matches the func def @@ -9652,18 +9893,22 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) // Push a reference to the temporary variable on the stack ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset); + + // Clean up arguments + ReleaseTemporaryVariable(args[0]->type, &ctx->bc); } else { asCString msg; msg.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, dt.GetFuncDef()->GetDeclaration()); Error(msg.AddressOf(), node); + error = true; } } // Clean-up arg asDELETE(args[0],asSExprContext); - return; + return error ? -1 : 0; } MatchFunctions(funcs, args, node, name.AddressOf(), &namedArgs, 0, false); @@ -9671,6 +9916,7 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) if( funcs.GetLength() != 1 ) { // The error was reported by MatchFunctions() + error = true; // Dummy value ctx->type.SetDummy(); @@ -9726,12 +9972,15 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) PerformFunctionCall(funcs[0], ctx, false, &args); } } + else + error = true; } } else { // Failed to compile the argument list, set the result to the dummy type ctx->type.SetDummy(); + error = true; } // Cleanup @@ -9745,6 +9994,8 @@ void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) { asDELETE(namedArgs[n].ctx,asSExprContext); } + + return error ? -1 : 0; } @@ -9951,7 +10202,7 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a 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' - if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) && !args[0]->type.IsVoidExpression() ) + if( args.GetLength() == 1 && args[0]->type.IsVoid() && !args[0]->IsVoidExpression() ) { // Evaluate the expression before the function call MergeExprBytecode(ctx, args[0]); @@ -10062,7 +10313,8 @@ asSNameSpace *asCCompiler::DetermineNameSpace(const asCString &scope) if( scope == "" ) { - if( outFunc->nameSpace->name != "" ) + // When compiling default argument expression the correct namespace is stored in the outFunc even for objects + if( outFunc->nameSpace->name != "" || isCompilingDefaultArg ) ns = outFunc->nameSpace; else if( outFunc->objectType && outFunc->objectType->nameSpace->name != "" ) ns = outFunc->objectType->nameSpace; @@ -10089,7 +10341,7 @@ int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx } // Don't allow any operators on void expressions - if( ctx->type.IsVoidExpression() ) + if( ctx->IsVoidExpression() ) { Error(TXT_VOID_CANT_BE_OPERAND, node); return -1; @@ -10927,11 +11179,25 @@ int asCCompiler::ProcessPropertyGetSetAccessor(asSExprContext *ctx, asSExprConte before.bc.InstrPTR(asBC_REFCPY, func->objectType); before.bc.Instr(asBC_PopPtr); + if( lctx->type.isTemporary ) + { + // Add the release of the temporary variable as a deferred expression + asSDeferredParam deferred; + deferred.origExpr = 0; + deferred.argInOutFlags = asTM_INREF; + deferred.argNode = 0; + deferred.argType.SetVariable(ctx->type.dataType, lctx->type.stackOffset, true); + before.deferredParams.PushLast(deferred); + } + // 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; + // Don't release the temporary variable too early + lctx->type.isTemporary = false; + ctx->bc.AddCode(&before.bc); } @@ -10960,6 +11226,10 @@ int asCCompiler::ProcessPropertyGetSetAccessor(asSExprContext *ctx, asSExprConte if( before.type.stackOffset ) ReleaseTemporaryVariable(before.type.stackOffset, &ctx->bc); + asASSERT( ctx->deferredParams.GetLength() == 0 ); + ctx->deferredParams = before.deferredParams; + ProcessDeferredParams(ctx); + return 0; } @@ -11044,7 +11314,7 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct } // Don't allow any operators on void expressions - if( ctx->type.IsVoidExpression() ) + if( ctx->IsVoidExpression() ) { Error(TXT_VOID_CANT_BE_OPERAND, node); return -1; @@ -11653,7 +11923,7 @@ asUINT asCCompiler::MatchArgument(asCArray &funcs, asCArraytype.IsVoidExpression() ) + if( argExpr->IsVoidExpression() ) { if( desc->inOutFlags[paramNum] == asTM_OUTREF ) return 0; @@ -11665,6 +11935,7 @@ int asCCompiler::MatchArgument(asCScriptFunction *desc, const asSExprContext *ar ti.type = argExpr->type; ti.methodName = argExpr->methodName; ti.enumValue = argExpr->enumValue; + ti.exprNode = argExpr->exprNode; if( argExpr->type.dataType.IsPrimitive() ) ti.type.dataType.MakeReference(false); int cost = ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, allowObjectConstruct); @@ -11723,7 +11994,8 @@ int asCCompiler::MatchArgument(asCScriptFunction *desc, const asSExprContext *ar 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 - if( paramType->IsReference() && !(refType & asTM_INREF) ) + // Clean arguments (i.e. default value) will be passed in directly as there is nothing to protect + if( paramType->IsReference() && !(refType & asTM_INREF) && !arg->isCleanArg ) { // Store the original bytecode so that it can be reused when processing the deferred output parameter asSExprContext *orig = asNEW(asSExprContext)(engine); @@ -12077,28 +12349,14 @@ int asCCompiler::CompileOverloadedDualOperator2(asCScriptNode *node, const char return 0; } -void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode * /*node*/, bool useVariable, int stackOffset, int funcPtrVar) +void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode *node, bool useVariable, int stackOffset, int funcPtrVar) { if( objectType ) - { Dereference(ctx, true); - // This following warning was removed as there may be valid reasons - // for calling non-const methods on temporary objects, and we shouldn't - // warn when there is no way of removing the warning. -/* - // Warn if the method is non-const and the object is temporary - // since the changes will be lost when the object is destroyed. - // If the object is accessed through a handle, then it is assumed - // the object is not temporary, even though the handle is. - if( ctx->type.isTemporary && - !ctx->type.dataType.IsObjectHandle() && - !engine->scriptFunctions[funcId]->isReadOnly ) - { - Warning("A non-const method is called on temporary object. Changes to the object may be lost.", node); - Information(engine->scriptFunctions[funcId]->GetDeclaration(), node); - } -*/ } + // Store the expression node for error reporting + if( ctx->exprNode == 0 ) + ctx->exprNode = node; asCByteCode objBC(engine); objBC.AddCode(&ctx->bc); @@ -12162,7 +12420,7 @@ int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSE } // Don't allow any operators on void expressions - if( lctx->type.IsVoidExpression() || rctx->type.IsVoidExpression() ) + if( lctx->IsVoidExpression() || rctx->IsVoidExpression() ) { Error(TXT_VOID_CANT_BE_OPERAND, node); return -1; @@ -14042,7 +14300,19 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo else if( descr->funcType == asFUNC_SCRIPT ) ctx->bc.Call(asBC_CALL , descr->id, argSize); else if( descr->funcType == asFUNC_SYSTEM ) - ctx->bc.Call(asBC_CALLSYS , descr->id, argSize); + { + // Check if we can use the faster asBC_Thiscall1 instruction, i.e. one of + // type &obj::func(int) + // type &obj::func(uint) + if( descr->GetObjectType() && descr->returnType.IsReference() && + descr->parameterTypes.GetLength() == 1 && + (descr->parameterTypes[0].IsIntegerType() || descr->parameterTypes[0].IsUnsignedType()) && + descr->parameterTypes[0].GetSizeInMemoryBytes() == 4 && + !descr->parameterTypes[0].IsReference() ) + ctx->bc.Call(asBC_Thiscall1, descr->id, argSize); + else + ctx->bc.Call(asBC_CALLSYS , descr->id, argSize); + } else if( descr->funcType == asFUNC_FUNCDEF ) ctx->bc.CallPtr(asBC_CallPtr, funcPtrVar, argSize); } @@ -14201,20 +14471,7 @@ void asCCompiler::MergeExprBytecodeAndType(asSExprContext *before, asSExprContex { MergeExprBytecode(before, after); - before->type = after->type; - before->property_get = after->property_get; - before->property_set = after->property_set; - before->property_const = after->property_const; - before->property_handle = after->property_handle; - before->property_ref = after->property_ref; - before->property_arg = after->property_arg; - before->exprNode = after->exprNode; - before->methodName = after->methodName; - before->enumValue = after->enumValue; - - after->property_arg = 0; - - // Do not copy the origExpr member + before->Merge(after); } void asCCompiler::FilterConst(asCArray &funcs, bool removeConst) diff --git a/lib/angelscript/source/as_compiler.h b/lib/angelscript/source/as_compiler.h index bd0ff7fd8..f9de7e475 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-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 @@ -73,14 +73,9 @@ struct asSExprContext { asSExprContext(asCScriptEngine *engine) : bc(engine) { - exprNode = 0; - origExpr = 0; - property_get = 0; - property_set = 0; - property_const = false; - property_handle = false; - property_ref = false; - property_arg = 0; + property_arg = 0; + + Clear(); } ~asSExprContext() { @@ -90,43 +85,95 @@ struct asSExprContext void Clear() { bc.ClearAll(); - type.SetDummy(); + type.Set(asCDataType()); + deferredParams.SetLength(0); if( property_arg ) asDELETE(property_arg, asSExprContext); - property_arg = 0; - deferredParams.SetLength(0); - exprNode = 0; - origExpr = 0; - property_get = 0; - property_set = 0; - property_const = false; - property_handle = false; - property_ref = false; - methodName = ""; - enumValue = ""; + property_arg = 0; + exprNode = 0; + origExpr = 0; + property_get = 0; + property_set = 0; + property_const = false; + property_handle = false; + property_ref = false; + methodName = ""; + enumValue = ""; + isVoidExpression = false; + isCleanArg = false; } - bool IsClassMethod() + bool IsClassMethod() const { if( type.dataType.GetObjectType() == 0 ) return false; if( methodName == "" ) return false; if( type.dataType.GetObjectType() == &type.dataType.GetObjectType()->engine->functionBehaviours ) return false; return true; } - bool IsGlobalFunc() + bool IsGlobalFunc() const { if( type.dataType.GetObjectType() == 0 ) return false; if( methodName == "" ) return false; if( type.dataType.GetObjectType() != &type.dataType.GetObjectType()->engine->functionBehaviours ) return false; return true; } + void SetLambda(asCScriptNode *funcDecl) + { + asASSERT( funcDecl && funcDecl->nodeType == snFunction ); + asASSERT( bc.GetLastInstr() == -1 ); + + Clear(); + type.SetUndefinedFuncHandle(bc.GetEngine()); + exprNode = funcDecl; + } + bool IsLambda() const + { + if( type.IsUndefinedFuncHandle() && exprNode && exprNode->nodeType == snFunction ) + return true; + + return false; + } + void SetVoidExpression() + { + Clear(); + type.SetVoid(); + isVoidExpression = true; + } + bool IsVoidExpression() const + { + if( isVoidExpression && type.IsVoid() && exprNode == 0 ) + return true; + + return false; + } + void Merge(asSExprContext *after) + { + type = after->type; + property_get = after->property_get; + property_set = after->property_set; + property_const = after->property_const; + property_handle = after->property_handle; + property_ref = after->property_ref; + property_arg = after->property_arg; + exprNode = after->exprNode; + methodName = after->methodName; + enumValue = after->enumValue; + isVoidExpression = after->isVoidExpression; + isCleanArg = after->isCleanArg; + + after->property_arg = 0; + + // Do not copy the origExpr member + } asCByteCode bc; asCTypeInfo type; int property_get; int property_set; - bool property_const; // If the object that is being accessed through property accessor is read-only - bool property_handle; // If the property accessor is called on an object stored in a handle - bool property_ref; // If the property accessor is called on a reference + bool property_const; // If the object that is being accessed through property accessor is read-only + bool property_handle; // If the property accessor is called on an object stored in a handle + bool property_ref; // If the property accessor is called on a reference + bool isVoidExpression; // Set to true if the expression is an explicit 'void', e.g. used to ignore out parameters in func calls + bool isCleanArg; // Set to true if the expression has only been initialized with default constructor asSExprContext *property_arg; asCArray deferredParams; asCScriptNode *exprNode; @@ -212,8 +259,8 @@ protected: int CompileExpressionPostOp(asCScriptNode *node, asSExprContext *out); int CompileExpressionValue(asCScriptNode *node, asSExprContext *out); 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 CompileConstructCall(asCScriptNode *node, asSExprContext *out); + int CompileConversion(asCScriptNode *node, 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); @@ -239,6 +286,7 @@ protected: void CompileInitAsCopy(asCDataType &type, int offset, asCByteCode *bc, asSExprContext *arg, asCScriptNode *node, bool derefDestination); // Helper functions + void ConvertToPostFix(asCScriptNode *expr, asCArray &postfix); 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); @@ -288,6 +336,7 @@ protected: 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); + asUINT ImplicitConvLambdaToFunc(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true); void LineInstr(asCByteCode *bc, size_t pos); @@ -307,16 +356,18 @@ protected: bool hasCompileErrors; int nextLabel; + int numLambdas; - asCVariableScope *variables; - asCBuilder *builder; - asCScriptEngine *engine; - asCScriptCode *script; + asCVariableScope *variables; + asCBuilder *builder; + asCScriptEngine *engine; + asCScriptCode *script; asCScriptFunction *outFunc; - bool m_isConstructor; - bool m_isConstructorCalled; - sClassDeclaration *m_classDecl; + bool m_isConstructor; + bool m_isConstructorCalled; + sClassDeclaration *m_classDecl; + sGlobalVariableDescription *m_globalVar; asCArray breakLabels; asCArray continueLabels; @@ -353,7 +404,7 @@ protected: bool isCompilingDefaultArg; bool isProcessingDeferredParams; - int noCodeOutput; + int noCodeOutput; }; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_config.h b/lib/angelscript/source/as_config.h index c9771e4ac..cb05bffbd 100644 --- a/lib/angelscript/source/as_config.h +++ b/lib/angelscript/source/as_config.h @@ -586,6 +586,7 @@ #define AS_NO_MEMORY_H #define AS_MIPS #define AS_PSP + #define AS_USE_DOUBLE_AS_FLOAT // PSVita #elif defined(__psp2__) #define AS_PSVITA @@ -799,6 +800,14 @@ // As of version 4.7 MinGW changed the ABI, presumably // to be better aligned with how MSVC works #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4 + #define AS_MINGW47 + #endif + + #if (__clang_major__ == 3 && __clang_minor__ > 4) || __clang_major > 3 + #define AS_MINGW47 + #endif + + #ifdef AS_MINGW47 #undef CALLEE_POPS_HIDDEN_RETURN_POINTER #define THISCALL_CALLEE_POPS_ARGUMENTS #else @@ -845,7 +854,7 @@ // STDCALL is not available on 64bit Linux #undef STDCALL #define STDCALL - #elif defined(__ARMEL__) || defined(__arm__) + #elif (defined(__ARMEL__) || defined(__arm__)) && !(defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)) #define AS_ARM // TODO: The stack unwind on exceptions currently fails due to the assembler code in as_callfunc_arm_gcc.S @@ -884,11 +893,24 @@ #elif defined(__mips__) #define AS_MIPS - #define AS_BIG_ENDIAN - #define AS_USE_DOUBLE_AS_FLOAT + #undef STDCALL + #define STDCALL - // Native calling conventions for Linux/Mips do not work yet. - #define AS_MAX_PORTABILITY + #ifdef _ABIO32 + #define AS_MIPS + + // All structures are returned in memory regardless of size or complexity + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #else + // For other ABIs the native calling convention is not available (yet) + #define AS_MAX_PORTABILITY + #endif #else #define AS_MAX_PORTABILITY #endif @@ -936,6 +958,7 @@ // Support native calling conventions on MIPS architecture #if (defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__)) && !defined(__LP64__) #define AS_MIPS + #define AS_USE_DOUBLE_AS_FLOAT #else #define AS_MAX_PORTABILITY #endif @@ -988,6 +1011,9 @@ #if (defined(_ARM_) || defined(__arm__)) // Android 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 THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE @@ -1013,6 +1039,26 @@ #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK #define AS_X86 #undef AS_NO_THISCALL_FUNCTOR_METHOD + #elif defined(__mips__) + #define AS_MIPS + #undef STDCALL + #define STDCALL + + #ifdef _ABIO32 + #define AS_MIPS + + // All structures are returned in memory regardless of size or complexity + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + #undef AS_NO_THISCALL_FUNCTOR_METHOD + #else + // For other ABIs the native calling convention is not available (yet) + #define AS_MAX_PORTABILITY + #endif #endif // Haiku OS @@ -1047,7 +1093,7 @@ // Support native calling conventions on Intel 32bit CPU #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK #define AS_X86 - #elif defined(__LP64__) + #elif defined(__x86_64__) #define AS_X64_GCC #define HAS_128_BIT_PRIMITIVES #define SPLIT_OBJS_BY_MEMBER_TYPES @@ -1112,11 +1158,6 @@ // Nothing special here #endif -// MIPS architecture (generally PS2 and PSP consoles, potentially supports N64 as well) -#if defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__) || defined(__PSP__) || defined(__psp__) || defined(_EE_) || defined(_PSP) || defined(_PS2) - #define AS_USE_DOUBLE_AS_FLOAT // use 32bit floats instead of doubles -#endif - // PowerPC, e.g. Mac, GameCube, PS3, XBox 360, Wii #if defined(__PPC__) || defined(__ppc__) || defined(_PPC_) || defined(EPPC) #define AS_BIG_ENDIAN diff --git a/lib/angelscript/source/as_context.cpp b/lib/angelscript/source/as_context.cpp index 489877a0f..9096a1045 100644 --- a/lib/angelscript/source/as_context.cpp +++ b/lib/angelscript/source/as_context.cpp @@ -134,17 +134,32 @@ public: #endif +// interface AS_API asIScriptContext *asGetActiveContext() { asCThreadLocalData *tld = asCThreadManager::GetLocalData(); - if( tld->activeContexts.GetLength() == 0 ) + + // tld can be 0 if asGetActiveContext is called before any engine has been created. + + // Observe! I've seen a case where an application linked with the library twice + // and thus ended up with two separate instances of the code and global variables. + // The application somehow mixed the two instances so that a function called from + // a script ended up calling asGetActiveContext from the other instance that had + // never been initialized. + + if( tld == 0 || tld->activeContexts.GetLength() == 0 ) return 0; return tld->activeContexts[tld->activeContexts.GetLength()-1]; } +// internal +// Note: There is no asPopActiveContext(), just call tld->activeContexts.PopLast() instead asCThreadLocalData *asPushActiveContext(asIScriptContext *ctx) { asCThreadLocalData *tld = asCThreadManager::GetLocalData(); + asASSERT( tld ); + if( tld == 0 ) + return 0; tld->activeContexts.PushLast(ctx); return tld; } @@ -1304,8 +1319,9 @@ int asCContext::Execute() } // Pop the active context - asASSERT(tld->activeContexts[tld->activeContexts.GetLength()-1] == this); - tld->activeContexts.PopLast(); + asASSERT(tld && tld->activeContexts[tld->activeContexts.GetLength()-1] == this); + if( tld ) + tld->activeContexts.PopLast(); if( m_status == asEXECUTION_FINISHED ) { @@ -4301,6 +4317,67 @@ void asCContext::ExecuteNext() } l_bc += 2; break; + case asBC_Thiscall1: + // This instruction is a faster version of asBC_CALLSYS. It is faster because + // it has much less runtime overhead with determining the calling convention + // and no dynamic code for loading the parameters. The instruction can only + // be used to call functions with the following signatures: + // + // type &obj::func(int) + // type &obj::func(uint) + // void obj::func(int) + // void obj::func(uint) + { + // Get function ID from the argument + int i = asBC_INTARG(l_bc); + + // 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; + + // Pop the thispointer from the stack + void *obj = *(void**)l_sp; + l_sp += AS_PTR_SIZE; + + // Pop the int arg from the stack + int arg = *(int*)l_sp; + l_sp++; + + // Call the method + m_callingSystemFunction = m_engine->scriptFunctions[i]; + void *ptr = m_engine->CallObjectMethodRetPtr(obj, arg, m_callingSystemFunction); + m_callingSystemFunction = 0; + *(asPWORD*)&m_regs.valueRegister = (asPWORD)ptr; + + // Update the program position after the call so that line number is correct + l_bc += 2; + + if( m_regs.doProcessSuspend ) + { + // Should the execution be suspended? + if( m_doSuspend ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + m_status = asEXECUTION_SUSPENDED; + return; + } + // An exception might have been raised + if( m_status != asEXECUTION_ACTIVE ) + { + m_regs.programPointer = l_bc; + m_regs.stackPointer = l_sp; + m_regs.stackFramePointer = l_fp; + + return; + } + } + } + break; // Don't let the optimizer optimize for size, // since it requires extra conditions and jumps @@ -4887,7 +4964,7 @@ int asCContext::GetExceptionLineNumber(int *column, const char **sectionName) if( sectionName ) { - // The section index can be -1 if the exception was raised in a generated function, e.g. factstub for templates + // The section index can be -1 if the exception was raised in a generated function, e.g. $fact for templates if( m_exceptionSectionIdx >= 0 ) *sectionName = m_engine->scriptSectionNames[m_exceptionSectionIdx]->AddressOf(); else diff --git a/lib/angelscript/source/as_datatype.cpp b/lib/angelscript/source/as_datatype.cpp index 05dc7b3ce..88d0067eb 100644 --- a/lib/angelscript/source/as_datatype.cpp +++ b/lib/angelscript/source/as_datatype.cpp @@ -227,7 +227,7 @@ asCString asCDataType::Format(asSNameSpace *currNs, bool includeNamespace) const str += "const"; } - if( isReference ) + if( isReference ) str += "&"; return str; diff --git a/lib/angelscript/source/as_debug.h b/lib/angelscript/source/as_debug.h index a86428d41..197887401 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-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 @@ -74,11 +74,19 @@ typedef double float64_t; #include #endif +#endif // AS_PSVITA +#endif // _WIN32_WCE +#endif // AS_WII + +#endif // !defined(AS_DEBUG) + + #if defined(_MSC_VER) && defined(AS_PROFILE) // Currently only do profiling with MSVC++ #include +#include #include "as_string.h" #include "as_map.h" #include "as_string_util.h" @@ -144,7 +152,7 @@ public: return time; } - void End(const char *name, double beginTime) + void End(const char * /*name*/, double beginTime) { double time = GetTime(); @@ -247,7 +255,7 @@ protected: END_AS_NAMESPACE -#else // _MSC_VER && AS_PROFILE +#else // !(_MSC_VER && AS_PROFILE) // Define it so nothing is done #define TimeIt(x) @@ -257,20 +265,6 @@ 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 +#endif // defined(AS_DEBUG_H) diff --git a/lib/angelscript/source/as_gc.cpp b/lib/angelscript/source/as_gc.cpp index 36f6230db..f488a5053 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-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 @@ -486,23 +486,18 @@ int asCGarbageCollector::ReportAndReleaseUndestroyedObjects() engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf()); // Add additional info for builtin types - if( gcObj.type->name == "_builtin_function_" ) + if( gcObj.type->name == "$func" ) { // Unfortunately we can't show the function declaration here, because the engine may have released the parameter list already so the declaration would only be misleading // We need to show the function type too as for example delegates do not have a name msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, reinterpret_cast(gcObj.obj)->GetName(), reinterpret_cast(gcObj.obj)->GetFuncType()); engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); } - else if( gcObj.type->name == "_builtin_objecttype_" ) + else if( gcObj.type->name == "$obj" ) { msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast(gcObj.obj)->GetName()); engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); } - else if( gcObj.type->name == "_builtin_globalprop_" ) - { - msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast(gcObj.obj)->name.AddressOf()); - engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf()); - } // Release the reference that the GC holds if the release functions is still available if( gcObj.type->beh.release && engine->scriptFunctions[gcObj.type->beh.release] ) diff --git a/lib/angelscript/source/as_module.cpp b/lib/angelscript/source/as_module.cpp index 232e17be1..2c81e0762 100644 --- a/lib/angelscript/source/as_module.cpp +++ b/lib/angelscript/source/as_module.cpp @@ -46,32 +46,6 @@ 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) @@ -315,7 +289,7 @@ int 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 + // TODO: 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); @@ -944,9 +918,6 @@ int asCModule::GetGlobalVarIndexByName(const char *name) const // 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; @@ -1273,6 +1244,33 @@ int asCModule::AddScriptFunction(asCScriptFunction *func) func->AddRefInternal(); engine->AddScriptFunction(func); + // If the function that is being added is an already compiled shared function + // then it is necessary to look for anonymous functions that may be declared + // within it and add those as well + if( func->isShared && func->funcType == asFUNC_SCRIPT ) + { + // Loop through the byte code and check all the + // asBC_FuncPtr instructions for anonymous functions + asDWORD *bc = func->scriptData->byteCode.AddressOf(); + asUINT bcLength = (asUINT)func->scriptData->byteCode.GetLength(); + for( asUINT n = 0; n < bcLength; ) + { + int c = *(asBYTE*)&bc[n]; + if( c == asBC_FuncPtr ) + { + asCScriptFunction *f = reinterpret_cast(asBC_PTRARG(&bc[n])); + // Anonymous functions start with $ + // There are never two equal anonymous functions so it is not necessary to look for duplicates + if( f && f->name[0] == '$' ) + { + AddScriptFunction(f); + globalFunctions.Put(f); + } + } + n += asBCTypeSize[asBCInfo[c].type]; + } + } + return 0; } @@ -1700,16 +1698,6 @@ 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); @@ -1734,6 +1722,7 @@ int asCModule::AddFuncDef(const asCString &name, asSNameSpace *ns) func->name = name; func->nameSpace = ns; + func->module = this; funcDefs.PushLast(func); diff --git a/lib/angelscript/source/as_objecttype.cpp b/lib/angelscript/source/as_objecttype.cpp index 92ceaece3..8bded269d 100644 --- a/lib/angelscript/source/as_objecttype.cpp +++ b/lib/angelscript/source/as_objecttype.cpp @@ -53,6 +53,7 @@ asCObjectType::asCObjectType() module = 0; derivedFrom = 0; size = 0; + typeId = -1; // start as -1 to signal that it hasn't been defined acceptValueSubType = true; acceptRefSubType = true; @@ -74,6 +75,7 @@ asCObjectType::asCObjectType(asCScriptEngine *engine) this->engine = engine; module = 0; derivedFrom = 0; + typeId = -1; // start as -1 to signal that it hasn't been defined acceptValueSubType = true; acceptRefSubType = true; @@ -241,7 +243,8 @@ void asCObjectType::DestroyInternal() userData.SetLength(0); // Remove the type from the engine - engine->RemoveFromTypeIdMap(this); + if( typeId != -1 ) + engine->RemoveFromTypeIdMap(this); // Clear the engine pointer to mark the object type as invalid engine = 0; @@ -252,7 +255,6 @@ asCObjectType::~asCObjectType() if( engine == 0 ) return; - // TODO: 2.30.0: redesign: Shouldn't this have been done already? DestroyInternal(); } @@ -322,12 +324,18 @@ asUINT asCObjectType::GetSize() const // interface int asCObjectType::GetTypeId() const { - // We need a non const pointer to create the asCDataType object. - // We're not breaking anything here because this function is not - // modifying the object, so this const cast is safe. - asCObjectType *ot = const_cast(this); + if( typeId == -1 ) + { + // We need a non const pointer to create the asCDataType object. + // We're not breaking anything here because this function is not + // modifying the object, so this const cast is safe. + asCObjectType *ot = const_cast(this); - return engine->GetTypeIdFromDataType(asCDataType::CreateObject(ot, false)); + // The engine will define the typeId for this object type + engine->GetTypeIdFromDataType(asCDataType::CreateObject(ot, false)); + } + + return typeId; } // interface diff --git a/lib/angelscript/source/as_objecttype.h b/lib/angelscript/source/as_objecttype.h index 2a72e514f..7c84b7831 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-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 @@ -191,6 +191,7 @@ public: #ifdef WIP_16BYTE_ALIGN int alignment; #endif + mutable int typeId; asCArray properties; asCArray methods; asCArray interfaces; diff --git a/lib/angelscript/source/as_parser.cpp b/lib/angelscript/source/as_parser.cpp index eaa0768f8..fca02fe68 100644 --- a/lib/angelscript/source/as_parser.cpp +++ b/lib/angelscript/source/as_parser.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 @@ -261,6 +261,13 @@ int asCParser::ParsePropertyDeclaration(asCScriptCode *script) scriptNode->AddChildLast(ParseType(true)); if( isSyntaxError ) return -1; + // Allow optional '&' to indicate that the property is indirect, i.e. stored as reference + sToken t; + GetToken(&t); + RewindTo(&t); + if( t.type == ttAmp ) + scriptNode->AddChildLast(ParseToken(ttAmp)); + // Allow optional namespace to be defined before the identifier in case // the declaration is to be used for searching for an existing property ParseOptionalScope(scriptNode); @@ -269,7 +276,6 @@ int asCParser::ParsePropertyDeclaration(asCScriptCode *script) if( isSyntaxError ) return -1; // The declaration should end after the identifier - sToken t; GetToken(&t); if( t.type != ttEnd ) { @@ -402,9 +408,11 @@ asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType, boo if( isSyntaxError ) return node; // If the datatype is a template type, then parse the subtype within the < > + GetToken(&t); + RewindTo(&t); asCScriptNode *type = node->lastChild; tempString.Assign(&script->code[type->tokenPos], type->tokenLength); - if( engine->IsTemplateType(tempString.AddressOf()) ) + if( engine->IsTemplateType(tempString.AddressOf()) && t.type == ttLessThan ) { GetToken(&t); if( t.type != ttLessThan ) @@ -602,7 +610,7 @@ asCScriptNode *asCParser::ParseIdentifier() return node; } -// BNF: PARAMLIST ::= '(' ('void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]}) ')' +// BNF: PARAMLIST ::= '(' ['void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]})] ')' asCScriptNode *asCParser::ParseParameterList() { asCScriptNode *node = CreateNode(snParameterList); @@ -1104,10 +1112,13 @@ bool asCParser::CheckTemplateType(sToken &t) tempString.Assign(&script->code[t.pos], t.length); if( engine->IsTemplateType(tempString.AddressOf()) ) { - // Expect the sub type within < > + // If the next token is a < then parse the sub-type too GetToken(&t); if( t.type != ttLessThan ) - return false; + { + RewindTo(&t); + return true; + } for(;;) { @@ -1232,7 +1243,7 @@ asCScriptNode *asCParser::ParseCast() return node; } -// BNF: EXPRVALUE ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')' +// BNF: EXPRVALUE ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')' | LAMBDA asCScriptNode *asCParser::ParseExprValue() { asCScriptNode *node = CreateNode(snExprValue); @@ -1250,44 +1261,54 @@ asCScriptNode *asCParser::ParseExprValue() node->AddChildLast(ParseConstructCall()); else if( t1.type == ttIdentifier || t1.type == ttScope ) { - // Determine the last identifier in order to check if it is a type - sToken t; - if( t1.type == ttScope ) t = t2; else t = t1; - RewindTo(&t); - GetToken(&t2); - while( t.type == ttIdentifier ) + // Check if the expression is an anonymous function + if( IsLambda() ) { - t2 = t; - GetToken(&t); - if( t.type == ttScope ) - GetToken(&t); - else - break; + node->AddChildLast(ParseLambda()); } - - 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 && (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()); else - node->AddChildLast(ParseVariableAccess()); + { + // Determine the last identifier in order to check if it is a type + sToken t; + if( t1.type == ttScope ) t = t2; else t = t1; + RewindTo(&t); + GetToken(&t2); + while( t.type == ttIdentifier ) + { + t2 = t; + GetToken(&t); + if( t.type == ttScope ) + GetToken(&t); + 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; + } + + GetToken(&t2); + + // Rewind so the real parsing can be done, after deciding what to parse + RewindTo(&t1); + + // Check if this is a construct call + if( isDataType && (t.type == ttOpenParanthesis || // type() + (t.type == ttOpenBracket && t2.type == ttCloseBracket)) ) // type[]() + node->AddChildLast(ParseConstructCall()); + else if( isTemplateType && t.type == ttLessThan ) // type() + node->AddChildLast(ParseConstructCall()); + else if( IsFunctionCall() ) + node->AddChildLast(ParseFunctionCall()); + else + node->AddChildLast(ParseVariableAccess()); + } } else if( t1.type == ttCast ) node->AddChildLast(ParseCast()); @@ -1355,6 +1376,83 @@ asCScriptNode *asCParser::ParseConstant() return node; } +bool asCParser::IsLambda() +{ + bool isLambda = false; + sToken t; + GetToken(&t); + if( t.type == ttIdentifier && IdentifierIs(t, FUNCTION_TOKEN) ) + { + sToken t2; + GetToken(&t2); + if( t2.type == ttOpenParanthesis ) + { + // Skip until ) + while( t2.type != ttCloseParanthesis && t2.type != ttEnd ) + GetToken(&t2); + + // The next token must be a { + GetToken(&t2); + if( t2.type == ttStartStatementBlock ) + isLambda = true; + } + } + + RewindTo(&t); + return isLambda; +} + +// BNF: LAMBDA ::= 'function' '(' [IDENTIFIER {',' IDENTIFIER}] ')' STATBLOCK +asCScriptNode *asCParser::ParseLambda() +{ + asCScriptNode *node = CreateNode(snFunction); + if( node == 0 ) return 0; + + sToken t; + GetToken(&t); + + if( t.type != ttIdentifier || !IdentifierIs(t, FUNCTION_TOKEN) ) + { + Error(ExpectedToken("function"), &t); + return node; + } + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("("), &t); + return node; + } + + GetToken(&t); + if( t.type == ttIdentifier ) + { + RewindTo(&t); + node->AddChildLast(ParseIdentifier()); + + GetToken(&t); + while( t.type == ttListSeparator ) + { + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&t); + } + } + + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")"), &t); + return node; + } + + // We should just find the end of the statement block here. The statements + // will be parsed on request by the compiler once it starts the compilation. + node->AddChildLast(SuperficiallyParseStatementBlock()); + + return node; +} + asCScriptNode *asCParser::ParseStringConstant() { asCScriptNode *node = CreateNode(snConstant); @@ -1618,7 +1716,7 @@ asCScriptNode *asCParser::ParseCondition() return node; } -// BNF: EXPR ::= (TYPE '=' INILIST) | (EXPRTERM {EXPROP EXPRTERM}) +// BNF: EXPR ::= (TYPE '=' INITLIST) | (EXPRTERM {EXPROP EXPRTERM}) asCScriptNode *asCParser::ParseExpression() { asCScriptNode *node = CreateNode(snExpression); @@ -2459,7 +2557,22 @@ bool asCParser::IsVirtualPropertyDecl() // 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( t1.type == ttScope ) + GetToken(&t1); + + if( t1.type == ttIdentifier ) + { + sToken t2; + GetToken(&t2); + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + GetToken(&t1); + GetToken(&t2); + } + + RewindTo(&t2); + } + else if( !IsRealType(t1.type) ) { RewindTo(&t); return false; @@ -3201,61 +3314,37 @@ asCScriptNode *asCParser::SuperficiallyParseVarInit() if( t.type == ttAssignment ) { GetToken(&t); - if( t.type == ttStartStatementBlock ) + sToken start = t; + + // Find the end of the expression + int indentParan = 0; + int indentBrace = 0; + while( indentParan || indentBrace || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) ) { - sToken start = t; - - // Find the end of the initialization list - int indent = 1; - while( indent ) + if( t.type == ttOpenParanthesis ) + indentParan++; + else if( t.type == ttCloseParanthesis ) + indentParan--; + else if( t.type == ttStartStatementBlock ) + indentBrace++; + else if( t.type == ttEndStatementBlock ) + indentBrace--; + else if( t.type == ttNonTerminatedStringConstant ) { - GetToken(&t); - if( t.type == ttStartStatementBlock ) - indent++; - else if( t.type == ttEndStatementBlock ) - indent--; - else if( t.type == ttNonTerminatedStringConstant ) - { - Error(TXT_NONTERMINATED_STRING, &t); - break; - } - else if( t.type == ttEnd ) - { - Error(TXT_UNEXPECTED_END_OF_FILE, &t); - Info(TXT_WHILE_PARSING_INIT_LIST, &start); - break; - } + Error(TXT_NONTERMINATED_STRING, &t); + break; } - } - else - { - sToken start = t; - - // Find the end of the expression - int indent = 0; - while( indent || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) ) + else if( t.type == ttEnd ) { - if( t.type == ttOpenParanthesis ) - indent++; - else if( t.type == ttCloseParanthesis ) - indent--; - else if( t.type == ttNonTerminatedStringConstant ) - { - Error(TXT_NONTERMINATED_STRING, &t); - break; - } - else if( t.type == ttEnd ) - { - Error(TXT_UNEXPECTED_END_OF_FILE, &t); - Info(TXT_WHILE_PARSING_EXPRESSION, &start); - break; - } - GetToken(&t); + Error(TXT_UNEXPECTED_END_OF_FILE, &t); + Info(TXT_WHILE_PARSING_EXPRESSION, &start); + break; } - - // Rewind so that the next token read is the list separator, end statement, or end statement block - RewindTo(&t); + GetToken(&t); } + + // Rewind so that the next token read is the list separator, end statement, or end statement block + RewindTo(&t); } else if( t.type == ttOpenParanthesis ) { @@ -3872,7 +3961,7 @@ asCScriptNode *asCParser::ParseIf() return node; } -// BNF: FOR ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN] ')' STATEMENT +// BNF: FOR ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN {',' ASSIGN}] ')' STATEMENT asCScriptNode *asCParser::ParseFor() { asCScriptNode *node = CreateNode(snFor); @@ -3911,18 +4000,27 @@ asCScriptNode *asCParser::ParseFor() { RewindTo(&t); - asCScriptNode *n = CreateNode(snExpressionStatement); - if( n == 0 ) return 0; - node->AddChildLast(n); - n->AddChildLast(ParseAssignment()); - if( isSyntaxError ) return node; - - GetToken(&t); - if( t.type != ttCloseParanthesis ) + // Parse N increment statements separated by , + for(;;) { - Error(ExpectedToken(")"), &t); - Error(InsteadFound(t), &t); - return node; + asCScriptNode *n = CreateNode(snExpressionStatement); + if( n == 0 ) return 0; + node->AddChildLast(n); + n->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type == ttListSeparator ) + continue; + else if( t.type == ttCloseParanthesis ) + break; + else + { + const char *tokens[] = {",", ")"}; + Error(ExpectedOneOf(tokens, 2), &t); + Error(InsteadFound(t), &t); + return node; + } } } diff --git a/lib/angelscript/source/as_parser.h b/lib/angelscript/source/as_parser.h index 76d74c0ba..06fd93ff8 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-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 @@ -133,6 +133,8 @@ protected: bool IsVarDecl(); bool IsVirtualPropertyDecl(); bool IsFuncDecl(bool isMethod); + bool IsLambda(); + bool IsFunctionCall(); // Expressions asCScriptNode *ParseAssignment(); @@ -151,13 +153,13 @@ protected: asCScriptNode *ParseCast(); asCScriptNode *ParseConstant(); asCScriptNode *ParseStringConstant(); + asCScriptNode *ParseLambda(); bool IsConstant(int tokenType); bool IsOperator(int tokenType); bool IsPreOperator(int tokenType); bool IsPostOperator(int tokenType); bool IsAssignOperator(int tokenType); - bool IsFunctionCall(); bool CheckTemplateType(sToken &t); #endif diff --git a/lib/angelscript/source/as_restore.cpp b/lib/angelscript/source/as_restore.cpp index 19eacc933..4e13157b8 100644 --- a/lib/angelscript/source/as_restore.cpp +++ b/lib/angelscript/source/as_restore.cpp @@ -40,6 +40,7 @@ #include "as_bytecode.h" #include "as_scriptobject.h" #include "as_texts.h" +#include "as_debug.h" BEGIN_AS_NAMESPACE @@ -65,6 +66,8 @@ void asCReader::ReadData(void *data, asUINT size) int asCReader::Read(bool *wasDebugInfoStripped) { + TimeIt("asCReader::Read"); + // Before starting the load, make sure that // any existing resources have been freed module->InternalReset(); @@ -124,6 +127,8 @@ int asCReader::Error(const char *msg) int asCReader::ReadInner() { + TimeIt("asCReader::ReadInner"); + // This function will load each entity one by one from the stream. // If any error occurs, it will return to the caller who is // responsible for cleaning up the partially loaded entities. @@ -258,31 +263,38 @@ int asCReader::ReadInner() asCScriptFunction *func = ReadFunction(isNew, false, true); if( func ) { + func->module = module; module->funcDefs.PushLast(func); engine->funcDefs.PushLast(func); // TODO: clean up: This is also done by the builder. It should probably be moved to a method in the module // Check if there is another identical funcdef from another module and if so reuse that instead - for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) + if( func->isShared ) { - asCScriptFunction *f2 = engine->funcDefs[n]; - if( f2 == 0 || func == f2 ) - continue; - - if( f2->name == func->name && - f2->nameSpace == func->nameSpace && - f2->IsSignatureExceptNameEqual(func) ) + for( asUINT n = 0; n < engine->funcDefs.GetLength(); n++ ) { - // Replace our funcdef for the existing one - module->funcDefs[module->funcDefs.IndexOf(func)] = f2; - f2->AddRefInternal(); + asCScriptFunction *f2 = engine->funcDefs[n]; + if( f2 == 0 || func == f2 ) + continue; - engine->funcDefs.RemoveValue(func); + if( !f2->isShared ) + continue; - savedFunctions[savedFunctions.IndexOf(func)] = f2; + if( f2->name == func->name && + f2->nameSpace == func->nameSpace && + f2->IsSignatureExceptNameEqual(func) ) + { + // Replace our funcdef for the existing one + module->funcDefs[module->funcDefs.IndexOf(func)] = f2; + f2->AddRefInternal(); - func->ReleaseInternal(); - break; + engine->funcDefs.RemoveValue(func); + + savedFunctions[savedFunctions.IndexOf(func)] = f2; + + func->ReleaseInternal(); + break; + } } } } @@ -549,6 +561,8 @@ int asCReader::ReadInner() void asCReader::ReadUsedStringConstants() { + TimeIt("asCReader::ReadUsedStringConstants"); + asCString str; asUINT count; @@ -563,6 +577,8 @@ void asCReader::ReadUsedStringConstants() void asCReader::ReadUsedFunctions() { + TimeIt("asCReader::ReadUsedFunctions"); + asUINT count; count = ReadEncodedUInt(); usedFunctions.SetLength(count); @@ -606,25 +622,44 @@ void asCReader::ReadUsedFunctions() for( asUINT i = 0; i < module->bindInformations.GetLength(); i++ ) { asCScriptFunction *f = module->bindInformations[i]->importedFunctionSignature; - if( !func.IsSignatureEqual(f) || - func.objectType != f->objectType || + if( func.objectType != f->objectType || func.funcType != f->funcType || - func.nameSpace != f->nameSpace ) + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) continue; usedFunctions[n] = f; break; } } + else if( func.funcType == asFUNC_FUNCDEF ) + { + const asCArray &funcs = module->funcDefs; + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = funcs[i]; + if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + // Funcdefs are always global so there is no need to compare object type + asASSERT( f->objectType == 0 ); + + usedFunctions[n] = f; + break; + } + } else { + // TODO: optimize: Global functions should be searched for in module->globalFunctions + // TODO: optimize: funcdefs should be searched for in module->funcDefs + // TODO: optimize: object methods should be searched for directly in the object type for( asUINT i = 0; i < module->scriptFunctions.GetLength(); i++ ) { asCScriptFunction *f = module->scriptFunctions[i]; - if( !func.IsSignatureEqual(f) || - func.objectType != f->objectType || + if( func.objectType != f->objectType || func.funcType != f->funcType || - func.nameSpace != f->nameSpace ) + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) continue; usedFunctions[n] = f; @@ -634,17 +669,157 @@ void asCReader::ReadUsedFunctions() } else { - for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ ) + if( func.funcType == asFUNC_FUNCDEF ) { - asCScriptFunction *f = engine->scriptFunctions[i]; - if( f == 0 || - !func.IsSignatureEqual(f) || - func.objectType != f->objectType || - func.nameSpace != f->nameSpace ) - continue; + // This is a funcdef (registered or shared) + const asCArray &funcs = engine->funcDefs; + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = funcs[i]; + if( f == 0 || func.name != f->name || !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; - usedFunctions[n] = f; - break; + // Funcdefs are always global so there is no need to compare object type + asASSERT( f->objectType == 0 ); + + usedFunctions[n] = f; + break; + } + } + else if( func.name[0] == '$' ) + { + // This is a special function + + // Check for string factory + if( func.name == "$str" && engine->stringFactory && + func.IsSignatureExceptNameAndObjectTypeEqual(engine->stringFactory) ) + usedFunctions[n] = engine->stringFactory; + else if( func.name == "$beh0" && func.objectType ) + { + // This is a class constructor, so we can search directly in the object type's constructors + for( asUINT i = 0; i < func.objectType->beh.constructors.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[func.objectType->beh.constructors[i]]; + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + else if( func.name == "$fact" || func.name == "$beh3" ) + { + // This is a factory (or stub), so look for the function in the return type's factories + asCObjectType *objType = func.returnType.GetObjectType(); + if( objType ) + { + for( asUINT i = 0; i < objType->beh.factories.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.factories[i]]; + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + } + else if( func.name == "$list" ) + { + // listFactory is used for both factory is global and returns a handle and constructor that is a method + asCObjectType *objType = func.objectType ? func.objectType : func.returnType.GetObjectType(); + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$beh2" ) + { + // This is a destructor, so check the object type's destructor + asCObjectType *objType = func.objectType; + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.destruct]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$beh4" ) + { + // This is a list factory, so check the return type's list factory + asCObjectType *objType = func.returnType.GetObjectType(); + if( objType ) + { + asCScriptFunction *f = engine->scriptFunctions[objType->beh.listFactory]; + if( f && func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + usedFunctions[n] = f; + } + } + else if( func.name == "$dlgte" ) + { + // This is the delegate factory + asCScriptFunction *f = engine->registeredGlobalFuncs.GetFirst(engine->nameSpaces[0], DELEGATE_FACTORY); + asASSERT( f && func.IsSignatureEqual(f) ); + usedFunctions[n] = f; + } + } + else if( func.objectType == 0 ) + { + // This is a global function + const asCArray &funcs = engine->registeredGlobalFuncs.GetIndexes(func.nameSpace, func.name); + for( asUINT i = 0; i < funcs.GetLength(); i++ ) + { + asCScriptFunction *f = engine->registeredGlobalFuncs.Get(funcs[i]); + if( f == 0 || + !func.IsSignatureExceptNameAndObjectTypeEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + else if( func.objectType ) + { + // It is a class member, so we can search directly in the object type's members + // TODO: virtual function is different that implemented method + for( asUINT i = 0; i < func.objectType->methods.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[func.objectType->methods[i]]; + if( f == 0 || + !func.IsSignatureEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + } + + if( usedFunctions[n] == 0 ) + { + // TODO: clean up: This part of the code should never happen. All functions should + // be found in the above logic. The only valid reason to come here + // is if the bytecode is wrong and the function doesn't exist anyway. + // This loop is kept temporarily until we can be certain all scenarios + // are covered. + for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[i]; + if( f == 0 || + func.objectType != f->objectType || + func.nameSpace != f->nameSpace || + !func.IsSignatureEqual(f) ) + continue; + + usedFunctions[n] = f; + break; + } + + // No function is expected to be found + asASSERT(usedFunctions[n] == 0); } } @@ -973,6 +1148,13 @@ asCScriptFunction *asCReader::ReadFunction(bool &isNew, bool addToModule, bool a { func->vfTableIdx = ReadEncodedUInt(); } + else if( func->funcType == asFUNC_FUNCDEF ) + { + asBYTE bits; + ReadData(&bits, 1); + if( bits ) + func->isShared = true; + } if( addToModule ) { @@ -1520,28 +1702,25 @@ asQWORD asCReader::ReadEncodedUInt64() void asCReader::ReadString(asCString* str) { - char b; - ReadData(&b, 1); - if( b == '\0' ) + asUINT len = ReadEncodedUInt(); + if( len & 1 ) { - str->SetLength(0); + asUINT idx = len/2; + if( idx < savedStrings.GetLength() ) + *str = savedStrings[idx]; + else + Error(TXT_INVALID_BYTECODE_d); } - else if( b == 'n' ) + else if( len > 0 ) { - asUINT len = ReadEncodedUInt(); + len /= 2; str->SetLength(len); stream->Read(str->AddressOf(), len); savedStrings.PushLast(*str); } else - { - asUINT n = ReadEncodedUInt(); - if( n < savedStrings.GetLength() ) - *str = savedStrings[n]; - else - Error(TXT_INVALID_BYTECODE_d); - } + str->SetLength(0); } void asCReader::ReadGlobalProperty() @@ -1625,7 +1804,7 @@ void asCReader::ReadDataType(asCDataType *dt) ReadData(&bits, 1); asCScriptFunction *funcDef = 0; - if( tokenType == ttIdentifier && objType && objType->name == "_builtin_function_" ) + if( tokenType == ttIdentifier && objType && objType->name == "$func" ) { asCScriptFunction func(engine, module, asFUNC_DUMMY); ReadFunctionSignature(&func); @@ -1790,7 +1969,7 @@ asCObjectType* asCReader::ReadObjectType() ReadString(&ns); asSNameSpace *nameSpace = engine->AddNameSpace(ns.AddressOf()); - if( typeName.GetLength() && typeName != "_builtin_object_" && typeName != "_builtin_function_" ) + if( typeName.GetLength() && typeName != "$obj" && typeName != "$func" ) { // Find the object type ot = module->GetObjectType(typeName.AddressOf(), nameSpace); @@ -1806,11 +1985,11 @@ asCObjectType* asCReader::ReadObjectType() return 0; } } - else if( typeName == "_builtin_object_" ) + else if( typeName == "$obj" ) { ot = &engine->scriptTypeBehaviours; } - else if( typeName == "_builtin_function_" ) + else if( typeName == "$func" ) { ot = &engine->functionBehaviours; } @@ -2073,6 +2252,8 @@ void asCReader::ReadByteCode(asCScriptFunction *func) void asCReader::ReadUsedTypeIds() { + TimeIt("asCReader::ReadUsedTypeIds"); + asUINT count = ReadEncodedUInt(); usedTypeIds.Allocate(count, false); for( asUINT n = 0; n < count; n++ ) @@ -2085,6 +2266,8 @@ void asCReader::ReadUsedTypeIds() void asCReader::ReadUsedGlobalProps() { + TimeIt("asCReader::ReadUsedGlobalProps"); + int c = ReadEncodedUInt(); usedGlobalProperties.Allocate(c, false); @@ -2124,6 +2307,8 @@ void asCReader::ReadUsedGlobalProps() void asCReader::ReadUsedObjectProps() { + TimeIt("asCReader::ReadUsedObjectProps"); + asUINT c = ReadEncodedUInt(); usedObjectProperties.SetLength(c); @@ -2293,7 +2478,8 @@ void asCReader::TranslateFunction(asCScriptFunction *func) } else if( c == asBC_CALL || c == asBC_CALLINTF || - c == asBC_CALLSYS ) + c == asBC_CALLSYS || + c == asBC_Thiscall1 ) { // Translate the index to the func id int *fid = (int*)&bc[n+1]; @@ -2800,7 +2986,8 @@ void asCReader::CalculateStackNeeded(asCScriptFunction *func) { // Determine the true delta from the instruction arguments if( bc == asBC_CALL || - bc == asBC_CALLSYS || + bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || bc == asBC_CALLBND || bc == asBC_ALLOC || bc == asBC_CALLINTF || @@ -3030,6 +3217,7 @@ asCScriptFunction *asCReader::GetCalledFunction(asCScriptFunction *func, asDWORD if( bc == asBC_CALL || bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || bc == asBC_CALLINTF ) { // Find the function from the function id in bytecode @@ -3085,11 +3273,13 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog // Find out which function that will be called asCScriptFunction *calledFunc = 0; + int stackDelta = 0; for( asUINT n = programPos; func->scriptData->byteCode.GetLength(); ) { asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n]; if( bc == asBC_CALL || bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || bc == asBC_CALLINTF || bc == asBC_ALLOC || bc == asBC_CALLBND || @@ -3106,6 +3296,10 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog return offset - (1 - AS_PTR_SIZE); } + // Keep track of the stack size between the + // instruction that needs to be adjusted and the call + stackDelta += asBCInfo[bc].stackInc; + n += asBCTypeSize[asBCInfo[bc].type]; } @@ -3118,16 +3312,30 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog // Count the number of pointers pushed on the stack above the // current offset, and then adjust the offset accordingly asUINT numPtrs = 0; - int currOffset = 0; + int currOffset = -stackDelta; if( offset > currOffset && calledFunc->GetObjectType() ) { - numPtrs++; currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif } if( offset > currOffset && calledFunc->DoesReturnOnStack() ) { - numPtrs++; currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif } for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ ) { @@ -3136,8 +3344,15 @@ int asCReader::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog if( !calledFunc->parameterTypes[p].IsPrimitive() || calledFunc->parameterTypes[p].IsReference() ) { - numPtrs++; currOffset++; + if( currOffset > 0 ) + numPtrs++; +#if AS_PTR_SIZE == 2 + // For 64bit platforms it is necessary to increment the currOffset by one more + // DWORD since the stackDelta was counting the full 64bit size of the pointer + else if( stackDelta ) + currOffset++; +#endif // The variable arg ? has an additiona 32bit integer with the typeid if( calledFunc->parameterTypes[p].IsAnyType() ) @@ -3197,6 +3412,8 @@ void asCWriter::WriteData(const void *data, asUINT size) int asCWriter::Write() { + TimeIt("asCWriter::Write"); + unsigned long i, count; // Store everything in the same order that the builder parses scripts @@ -3207,101 +3424,149 @@ int asCWriter::Write() WriteData(&stripDebugInfo, sizeof(stripDebugInfo)); // Store enums - count = (asUINT)module->enumTypes.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; i++ ) { - WriteObjectTypeDeclaration(module->enumTypes[i], 1); - WriteObjectTypeDeclaration(module->enumTypes[i], 2); + TimeIt("store enums"); + + count = (asUINT)module->enumTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + WriteObjectTypeDeclaration(module->enumTypes[i], 1); + WriteObjectTypeDeclaration(module->enumTypes[i], 2); + } } // Store type declarations first - count = (asUINT)module->classTypes.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; i++ ) { - // Store only the name of the class/interface types - WriteObjectTypeDeclaration(module->classTypes[i], 1); + TimeIt("type declarations"); + + count = (asUINT)module->classTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + // Store only the name of the class/interface types + WriteObjectTypeDeclaration(module->classTypes[i], 1); + } } // Store func defs - count = (asUINT)module->funcDefs.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; i++ ) - WriteFunction(module->funcDefs[i]); + { + TimeIt("func defs"); + + count = (asUINT)module->funcDefs.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + WriteFunction(module->funcDefs[i]); + } // Now store all interface methods - count = (asUINT)module->classTypes.GetLength(); - for( i = 0; i < count; i++ ) { - if( module->classTypes[i]->IsInterface() ) - WriteObjectTypeDeclaration(module->classTypes[i], 2); + TimeIt("interface methods"); + + count = (asUINT)module->classTypes.GetLength(); + for( i = 0; i < count; i++ ) + { + if( module->classTypes[i]->IsInterface() ) + WriteObjectTypeDeclaration(module->classTypes[i], 2); + } } // Then store the class methods and behaviours - for( i = 0; i < count; ++i ) { - if( !module->classTypes[i]->IsInterface() ) - WriteObjectTypeDeclaration(module->classTypes[i], 2); + TimeIt("class methods and behaviours"); + + for( i = 0; i < count; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + WriteObjectTypeDeclaration(module->classTypes[i], 2); + } } // Then store the class properties - for( i = 0; i < count; ++i ) { - if( !module->classTypes[i]->IsInterface() ) - WriteObjectTypeDeclaration(module->classTypes[i], 3); + TimeIt("class properties"); + + for( i = 0; i < count; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + WriteObjectTypeDeclaration(module->classTypes[i], 3); + } } // Store typedefs - count = (asUINT)module->typeDefs.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; i++ ) { - WriteObjectTypeDeclaration(module->typeDefs[i], 1); - WriteObjectTypeDeclaration(module->typeDefs[i], 2); + TimeIt("type defs"); + + count = (asUINT)module->typeDefs.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; i++ ) + { + WriteObjectTypeDeclaration(module->typeDefs[i], 1); + WriteObjectTypeDeclaration(module->typeDefs[i], 2); + } } // scriptGlobals[] - count = (asUINT)module->scriptGlobals.GetSize(); - WriteEncodedInt64(count); - asCSymbolTable::iterator it = module->scriptGlobals.List(); - for( ; it; it++ ) - WriteGlobalProperty(*it); + { + TimeIt("script globals"); + + count = (asUINT)module->scriptGlobals.GetSize(); + WriteEncodedInt64(count); + asCSymbolTable::iterator it = module->scriptGlobals.List(); + for( ; it; it++ ) + WriteGlobalProperty(*it); + } // scriptFunctions[] - count = 0; - for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) - if( module->scriptFunctions[i]->objectType == 0 ) - count++; - WriteEncodedInt64(count); - for( i = 0; i < module->scriptFunctions.GetLength(); ++i ) - if( module->scriptFunctions[i]->objectType == 0 ) - WriteFunction(module->scriptFunctions[i]); + { + TimeIt("scriptFunctions"); + + count = 0; + for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) + if( module->scriptFunctions[i]->objectType == 0 ) + count++; + WriteEncodedInt64(count); + for( i = 0; i < module->scriptFunctions.GetLength(); ++i ) + if( module->scriptFunctions[i]->objectType == 0 ) + WriteFunction(module->scriptFunctions[i]); + } // globalFunctions[] - count = (int)module->globalFunctions.GetSize(); - asCSymbolTable::iterator funcIt = module->globalFunctions.List(); - WriteEncodedInt64(count); - while( funcIt ) { - WriteFunction(*funcIt); - funcIt++; + TimeIt("globalFunctions"); + + count = (int)module->globalFunctions.GetSize(); + asCSymbolTable::iterator funcIt = module->globalFunctions.List(); + WriteEncodedInt64(count); + while( funcIt ) + { + WriteFunction(*funcIt); + funcIt++; + } } // bindInformations[] - count = (asUINT)module->bindInformations.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; ++i ) { - WriteFunction(module->bindInformations[i]->importedFunctionSignature); - WriteString(&module->bindInformations[i]->importFromModule); + TimeIt("bindInformations"); + + count = (asUINT)module->bindInformations.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + { + WriteFunction(module->bindInformations[i]->importedFunctionSignature); + WriteString(&module->bindInformations[i]->importFromModule); + } } // usedTypes[] - count = (asUINT)usedTypes.GetLength(); - WriteEncodedInt64(count); - for( i = 0; i < count; ++i ) - WriteObjectType(usedTypes[i]); + { + TimeIt("usedTypes"); + + count = (asUINT)usedTypes.GetLength(); + WriteEncodedInt64(count); + for( i = 0; i < count; ++i ) + WriteObjectType(usedTypes[i]); + } // usedTypeIds[] WriteUsedTypeIds(); @@ -3335,6 +3600,8 @@ int asCWriter::FindStringConstantIndex(int id) void asCWriter::WriteUsedStringConstants() { + TimeIt("asCWriter::WriteUsedStringConstants"); + asUINT count = (asUINT)usedStringConstants.GetLength(); WriteEncodedInt64(count); for( asUINT i = 0; i < count; ++i ) @@ -3343,6 +3610,8 @@ void asCWriter::WriteUsedStringConstants() void asCWriter::WriteUsedFunctions() { + TimeIt("asCWriter::WriteUsedFunctions"); + asUINT count = (asUINT)usedFunctions.GetLength(); WriteEncodedInt64(count); @@ -3572,6 +3841,12 @@ void asCWriter::WriteFunction(asCScriptFunction* func) // TODO: Do we really need to store this? It can probably be reconstructed by the reader WriteEncodedInt64(func->vfTableIdx); } + else if( func->funcType == asFUNC_FUNCDEF ) + { + char bits = 0; + bits += func->isShared ? 1 : 0; + WriteData(&bits,1); + } } void asCWriter::WriteObjectTypeDeclaration(asCObjectType *ot, int phase) @@ -3755,39 +4030,28 @@ void asCWriter::WriteEncodedInt64(asINT64 i) void asCWriter::WriteString(asCString* str) { - // TODO: All strings should be stored in a separate section, and when - // they are used an offset into that section should be stored. - // This will make it unnecessary to store the extra byte to - // identify new versus old strings. - - if( str->GetLength() == 0 ) - { - char z = '\0'; - WriteData(&z, 1); - return; - } - // First check if the string hasn't been saved already asSMapNode *cursor = 0; if (stringToIdMap.MoveTo(&cursor, asCStringPointer(str))) { // Save a reference to the existing string - char b = 'r'; - WriteData(&b, 1); - WriteEncodedInt64(cursor->value); + // The lowest bit is set to 1 to indicate a reference + WriteEncodedInt64(cursor->value*2+1); return; } // Save a new string - char b = 'n'; - WriteData(&b, 1); - + // The lowest bit is set to 0 to indicate a new string asUINT len = (asUINT)str->GetLength(); - WriteEncodedInt64(len); - stream->Write(str->AddressOf(), (asUINT)len); + WriteEncodedInt64(len*2); - savedStrings.PushLast(*str); - stringToIdMap.Insert(asCStringPointer(str), int(savedStrings.GetLength()) - 1); + if( len > 0 ) + { + stream->Write(str->AddressOf(), (asUINT)len); + + savedStrings.PushLast(*str); + stringToIdMap.Insert(asCStringPointer(str), int(savedStrings.GetLength()) - 1); + } } void asCWriter::WriteGlobalProperty(asCGlobalProperty* prop) @@ -3851,7 +4115,7 @@ void asCWriter::WriteDataType(const asCDataType *dt) bits.isReadOnly = dt->IsReadOnly(); WriteData(&bits, 1); - if( t == ttIdentifier && dt->GetObjectType()->name == "_builtin_function_" ) + if( t == ttIdentifier && dt->GetObjectType()->name == "$func" ) { WriteFunctionSignature(dt->GetFuncDef()); } @@ -3883,17 +4147,17 @@ void asCWriter::WriteObjectType(asCObjectType* ot) WriteEncodedInt64(ot->templateSubTypes.GetLength()); for( asUINT n = 0; n < ot->templateSubTypes.GetLength(); n++ ) { - if( ot->templateSubTypes[0].IsObject() || ot->templateSubTypes[0].IsEnumType() ) + if( ot->templateSubTypes[n].IsObject() || ot->templateSubTypes[n].IsEnumType() ) { ch = 's'; WriteData(&ch, 1); - WriteDataType(&ot->templateSubTypes[0]); + WriteDataType(&ot->templateSubTypes[n]); } else { ch = 't'; WriteData(&ch, 1); - eTokenType t = ot->templateSubTypes[0].GetTokenType(); + eTokenType t = ot->templateSubTypes[n].GetTokenType(); WriteEncodedInt64(t); } } @@ -4053,11 +4317,13 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog // Find out which function that will be called asCScriptFunction *calledFunc = 0; + int stackDelta = 0; for( asUINT n = programPos; n < func->scriptData->byteCode.GetLength(); ) { asBYTE bc = *(asBYTE*)&func->scriptData->byteCode[n]; if( bc == asBC_CALL || bc == asBC_CALLSYS || + bc == asBC_Thiscall1 || bc == asBC_CALLINTF ) { // Find the function from the function id in bytecode @@ -4120,6 +4386,10 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog return offset + (1 - AS_PTR_SIZE); } + // Keep track of the stack size between the + // instruction that needs to be adjusted and the call + stackDelta += asBCInfo[bc].stackInc; + n += asBCTypeSize[asBCInfo[bc].type]; } @@ -4128,16 +4398,18 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog // Count the number of pointers pushed on the stack above the // current offset, and then adjust the offset accordingly asUINT numPtrs = 0; - int currOffset = 0; + int currOffset = -stackDelta; if( offset > currOffset && calledFunc->GetObjectType() ) { - numPtrs++; currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; } if( offset > currOffset && calledFunc->DoesReturnOnStack() ) { - numPtrs++; currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; } for( asUINT p = 0; p < calledFunc->parameterTypes.GetLength(); p++ ) { @@ -4147,8 +4419,9 @@ int asCWriter::AdjustGetOffset(int offset, asCScriptFunction *func, asDWORD prog calledFunc->parameterTypes[p].IsReference() ) { // objects and references are passed by pointer - numPtrs++; currOffset += AS_PTR_SIZE; + if( currOffset > 0 ) + numPtrs++; // The variable arg ? has an additional 32bit int with the typeid if( calledFunc->parameterTypes[p].IsAnyType() ) @@ -4262,7 +4535,8 @@ void asCWriter::WriteByteCode(asCScriptFunction *func) } else if( c == asBC_CALL || // DW_ARG c == asBC_CALLINTF || // DW_ARG - c == asBC_CALLSYS ) // DW_ARG + c == asBC_CALLSYS || // DW_ARG + c == asBC_Thiscall1 ) // DW_ARG { // Translate the function id *(int*)(tmp+1) = FindFunctionIndex(engine->scriptFunctions[*(int*)(tmp+1)]); @@ -4772,6 +5046,8 @@ void asCWriter::SListAdjuster::SetNextType(int typeId) void asCWriter::WriteUsedTypeIds() { + TimeIt("asCWriter::WriteUsedTypeIds"); + asUINT count = (asUINT)usedTypeIds.GetLength(); WriteEncodedInt64(count); for( asUINT n = 0; n < count; n++ ) @@ -4792,6 +5068,8 @@ int asCWriter::FindGlobalPropPtrIndex(void *ptr) void asCWriter::WriteUsedGlobalProps() { + TimeIt("asCWriter::WriteUsedGlobalProps"); + int c = (int)usedGlobalProperties.GetLength(); WriteEncodedInt64(c); @@ -4799,32 +5077,12 @@ void asCWriter::WriteUsedGlobalProps() { asPWORD *p = (asPWORD*)usedGlobalProperties[n]; - // First search for the global in the module - char moduleProp = 0; + // Find the property descriptor from the address asCGlobalProperty *prop = 0; - asCSymbolTable::iterator it = module->scriptGlobals.List(); - for( ; it; it++ ) + asSMapNode *cursor; + if( engine->varAddressMap.MoveTo(&cursor, p) ) { - if( p == (*it)->GetAddressOfValue() ) - { - prop = (*it); - moduleProp = 1; - break; - } - } - - // If it is not in the module, it must be an application registered property - if( !prop ) - { - asCSymbolTable::iterator it = engine->registeredGlobalProps.List(); - for( ; it; it++ ) - { - if( it->GetAddressOfValue() == p ) - { - prop = *it; - break; - } - } + prop = engine->varAddressMap.GetValue(cursor); } asASSERT(prop); @@ -4835,12 +5093,17 @@ void asCWriter::WriteUsedGlobalProps() WriteDataType(&prop->type); // Also store whether the property is a module property or a registered property + char moduleProp = 0; + if( prop->realAddress == 0 ) + moduleProp = 1; WriteData(&moduleProp, 1); } } void asCWriter::WriteUsedObjectProps() { + TimeIt("asCWriter::WriteUsedObjectProps"); + int c = (int)usedObjectProperties.GetLength(); WriteEncodedInt64(c); diff --git a/lib/angelscript/source/as_scriptengine.cpp b/lib/angelscript/source/as_scriptengine.cpp index 85cf0cd75..ecc489840 100644 --- a/lib/angelscript/source/as_scriptengine.cpp +++ b/lib/angelscript/source/as_scriptengine.cpp @@ -118,6 +118,9 @@ AS_API const char * asGetLibraryOptions() #ifdef WIP_16BYTE_ALIGN "WIP_16BYTE_ALIGN " #endif +#ifdef AS_BIG_ENDIAN + "AS_BIG_ENDIAN " +#endif // Target system #ifdef AS_WIN @@ -584,21 +587,22 @@ asCScriptEngine::asCScriptEngine() // Reserve function id 0 for no function scriptFunctions.PushLast(0); + // Reserve the first typeIds for the primitive types + typeIdSeqNbr = asTYPEID_DOUBLE + 1; + // Make sure typeId for the built-in primitives are defined according to asETypeIdFlags - int id = 0; - UNUSED_VAR(id); // It is only used in debug mode - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)); asASSERT( id == asTYPEID_VOID ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)); asASSERT( id == asTYPEID_BOOL ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)); asASSERT( id == asTYPEID_INT8 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)); asASSERT( id == asTYPEID_INT16 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)); asASSERT( id == asTYPEID_INT32 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)); asASSERT( id == asTYPEID_INT64 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)); asASSERT( id == asTYPEID_UINT8 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)); asASSERT( id == asTYPEID_UINT16 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)); asASSERT( id == asTYPEID_UINT32 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)); asASSERT( id == asTYPEID_UINT64 ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)); asASSERT( id == asTYPEID_FLOAT ); - id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)); asASSERT( id == asTYPEID_DOUBLE ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)) == asTYPEID_VOID ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)) == asTYPEID_BOOL ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)) == asTYPEID_INT8 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)) == asTYPEID_INT16 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)) == asTYPEID_INT32 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)) == asTYPEID_INT64 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)) == asTYPEID_UINT8 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)) == asTYPEID_UINT16 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)) == asTYPEID_UINT32 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)) == asTYPEID_UINT64 ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)) == asTYPEID_FLOAT ); + asASSERT( GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)) == asTYPEID_DOUBLE ); defaultArrayObjectType = 0; @@ -608,9 +612,9 @@ asCScriptEngine::asCScriptEngine() 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. + // TODO: 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(); @@ -646,7 +650,7 @@ void asCScriptEngine::DeleteDiscardedModules() asCScriptEngine::~asCScriptEngine() { - // TODO: 2.30.0: redesign: Clean up redundant code + // TODO: clean-up: Clean up redundant code asUINT n = 0; inDestructor = true; @@ -690,12 +694,8 @@ asCScriptEngine::~asCScriptEngine() if( refCount.get() > 0 ) WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ENGINE_REF_COUNT_ERROR_DURING_SHUTDOWN); - asSMapNode *cursor = 0; - while( mapTypeIdToDataType.MoveFirst(&cursor) ) - { - asDELETE(mapTypeIdToDataType.GetValue(cursor),asCDataType); - mapTypeIdToDataType.Erase(cursor); - } + mapTypeIdToObjectType.EraseAll(); + mapTypeIdToFunction.EraseAll(); // First remove what is not used, so that other groups can be deleted safely defaultGroup.RemoveConfiguration(this, true); @@ -1976,7 +1976,7 @@ int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, as listPattern->Destroy(this); return ConfigError(asINVALID_DECLARATION, "RegisterObjectBehaviour", objectType->name.AddressOf(), decl); } - func.name.Format("_beh_%d_", behaviour); + func.name.Format("$beh%d", behaviour); if( behaviour != asBEHAVE_FACTORY && behaviour != asBEHAVE_LIST_FACTORY ) { @@ -3229,7 +3229,7 @@ int asCScriptEngine::RegisterStringFactory(const char *datatype, const asSFuncPt return ConfigError(asOUT_OF_MEMORY, "RegisterStringFactory", datatype, 0); } - func->name = "_string_factory_"; + func->name = "$str"; func->sysFuncIntf = newInterface; asCBuilder bld(this, 0); @@ -3590,6 +3590,9 @@ asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateT if( templateType->beh.listFactory ) { asCScriptFunction *func = GenerateTemplateFactoryStub(templateType, ot, templateType->beh.listFactory); + + // Rename the function to easily identify it in LoadByteCode + func->name = "$list"; ot->beh.listFactory = func->id; } @@ -3787,7 +3790,7 @@ asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *t func->funcType = asFUNC_SCRIPT; func->AllocateScriptFunctionData(); - func->name = "factstub"; + func->name = "$fact"; func->id = GetNextScriptFunctionId(); AddScriptFunction(func); @@ -4230,6 +4233,62 @@ void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int func) const #endif } +void *asCScriptEngine::CallObjectMethodRetPtr(void *obj, int param1, asCScriptFunction *func) const +{ + asASSERT( func != 0 ); + asSSystemFunctionInterface *i = func->sysFuncIntf; + +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL || i->callConv == ICC_VIRTUAL_THISCALL ) + { +#if defined(__GNUC__) || defined(AS_PSVITA) + // For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; + } f; + } p; + p.f.func = (asFUNCTION_t)(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + void *(asCSimpleDummy::*f)(int) = (void *(asCSimpleDummy::*)(int))(p.mthd); + return (((asCSimpleDummy*)obj)->*f)(param1); +#else + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (asFUNCTION_t)(i->func); + void *(asCSimpleDummy::*f)(int) = (void *(asCSimpleDummy::*)(int))p.mthd; + obj = (void*)(asPWORD(obj) + i->baseOffset); + return (((asCSimpleDummy*)obj)->*f)(param1); +#endif + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(const_cast(this), func, obj, reinterpret_cast(¶m1)); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void **)gen.GetReturnPointer(); + } + else if( i->callConv == ICC_CDECL_OBJLAST ) + { + void *(*f)(int, void *) = (void *(*)(int, void *))(i->func); + return f(param1, obj); + } + else /*if( i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void *(*f)(void *, int) = (void *(*)(void *, int))(i->func); + return f(obj, param1); + } +} + void *asCScriptEngine::CallGlobalFunctionRetPtr(int func) const { asCScriptFunction *s = scriptFunctions[func]; @@ -4496,82 +4555,154 @@ void asCScriptEngine::GCEnumCallback(void *reference) } -// TODO: multithread: The mapTypeIdToDataType must be protected with critical sections in all functions that access it int asCScriptEngine::GetTypeIdFromDataType(const asCDataType &dtIn) const { - if( dtIn.IsNullHandle() ) return 0; + if( dtIn.IsNullHandle() ) return asTYPEID_VOID; - // Register the base form - asCDataType dt(dtIn); - if( dt.GetObjectType() ) - dt.MakeHandle(false); - - // Find the existing type id - asSMapNode *cursor = 0; - mapTypeIdToDataType.MoveFirst(&cursor); - while( cursor ) + if( dtIn.GetObjectType() == 0 ) { - if( mapTypeIdToDataType.GetValue(cursor)->IsEqualExceptRefAndConst(dt) ) + // Primitives have pre-fixed typeIds + switch( dtIn.GetTokenType() ) { - int typeId = mapTypeIdToDataType.GetKey(cursor); - if( dtIn.GetObjectType() && !(dtIn.GetObjectType()->flags & asOBJ_ASHANDLE) ) + case ttVoid: return asTYPEID_VOID; + case ttBool: return asTYPEID_BOOL; + case ttInt8: return asTYPEID_INT8; + case ttInt16: return asTYPEID_INT16; + case ttInt: return asTYPEID_INT32; + case ttInt64: return asTYPEID_INT64; + case ttUInt8: return asTYPEID_UINT8; + case ttUInt16: return asTYPEID_UINT16; + case ttUInt: return asTYPEID_UINT32; + case ttUInt64: return asTYPEID_UINT64; + case ttFloat: return asTYPEID_FLOAT; + case ttDouble: return asTYPEID_DOUBLE; + default: + // All types should be covered by the above. The variable type is not really a type + asASSERT(dtIn.GetTokenType() == ttQuestion); + return -1; + } + } + + int typeId = -1; + asCObjectType *ot = dtIn.GetObjectType(); + if( ot != &functionBehaviours ) + { + // Object's hold the typeId themselves + typeId = ot->typeId; + + if( typeId == -1 ) + { + ACQUIREEXCLUSIVE(engineRWLock); + // Make sure another thread didn't determine the typeId while we were waiting for the lock + if( ot->typeId == -1 ) { - // 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; - if( dtIn.IsHandleToConst() ) - typeId |= asTYPEID_HANDLETOCONST; + typeId = typeIdSeqNbr++; + if( ot->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT; + else if( ot->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_TEMPLATE; + else if( ot->flags & asOBJ_ENUM ) {} // TODO: Should we have a specific bit for this? + else typeId |= asTYPEID_APPOBJECT; + + ot->typeId = typeId; + + mapTypeIdToObjectType.Insert(typeId, ot); + } + RELEASEEXCLUSIVE(engineRWLock); + } + } + else + { + // This a funcdef, so we'll need to look in the map for the funcdef + + // TODO: optimize: It shouldn't be necessary to exclusive lock when the typeId already exists + ACQUIREEXCLUSIVE(engineRWLock); + + // Find the existing type id + asCScriptFunction *func = dtIn.GetFuncDef(); + asASSERT(func); + asSMapNode *cursor = 0; + mapTypeIdToFunction.MoveFirst(&cursor); + while( cursor ) + { + if( mapTypeIdToFunction.GetValue(cursor) == func ) + { + typeId = mapTypeIdToFunction.GetKey(cursor); + break; } - return typeId; + mapTypeIdToFunction.MoveNext(&cursor, cursor); } - mapTypeIdToDataType.MoveNext(&cursor, cursor); + // The type id doesn't exist, create it + if( typeId == -1 ) + { + // Setup the type id for the funcdef + typeId = typeIdSeqNbr++; + typeId |= asTYPEID_APPOBJECT; + mapTypeIdToFunction.Insert(typeId, func); + } + + RELEASEEXCLUSIVE(engineRWLock); } - // The type id doesn't exist, create it - - // Setup the basic type id - int typeId = typeIdSeqNbr++; - if( dt.GetObjectType() ) + // Add flags according to the requested type + if( dtIn.GetObjectType() && !(dtIn.GetObjectType()->flags & asOBJ_ASHANDLE) ) { - if( dt.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT; - else if( dt.GetObjectType()->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_TEMPLATE; - else if( dt.GetObjectType()->flags & asOBJ_ENUM ) {} // TODO: Should we have a specific bit for this? - else typeId |= asTYPEID_APPOBJECT; + // 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; + if( dtIn.IsHandleToConst() ) + typeId |= asTYPEID_HANDLETOCONST; } - // Insert the basic object type - asCDataType *newDt = asNEW(asCDataType)(dt); - if( newDt == 0 ) - { - // Out of memory - return 0; - } - - newDt->MakeReference(false); - newDt->MakeReadOnly(false); - newDt->MakeHandle(false); - - mapTypeIdToDataType.Insert(typeId, newDt); - - // Call recursively to get the correct typeId - return GetTypeIdFromDataType(dtIn); + return typeId; } asCDataType asCScriptEngine::GetDataTypeFromTypeId(int typeId) const { int baseId = typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR); - asSMapNode *cursor = 0; - if( mapTypeIdToDataType.MoveTo(&cursor, baseId) ) + if( typeId <= asTYPEID_DOUBLE ) { - asCDataType dt(*mapTypeIdToDataType.GetValue(cursor)); + eTokenType type[] = {ttVoid, ttBool, ttInt8, ttInt16, ttInt, ttInt64, ttUInt8, ttUInt16, ttUInt, ttUInt64, ttFloat, ttDouble}; + return asCDataType::CreatePrimitive(type[typeId], false); + } + + // First check if the typeId is an object type + asCObjectType *ot = 0; + ACQUIRESHARED(engineRWLock); + asSMapNode *cursor = 0; + if( mapTypeIdToObjectType.MoveTo(&cursor, baseId) ) + ot = mapTypeIdToObjectType.GetValue(cursor); + RELEASESHARED(engineRWLock); + + if( ot ) + { + asCDataType dt = asCDataType::CreateObject(ot, false); if( typeId & asTYPEID_OBJHANDLE ) dt.MakeHandle(true, true); if( typeId & asTYPEID_HANDLETOCONST ) dt.MakeHandleToConst(true); + + return dt; + } + + // Then check if it is a funcdef + asCScriptFunction *func = 0; + ACQUIRESHARED(engineRWLock); + asSMapNode *cursor2 = 0; + if( mapTypeIdToFunction.MoveTo(&cursor2, baseId) ) + func = mapTypeIdToFunction.GetValue(cursor2); + RELEASESHARED(engineRWLock); + + if( func ) + { + asCDataType dt = asCDataType::CreateFuncDef(func); + if( typeId & asTYPEID_OBJHANDLE ) + dt.MakeHandle(true, true); + if( typeId & asTYPEID_HANDLETOCONST ) + dt.MakeHandleToConst(true); + return dt; } @@ -4586,19 +4717,19 @@ asCObjectType *asCScriptEngine::GetObjectTypeFromTypeId(int typeId) const void asCScriptEngine::RemoveFromTypeIdMap(asCObjectType *type) { - asSMapNode *cursor = 0; - mapTypeIdToDataType.MoveFirst(&cursor); + ACQUIREEXCLUSIVE(engineRWLock); + asSMapNode *cursor = 0; + mapTypeIdToObjectType.MoveFirst(&cursor); while( cursor ) { - asCDataType *dt = mapTypeIdToDataType.GetValue(cursor); - asSMapNode *old = cursor; - mapTypeIdToDataType.MoveNext(&cursor, cursor); - if( dt->GetObjectType() == type ) + if( mapTypeIdToObjectType.GetValue(cursor) == type ) { - asDELETE(dt,asCDataType); - mapTypeIdToDataType.Erase(old); + mapTypeIdToObjectType.Erase(cursor); + break; } + mapTypeIdToObjectType.MoveNext(&cursor, cursor); } + RELEASEEXCLUSIVE(engineRWLock); } // interface @@ -5318,7 +5449,6 @@ void asCScriptEngine::RemoveScriptFunction(asCScriptFunction *func) // 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); } diff --git a/lib/angelscript/source/as_scriptengine.h b/lib/angelscript/source/as_scriptengine.h index 161d22781..c9a2a487f 100644 --- a/lib/angelscript/source/as_scriptengine.h +++ b/lib/angelscript/source/as_scriptengine.h @@ -238,6 +238,7 @@ public: bool CallObjectMethodRetBool(void *obj, int func) const; int CallObjectMethodRetInt(void *obj, int func) const; void *CallObjectMethodRetPtr(void *obj, int func) const; + void *CallObjectMethodRetPtr(void *obj, int param1, asCScriptFunction *func) const; void CallGlobalFunction(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc) const; bool CallGlobalFunctionRetBool(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc) const; @@ -400,16 +401,17 @@ public: // This array stores the template instances types that have been automatically generated from template types asCArray generatedTemplateTypes; // Stores the 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) + // TODO: 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; // Type identifiers - mutable int typeIdSeqNbr; - mutable asCMap mapTypeIdToDataType; + mutable int typeIdSeqNbr; + mutable asCMap mapTypeIdToObjectType; + mutable asCMap mapTypeIdToFunction; // Garbage collector asCGarbageCollector gc; diff --git a/lib/angelscript/source/as_scriptfunction.cpp b/lib/angelscript/source/as_scriptfunction.cpp index 874cab558..efb4a5c07 100644 --- a/lib/angelscript/source/as_scriptfunction.cpp +++ b/lib/angelscript/source/as_scriptfunction.cpp @@ -105,7 +105,7 @@ static void ScriptFunction_CreateDelegate_Generic(asIScriptGeneric *gen) gen->SetReturnAddress(CreateDelegate(func, obj)); } -// TODO: 2.29.0: operator== +// TODO: operator== /*static void ScriptFunction_opEquals_Generic(asIScriptGeneric *gen) { asCScriptFunction *funcSelf = (asCScriptFunction*)gen->GetObject(); @@ -124,7 +124,7 @@ void RegisterScriptFunction(asCScriptEngine *engine) UNUSED_VAR(r); // It is only used in debug mode engine->functionBehaviours.engine = engine; engine->functionBehaviours.flags = asOBJ_REF | asOBJ_GC | asOBJ_SCRIPT_FUNCTION; - engine->functionBehaviours.name = "_builtin_function_"; + engine->functionBehaviours.name = "$func"; #ifndef AS_MAX_PORTABILITY r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptFunction,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptFunction,Release), asCALL_THISCALL, 0); asASSERT( r >= 0 ); @@ -133,7 +133,7 @@ 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 + // TODO: 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 ); @@ -226,7 +226,7 @@ asIScriptFunction *asCScriptFunction::GetDelegateFunction() const return funcForDelegate; } -// TODO: 2.29.0: operator== +// TODO: operator== /* // internal bool asCScriptFunction::operator==(const asCScriptFunction &other) const @@ -419,7 +419,6 @@ asCScriptFunction::~asCScriptFunction() // 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(); // Finally set the engine pointer to 0 because it must not be accessed again @@ -678,7 +677,7 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl if( !(returnType.GetTokenType() == ttVoid && objectType && (name == objectType->name || (name.GetLength() > 0 && name[0] == '~') || - name == "_beh_0_" || name == "_beh_2_")) ) + name == "$beh0" || name == "$beh2")) ) { str = returnType.Format(nameSpace, includeNamespace); str += " "; @@ -699,13 +698,13 @@ asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName, bool incl } if( name == "" ) str += "_unnamed_function_("; - else if( name.SubString(0,5) == "_beh_" && name.GetLength() == 7 ) + else if( name.SubString(0,4) == "$beh" && name.GetLength() == 5 ) { - if( name[5] == '0' + asBEHAVE_CONSTRUCT ) + if( name[4] == '0' + asBEHAVE_CONSTRUCT ) str += objectType->name + "("; - else if( name[5] == '0' + asBEHAVE_FACTORY ) + else if( name[4] == '0' + asBEHAVE_FACTORY ) str += returnType.GetObjectType()->name + "("; - else if( name[5] == '0' + asBEHAVE_DESTRUCT ) + else if( name[4] == '0' + asBEHAVE_DESTRUCT ) str += "~" + objectType->name + "("; else str += name + "("; @@ -1008,7 +1007,7 @@ void asCScriptFunction::ComputeSignatureId() // internal bool asCScriptFunction::IsSignatureEqual(const asCScriptFunction *func) const { - if( !IsSignatureExceptNameEqual(func) || name != func->name ) return false; + if( name != func->name || !IsSignatureExceptNameEqual(func) ) return false; return true; } @@ -1043,9 +1042,9 @@ bool asCScriptFunction::IsSignatureExceptNameAndReturnTypeEqual(const asCScriptF bool asCScriptFunction::IsSignatureExceptNameAndReturnTypeEqual(const asCArray ¶mTypes, const asCArray ¶mInOut, const asCObjectType *objType, bool readOnly) const { if( this->isReadOnly != readOnly ) return false; + if( (this->objectType != 0) != (objType != 0) ) return false; if( this->inOutFlags != paramInOut ) return false; if( this->parameterTypes != paramTypes ) return false; - if( (this->objectType != 0) != (objType != 0) ) return false; return true; } @@ -1299,7 +1298,11 @@ void asCScriptFunction::ReleaseReferences() if( group != 0 ) group->Release(); if( funcId ) - engine->scriptFunctions[funcId]->ReleaseInternal(); + { + asCScriptFunction *fptr = engine->scriptFunctions[funcId]; + if( fptr ) + fptr->ReleaseInternal(); + } } break; diff --git a/lib/angelscript/source/as_scriptfunction.h b/lib/angelscript/source/as_scriptfunction.h index a5044d859..1ab9a75e3 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-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 @@ -186,12 +186,12 @@ public: 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 + // TODO: operator== + // TODO: 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: 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(); @@ -329,7 +329,7 @@ public: asSSystemFunctionInterface *sysFuncIntf; }; -const char * const DELEGATE_FACTORY = "%delegate_factory"; +const char * const DELEGATE_FACTORY = "$dlgte"; asCScriptFunction *CreateDelegate(asCScriptFunction *func, void *obj); END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_scriptobject.cpp b/lib/angelscript/source/as_scriptobject.cpp index 89cc4d16e..80af81e83 100644 --- a/lib/angelscript/source/as_scriptobject.cpp +++ b/lib/angelscript/source/as_scriptobject.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 @@ -173,6 +173,24 @@ static void ScriptObject_ReleaseAllHandles_Generic(asIScriptGeneric *gen) self->ReleaseAllHandles(engine); } +static void ScriptObject_Assignment_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0); + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + + *self = *other; + + *(asCScriptObject**)gen->GetAddressOfReturnLocation() = self; +} + +static void ScriptObject_Construct_Generic(asIScriptGeneric *gen) +{ + asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0); + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + + ScriptObject_Construct(objType, self); +} + #endif void RegisterScriptObject(asCScriptEngine *engine) @@ -182,7 +200,7 @@ void RegisterScriptObject(asCScriptEngine *engine) UNUSED_VAR(r); // It is only used in debug mode engine->scriptTypeBehaviours.engine = engine; engine->scriptTypeBehaviours.flags = asOBJ_SCRIPT_OBJECT | asOBJ_REF | asOBJ_GC; - engine->scriptTypeBehaviours.name = "_builtin_object_"; + engine->scriptTypeBehaviours.name = "$obj"; #ifndef AS_MAX_PORTABILITY r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct), asCALL_CDECL_OBJLAST, 0); asASSERT( r >= 0 ); r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptObject,AddRef), asCALL_THISCALL, 0); asASSERT( r >= 0 ); @@ -216,14 +234,6 @@ void RegisterScriptObject(asCScriptEngine *engine) #endif } -void ScriptObject_Construct_Generic(asIScriptGeneric *gen) -{ - asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0); - asCScriptObject *self = (asCScriptObject*)gen->GetObject(); - - ScriptObject_Construct(objType, self); -} - void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self) { new(self) asCScriptObject(objType); @@ -744,16 +754,6 @@ void asCScriptObject::ReleaseAllHandles(asIScriptEngine *engine) } } -void ScriptObject_Assignment_Generic(asIScriptGeneric *gen) -{ - asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0); - asCScriptObject *self = (asCScriptObject*)gen->GetObject(); - - *self = *other; - - *(asCScriptObject**)gen->GetAddressOfReturnLocation() = self; -} - asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self) { return (*self = *other); diff --git a/lib/angelscript/source/as_scriptobject.h b/lib/angelscript/source/as_scriptobject.h index eab7ca64b..dcdc5f371 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-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 @@ -154,9 +154,6 @@ asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject void ScriptObject_ConstructUnitialized(asCObjectType *objType, asCScriptObject *self); -void ScriptObject_Construct_Generic(asIScriptGeneric *gen); -void ScriptObject_Assignment_Generic(asIScriptGeneric *gen); - void RegisterScriptObject(asCScriptEngine *engine); asIScriptObject *ScriptObjectFactory(const asCObjectType *objType, asCScriptEngine *engine); diff --git a/lib/angelscript/source/as_texts.h b/lib/angelscript/source/as_texts.h index 808e1644c..b43648b16 100644 --- a/lib/angelscript/source/as_texts.h +++ b/lib/angelscript/source/as_texts.h @@ -142,6 +142,7 @@ #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_EXPRESSION_LAMBDA "Invalid expression: stand-alone anonymous function" #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" @@ -252,6 +253,7 @@ #define TXT_TOO_MANY_JUMP_LABELS "The function has too many jump labels to handle. Split the function into smaller ones." #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_TYPE_s_NOT_TEMPLATE "Type '%s' is not a template type" #define TXT_UNEXPECTED_END_OF_FILE "Unexpected end of file" #define TXT_UNEXPECTED_TOKEN_s "Unexpected token '%s'" diff --git a/lib/angelscript/source/as_tokendef.h b/lib/angelscript/source/as_tokendef.h index 6e08650a3..a88d2605a 100644 --- a/lib/angelscript/source/as_tokendef.h +++ b/lib/angelscript/source/as_tokendef.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 @@ -311,6 +311,7 @@ const char * const OVERRIDE_TOKEN = "override"; const char * const GET_TOKEN = "get"; const char * const SET_TOKEN = "set"; const char * const ABSTRACT_TOKEN = "abstract"; +const char * const FUNCTION_TOKEN = "function"; END_AS_NAMESPACE diff --git a/lib/angelscript/source/as_typeinfo.cpp b/lib/angelscript/source/as_typeinfo.cpp index 534ac0b53..230aff05f 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-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 @@ -53,7 +53,6 @@ asCTypeInfo::asCTypeInfo() isExplicitHandle = false; qwordValue = 0; isLValue = false; - isVoidExpression = false; isRefToLocal = false; } @@ -68,7 +67,6 @@ void asCTypeInfo::Set(const asCDataType &dt) isExplicitHandle = false; qwordValue = 0; isLValue = false; - isVoidExpression = false; isRefToLocal = false; } @@ -135,6 +133,18 @@ void asCTypeInfo::SetUndefinedFuncHandle(asCScriptEngine *engine) isLValue = false; } +bool asCTypeInfo::IsUndefinedFuncHandle() const +{ + if( isConstant == false ) return false; + if( qwordValue == 0 ) return false; + if( isLValue ) return false; + if( dataType.GetObjectType() == 0 ) return false; + if( dataType.GetObjectType()->name != "$func" ) return false; + if( dataType.GetFuncDef() ) return false; + + return true; +} + void asCTypeInfo::SetNullConstant() { Set(asCDataType::CreateNullHandle()); @@ -153,17 +163,19 @@ bool asCTypeInfo::IsNullConstant() const return false; } -void asCTypeInfo::SetVoidExpression() +void asCTypeInfo::SetVoid() { Set(asCDataType::CreatePrimitive(ttVoid, false)); isLValue = false; - isConstant = false; - isVoidExpression = true; + isConstant = true; } -bool asCTypeInfo::IsVoidExpression() const +bool asCTypeInfo::IsVoid() const { - return isVoidExpression; + if( dataType.GetTokenType() == ttVoid ) + return true; + + return false; } void asCTypeInfo::SetDummy() diff --git a/lib/angelscript/source/as_typeinfo.h b/lib/angelscript/source/as_typeinfo.h index aa8ad528c..724411e82 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-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,11 +61,12 @@ struct asCTypeInfo void SetConstantD(const asCDataType &dataType, double value); void SetNullConstant(); void SetUndefinedFuncHandle(asCScriptEngine *engine); - void SetVoidExpression(); + void SetVoid(); void SetDummy(); + bool IsUndefinedFuncHandle() const; bool IsNullConstant() const; - bool IsVoidExpression() const; + bool IsVoid() const; asCDataType dataType; bool isLValue : 1; // Can this value be updated in assignment, or increment operators, etc @@ -73,9 +74,8 @@ struct asCTypeInfo bool isConstant : 1; bool isVariable : 1; bool isExplicitHandle : 1; - bool isVoidExpression : 1; bool isRefToLocal : 1; // The reference may be to a local variable - short dummy : 9; + short dummy : 10; short stackOffset; union {