123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921 |
- /*
- AngelCode Scripting Library
- Copyright (c) 2003-2021 Andreas Jonsson
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any
- damages arising from the use of this software.
- Permission is granted to anyone to use this software for any
- purpose, including commercial applications, and to alter it and
- redistribute it freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you
- must not claim that you wrote the original software. If you use
- this software in a product, an acknowledgment in the product
- documentation would be appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and
- must not be misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source
- distribution.
- The original version of this library can be located at:
- http://www.angelcode.com/angelscript/
- Andreas Jonsson
- andreas@angelcode.com
- */
- //
- // as_callfunc.cpp
- //
- // These functions handle the actual calling of system functions
- //
- #include "as_config.h"
- #include "as_callfunc.h"
- #include "as_scriptengine.h"
- #include "as_texts.h"
- #include "as_context.h"
- BEGIN_AS_NAMESPACE
- // ref: Member Function Pointers and the Fastest Possible C++ Delegates
- // describes the structure of class method pointers for most compilers
- // http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
- // ref: The code comments for ItaniumCXXABI::EmitLoadOfMemberFunctionPointer in the LLVM compiler
- // describes the structure for class method pointers on Itanium and arm64 ABI
- // http://clang.llvm.org/doxygen/CodeGen_2ItaniumCXXABI_8cpp_source.html#l00937
- int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, void *auxiliary, asSSystemFunctionInterface *internal)
- {
- internal->Clear();
- internal->func = ptr.ptr.f.func;
- internal->auxiliary = 0;
- // Was a compatible calling convention specified?
- if( internal->func )
- {
- if( ptr.flag == 1 && callConv != asCALL_GENERIC )
- return asWRONG_CALLING_CONV;
- else if( ptr.flag == 2 && (callConv == asCALL_GENERIC || callConv == asCALL_THISCALL || callConv == asCALL_THISCALL_ASGLOBAL || callConv == asCALL_THISCALL_OBJFIRST || callConv == asCALL_THISCALL_OBJLAST) )
- return asWRONG_CALLING_CONV;
- else if( ptr.flag == 3 && !(callConv == asCALL_THISCALL || callConv == asCALL_THISCALL_ASGLOBAL || callConv == asCALL_THISCALL_OBJFIRST || callConv == asCALL_THISCALL_OBJLAST) )
- return asWRONG_CALLING_CONV;
- }
- asDWORD base = callConv;
- if( !isMethod )
- {
- if( base == asCALL_CDECL )
- internal->callConv = ICC_CDECL;
- else if( base == asCALL_STDCALL )
- internal->callConv = ICC_STDCALL;
- else if( base == asCALL_THISCALL_ASGLOBAL )
- {
- if(auxiliary == 0)
- return asINVALID_ARG;
- internal->auxiliary = auxiliary;
- internal->callConv = ICC_THISCALL;
- // This is really a thiscall, so it is necessary to check for virtual method pointers
- base = asCALL_THISCALL;
- isMethod = true;
- }
- else if (base == asCALL_GENERIC)
- {
- internal->callConv = ICC_GENERIC_FUNC;
- // The auxiliary object is optional for generic calling convention
- internal->auxiliary = auxiliary;
- }
- else
- return asNOT_SUPPORTED;
- }
- if( isMethod )
- {
- #ifndef AS_NO_CLASS_METHODS
- if( base == asCALL_THISCALL || base == asCALL_THISCALL_OBJFIRST || base == asCALL_THISCALL_OBJLAST )
- {
- internalCallConv thisCallConv;
- if( base == asCALL_THISCALL )
- {
- if(callConv != asCALL_THISCALL_ASGLOBAL && auxiliary)
- return asINVALID_ARG;
- thisCallConv = ICC_THISCALL;
- }
- else
- {
- #ifdef AS_NO_THISCALL_FUNCTOR_METHOD
- return asNOT_SUPPORTED;
- #else
- if(auxiliary == 0)
- return asINVALID_ARG;
- internal->auxiliary = auxiliary;
- if( base == asCALL_THISCALL_OBJFIRST )
- thisCallConv = ICC_THISCALL_OBJFIRST;
- else //if( base == asCALL_THISCALL_OBJLAST )
- thisCallConv = ICC_THISCALL_OBJLAST;
- #endif
- }
- internal->callConv = thisCallConv;
- #ifdef GNU_STYLE_VIRTUAL_METHOD
- if( (size_t(ptr.ptr.f.func) & 1) )
- internal->callConv = (internalCallConv)(thisCallConv + 2);
- #endif
- internal->baseOffset = ( int )MULTI_BASE_OFFSET(ptr);
- #if (defined(AS_ARM64) || 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
- #ifdef HAVE_VIRTUAL_BASE_OFFSET
- // We don't support virtual inheritance
- if( VIRTUAL_BASE_OFFSET(ptr) != 0 )
- return asNOT_SUPPORTED;
- #endif
- }
- else
- #endif
- if( base == asCALL_CDECL_OBJLAST )
- internal->callConv = ICC_CDECL_OBJLAST;
- else if( base == asCALL_CDECL_OBJFIRST )
- internal->callConv = ICC_CDECL_OBJFIRST;
- else if (base == asCALL_GENERIC)
- {
- internal->callConv = ICC_GENERIC_METHOD;
- internal->auxiliary = auxiliary;
- }
- else
- return asNOT_SUPPORTED;
- }
- return 0;
- }
- // This function should prepare system functions so that it will be faster to call them
- int PrepareSystemFunctionGeneric(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine)
- {
- asASSERT(internal->callConv == ICC_GENERIC_METHOD || internal->callConv == ICC_GENERIC_FUNC);
- // Calculate the size needed for the parameters
- internal->paramSize = func->GetSpaceNeededForArguments();
- // Prepare the clean up instructions for the function arguments
- internal->cleanArgs.SetLength(0);
- int offset = 0;
- for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ )
- {
- asCDataType &dt = func->parameterTypes[n];
- if( (dt.IsObject() || dt.IsFuncdef()) && !dt.IsReference() )
- {
- if (dt.IsFuncdef())
- {
- // If the generic call mode is set to old behaviour then always release handles
- // else only release the handle if the function is declared with auto handles
- if (engine->ep.genericCallMode == 0 || (internal->paramAutoHandles.GetLength() > n && internal->paramAutoHandles[n]))
- {
- asSSystemFunctionInterface::SClean clean;
- clean.op = 0; // call release
- clean.ot = &engine->functionBehaviours;
- clean.off = short(offset);
- internal->cleanArgs.PushLast(clean);
- }
- }
- else if( dt.GetTypeInfo()->flags & asOBJ_REF )
- {
- // If the generic call mode is set to old behaviour then always release handles
- // else only release the handle if the function is declared with auto handles
- if (!dt.IsObjectHandle() ||
- engine->ep.genericCallMode == 0 ||
- (internal->paramAutoHandles.GetLength() > n && internal->paramAutoHandles[n]) )
- {
- asSTypeBehaviour *beh = &CastToObjectType(dt.GetTypeInfo())->beh;
- asASSERT((dt.GetTypeInfo()->flags & asOBJ_NOCOUNT) || beh->release);
- if (beh->release)
- {
- asSSystemFunctionInterface::SClean clean;
- clean.op = 0; // call release
- clean.ot = CastToObjectType(dt.GetTypeInfo());
- clean.off = short(offset);
- internal->cleanArgs.PushLast(clean);
- }
- }
- }
- else
- {
- asSSystemFunctionInterface::SClean clean;
- clean.op = 1; // call free
- clean.ot = CastToObjectType(dt.GetTypeInfo());
- clean.off = short(offset);
- // Call the destructor then free the memory
- asSTypeBehaviour *beh = &CastToObjectType(dt.GetTypeInfo())->beh;
- if( beh->destruct )
- clean.op = 2; // call destruct, then free
- internal->cleanArgs.PushLast(clean);
- }
- }
- if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() )
- offset += AS_PTR_SIZE;
- else
- offset += dt.GetSizeOnStackDWords();
- }
- return 0;
- }
- // This function should prepare system functions so that it will be faster to call them
- int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine)
- {
- #ifdef AS_MAX_PORTABILITY
- UNUSED_VAR(func);
- UNUSED_VAR(internal);
- UNUSED_VAR(engine);
- // This should never happen, as when AS_MAX_PORTABILITY is on, all functions
- // are asCALL_GENERIC, which are prepared by PrepareSystemFunctionGeneric
- asASSERT(false);
- #else
- // References are always returned as primitive data
- if( func->returnType.IsReference() || func->returnType.IsObjectHandle() )
- {
- internal->hostReturnInMemory = false;
- internal->hostReturnSize = sizeof(void*)/4;
- internal->hostReturnFloat = false;
- }
- // Registered types have special flags that determine how they are returned
- else if( func->returnType.IsObject() )
- {
- asDWORD objType = func->returnType.GetTypeInfo()->flags;
- // Only value types can be returned by value
- asASSERT( objType & asOBJ_VALUE );
- if( !(objType & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_ARRAY)) )
- {
- // If the return is by value then we need to know the true type
- engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf());
- asCString str;
- str.Format(TXT_CANNOT_RET_TYPE_s_BY_VAL, func->returnType.GetTypeInfo()->name.AddressOf());
- engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
- engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0);
- }
- else if( objType & asOBJ_APP_ARRAY )
- {
- // Array types are always returned in memory
- internal->hostReturnInMemory = true;
- internal->hostReturnSize = sizeof(void*)/4;
- internal->hostReturnFloat = false;
- }
- else if( objType & asOBJ_APP_CLASS )
- {
- internal->hostReturnFloat = false;
- if( objType & COMPLEX_RETURN_MASK )
- {
- internal->hostReturnInMemory = true;
- internal->hostReturnSize = sizeof(void*)/4;
- }
- else
- {
- #ifdef HAS_128_BIT_PRIMITIVES
- if( func->returnType.GetSizeInMemoryDWords() > 4 )
- #else
- if( func->returnType.GetSizeInMemoryDWords() > 2 )
- #endif
- {
- internal->hostReturnInMemory = true;
- internal->hostReturnSize = sizeof(void*)/4;
- }
- else
- {
- internal->hostReturnInMemory = false;
- internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords();
- #ifdef SPLIT_OBJS_BY_MEMBER_TYPES
- if( func->returnType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS )
- internal->hostReturnFloat = true;
- #endif
- }
- #ifdef THISCALL_RETURN_SIMPLE_IN_MEMORY
- if((internal->callConv == ICC_THISCALL ||
- #ifdef AS_NO_THISCALL_FUNCTOR_METHOD
- internal->callConv == ICC_VIRTUAL_THISCALL) &&
- #else
- internal->callConv == ICC_VIRTUAL_THISCALL ||
- internal->callConv == ICC_THISCALL_OBJFIRST ||
- internal->callConv == ICC_THISCALL_OBJLAST) &&
- #endif
- func->returnType.GetSizeInMemoryDWords() >= THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE)
- {
- internal->hostReturnInMemory = true;
- internal->hostReturnSize = sizeof(void*)/4;
- }
- #endif
- #ifdef CDECL_RETURN_SIMPLE_IN_MEMORY
- if((internal->callConv == ICC_CDECL ||
- internal->callConv == ICC_CDECL_OBJLAST ||
- internal->callConv == ICC_CDECL_OBJFIRST) &&
- func->returnType.GetSizeInMemoryDWords() >= CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE)
- {
- internal->hostReturnInMemory = true;
- internal->hostReturnSize = sizeof(void*)/4;
- }
- #endif
- #ifdef STDCALL_RETURN_SIMPLE_IN_MEMORY
- if( internal->callConv == ICC_STDCALL &&
- func->returnType.GetSizeInMemoryDWords() >= STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE)
- {
- internal->hostReturnInMemory = true;
- internal->hostReturnSize = sizeof(void*)/4;
- }
- #endif
- }
- #ifdef SPLIT_OBJS_BY_MEMBER_TYPES
- // It's not safe to return objects by value because different registers
- // will be used depending on the memory layout of the object.
- // Ref: http://www.x86-64.org/documentation/abi.pdf
- // Ref: http://www.agner.org/optimize/calling_conventions.pdf
- // If the application informs that the class should be treated as all integers, then we allow it
- if( !internal->hostReturnInMemory &&
- !(func->returnType.GetTypeInfo()->flags & (asOBJ_APP_CLASS_ALLINTS | asOBJ_APP_CLASS_ALLFLOATS)) )
- {
- engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf());
- asCString str;
- str.Format(TXT_DONT_SUPPORT_RET_TYPE_s_BY_VAL, func->returnType.Format(func->nameSpace).AddressOf());
- engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
- engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0);
- }
- #endif
- }
- else if( objType & asOBJ_APP_PRIMITIVE )
- {
- internal->hostReturnInMemory = false;
- internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords();
- internal->hostReturnFloat = false;
- }
- else if( objType & asOBJ_APP_FLOAT )
- {
- internal->hostReturnInMemory = false;
- internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords();
- internal->hostReturnFloat = true;
- }
- }
- // Primitive types can easily be determined
- #ifdef HAS_128_BIT_PRIMITIVES
- else if( func->returnType.GetSizeInMemoryDWords() > 4 )
- {
- // Shouldn't be possible to get here
- asASSERT(false);
- }
- else if( func->returnType.GetSizeInMemoryDWords() == 4 )
- {
- internal->hostReturnInMemory = false;
- internal->hostReturnSize = 4;
- internal->hostReturnFloat = false;
- }
- #else
- else if( func->returnType.GetSizeInMemoryDWords() > 2 )
- {
- // Shouldn't be possible to get here
- asASSERT(false);
- }
- #endif
- else if( func->returnType.GetSizeInMemoryDWords() == 2 )
- {
- internal->hostReturnInMemory = false;
- internal->hostReturnSize = 2;
- internal->hostReturnFloat = func->returnType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttDouble, true));
- }
- else if( func->returnType.GetSizeInMemoryDWords() == 1 )
- {
- internal->hostReturnInMemory = false;
- internal->hostReturnSize = 1;
- internal->hostReturnFloat = func->returnType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttFloat, true));
- }
- else
- {
- internal->hostReturnInMemory = false;
- internal->hostReturnSize = 0;
- internal->hostReturnFloat = false;
- }
- // Calculate the size needed for the parameters
- internal->paramSize = func->GetSpaceNeededForArguments();
- // Verify if the function takes any objects by value
- asUINT n;
- internal->takesObjByVal = false;
- for( n = 0; n < func->parameterTypes.GetLength(); n++ )
- {
- if( func->parameterTypes[n].IsObject() && !func->parameterTypes[n].IsObjectHandle() && !func->parameterTypes[n].IsReference() )
- {
- internal->takesObjByVal = true;
- // Can't pass objects by value unless the application type is informed
- if( !(func->parameterTypes[n].GetTypeInfo()->flags & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_ARRAY)) )
- {
- engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf());
- asCString str;
- str.Format(TXT_CANNOT_PASS_TYPE_s_BY_VAL, func->parameterTypes[n].GetTypeInfo()->name.AddressOf());
- engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
- engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0);
- }
- #ifdef SPLIT_OBJS_BY_MEMBER_TYPES
- // It's not safe to pass objects by value because different registers
- // will be used depending on the memory layout of the object
- // Ref: http://www.x86-64.org/documentation/abi.pdf
- // Ref: http://www.agner.org/optimize/calling_conventions.pdf
- if(
- #ifdef COMPLEX_OBJS_PASSED_BY_REF
- !(func->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK) &&
- #endif
- #ifdef LARGE_OBJS_PASS_BY_REF
- func->parameterTypes[n].GetSizeInMemoryDWords() < AS_LARGE_OBJ_MIN_SIZE &&
- #endif
- !(func->parameterTypes[n].GetTypeInfo()->flags & (asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT | asOBJ_APP_CLASS_ALLINTS | asOBJ_APP_CLASS_ALLFLOATS)) )
- {
- engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf());
- asCString str;
- str.Format(TXT_DONT_SUPPORT_TYPE_s_BY_VAL, func->parameterTypes[n].GetTypeInfo()->name.AddressOf());
- engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
- engine->ConfigError(asINVALID_CONFIGURATION, 0, 0, 0);
- }
- #endif
- break;
- }
- }
- // Prepare the clean up instructions for the function arguments
- internal->cleanArgs.SetLength(0);
- int offset = 0;
- for( n = 0; n < func->parameterTypes.GetLength(); n++ )
- {
- asCDataType &dt = func->parameterTypes[n];
- #if defined(COMPLEX_OBJS_PASSED_BY_REF) || defined(AS_LARGE_OBJS_PASSED_BY_REF)
- bool needFree = false;
- #ifdef COMPLEX_OBJS_PASSED_BY_REF
- if( dt.GetTypeInfo() && dt.GetTypeInfo()->flags & COMPLEX_MASK ) needFree = true;
- #endif
- #ifdef AS_LARGE_OBJS_PASSED_BY_REF
- if( dt.GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE ) needFree = true;
- #endif
- if( needFree &&
- dt.IsObject() &&
- !dt.IsObjectHandle() &&
- !dt.IsReference() )
- {
- asSSystemFunctionInterface::SClean clean;
- clean.op = 1; // call free
- clean.ot = CastToObjectType(dt.GetTypeInfo());
- clean.off = short(offset);
- #ifndef AS_CALLEE_DESTROY_OBJ_BY_VAL
- // If the called function doesn't destroy objects passed by value we must do so here
- asSTypeBehaviour *beh = &CastToObjectType(dt.GetTypeInfo())->beh;
- if( beh->destruct )
- clean.op = 2; // call destruct, then free
- #endif
- internal->cleanArgs.PushLast(clean);
- }
- #endif
- if( n < internal->paramAutoHandles.GetLength() && internal->paramAutoHandles[n] )
- {
- asSSystemFunctionInterface::SClean clean;
- clean.op = 0; // call release
- if (dt.IsFuncdef())
- clean.ot = &engine->functionBehaviours;
- else
- clean.ot = CastToObjectType(dt.GetTypeInfo());
- clean.off = short(offset);
- internal->cleanArgs.PushLast(clean);
- }
- if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() )
- offset += AS_PTR_SIZE;
- else
- offset += dt.GetSizeOnStackDWords();
- }
- #endif // !defined(AS_MAX_PORTABILITY)
- return 0;
- }
- #ifdef AS_MAX_PORTABILITY
- int CallSystemFunction(int id, asCContext *context)
- {
- asCScriptEngine *engine = context->m_engine;
- asCScriptFunction *func = engine->scriptFunctions[id];
- asSSystemFunctionInterface *sysFunc = func->sysFuncIntf;
- int callConv = sysFunc->callConv;
- if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD )
- return context->CallGeneric(func);
- context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
- return 0;
- }
- #else
- //
- // CallSystemFunctionNative
- //
- // This function is implemented for each platform where the native calling conventions is supported.
- // See the various as_callfunc_xxx.cpp files for their implementation. It is responsible for preparing
- // the arguments for the function call, calling the function, and then retrieving the return value.
- //
- // Parameters:
- //
- // context - This is the context that can be used to retrieve specific information from the engine
- // descr - This is the script function object that holds the information on how to call the function
- // obj - This is the object pointer, if the call is for a class method, otherwise it is null
- // args - This is the function arguments, which are packed as in AngelScript
- // retPointer - This points to a the memory buffer where the return object is to be placed, if the function returns the value in memory rather than in registers
- // retQW2 - This output parameter should be used if the function returns a value larger than 64bits in registers
- // secondObj - This is the object pointer that the proxy method should invoke its method on when the call convention is THISCALL_OBJFIRST/LAST
- //
- // Return value:
- //
- // The function should return the value that is returned in registers.
- asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObj);
- int CallSystemFunction(int id, asCContext *context)
- {
- asCScriptEngine *engine = context->m_engine;
- asCScriptFunction *descr = engine->scriptFunctions[id];
- asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
- int callConv = sysFunc->callConv;
- if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD )
- return context->CallGeneric(descr);
- asQWORD retQW = 0;
- asQWORD retQW2 = 0;
- asDWORD *args = context->m_regs.stackPointer;
- void *retPointer = 0;
- int popSize = sysFunc->paramSize;
- // TODO: clean-up: CallSystemFunctionNative should have two arguments for object pointers
- // objForThiscall is the object pointer that should be used for the thiscall
- // objForArg is the object pointer that should be passed as argument when using OBJFIRST or OBJLAST
- // Used to save two object pointers with THISCALL_OBJLAST or THISCALL_OBJFIRST
- void *obj = 0;
- void *secondObj = 0;
- #ifdef AS_NO_THISCALL_FUNCTOR_METHOD
- if( callConv >= ICC_THISCALL )
- {
- if(sysFunc->auxiliary)
- {
- // This class method is being called as if it is a global function
- obj = sysFunc->auxiliary;
- }
- else
- {
- // The object pointer should be popped from the context stack
- popSize += AS_PTR_SIZE;
- // Check for null pointer
- obj = (void*)*(asPWORD*)(args);
- if( obj == 0 )
- {
- context->SetInternalException(TXT_NULL_POINTER_ACCESS);
- return 0;
- }
- // Skip the object pointer
- args += AS_PTR_SIZE;
- }
-
- // Add the base offset for multiple inheritance
- #if (defined(__GNUC__) && (defined(AS_ARM64) || 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);
- #endif
- }
- #else // !defined(AS_NO_THISCALL_FUNCTOR_METHOD)
- if( callConv >= ICC_THISCALL )
- {
- bool continueCheck = true; // True if need check objectPointer or context stack for object
- int continueCheckIndex = 0; // Index into objectsPtrs to save the object if continueCheck
- if( callConv >= ICC_THISCALL_OBJLAST )
- {
- asASSERT( sysFunc->auxiliary != 0 );
- // This class method is being called as object method (sysFunc->auxiliary must be set).
- obj = sysFunc->auxiliary;
- continueCheckIndex = 1;
- }
- else if(sysFunc->auxiliary)
- {
- // This class method is being called as if it is a global function
- obj = sysFunc->auxiliary;
- continueCheck = false;
- }
-
- if( obj )
- {
- // Add the base offset for multiple inheritance
- #if (defined(__GNUC__) && (defined(AS_ARM64) || 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);
- #endif
- }
- if( continueCheck )
- {
- void *tempPtr = 0;
- // The object pointer should be popped from the context stack
- popSize += AS_PTR_SIZE;
- // Check for null pointer
- tempPtr = (void*)*(asPWORD*)(args);
- if( tempPtr == 0 )
- {
- context->SetInternalException(TXT_NULL_POINTER_ACCESS);
- return 0;
- }
- // Add the base offset for multiple inheritance
- #if (defined(__GNUC__) && (defined(AS_ARM64) || 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);
- #endif
- // Skip the object pointer
- args += AS_PTR_SIZE;
- if( continueCheckIndex )
- secondObj = tempPtr;
- else
- {
- asASSERT( obj == 0 );
- obj = tempPtr;
- }
- }
- }
- #endif // AS_NO_THISCALL_FUNCTOR_METHOD
- if( descr->DoesReturnOnStack() )
- {
- // Get the address of the location for the return value from the stack
- retPointer = (void*)*(asPWORD*)(args);
- popSize += AS_PTR_SIZE;
- args += AS_PTR_SIZE;
- // When returning the value on the location allocated by the called
- // we shouldn't set the object type in the register
- context->m_regs.objectType = 0;
- }
- else
- {
- // Set the object type of the reference held in the register
- context->m_regs.objectType = descr->returnType.GetTypeInfo();
- }
- // For composition we need to add the offset and/or dereference the pointer
- if(obj)
- {
- obj = (void*) ((char*) obj + sysFunc->compositeOffset);
- if(sysFunc->isCompositeIndirect) obj = *((void**)obj);
- }
- context->m_callingSystemFunction = descr;
- bool cppException = false;
- #ifdef AS_NO_EXCEPTIONS
- retQW = CallSystemFunctionNative(context, descr, obj, args, sysFunc->hostReturnInMemory ? retPointer : 0, retQW2, secondObj);
- #else
- // This try/catch block is to catch potential exception that may
- // be thrown by the registered function. The implementation of the
- // CallSystemFunctionNative() must make sure not to have any manual
- // clean-up after the call to the real function, or that won't be
- // executed in case of an exception.
- try
- {
- retQW = CallSystemFunctionNative(context, descr, obj, args, sysFunc->hostReturnInMemory ? retPointer : 0, retQW2, secondObj);
- }
- catch(...)
- {
- cppException = true;
- // Convert the exception to a script exception so the VM can
- // properly report the error to the application and then clean up
- context->HandleAppException();
- }
- #endif
- context->m_callingSystemFunction = 0;
- // Store the returned value in our stack
- if( (descr->returnType.IsObject() || descr->returnType.IsFuncdef()) && !descr->returnType.IsReference() )
- {
- if( descr->returnType.IsObjectHandle() )
- {
- #if defined(AS_BIG_ENDIAN) && AS_PTR_SIZE == 1
- // Since we're treating the system function as if it is returning a QWORD we are
- // actually receiving the value in the high DWORD of retQW.
- retQW >>= 32;
- #endif
- context->m_regs.objectRegister = (void*)(asPWORD)retQW;
- if( sysFunc->returnAutoHandle && context->m_regs.objectRegister )
- {
- asASSERT( !(descr->returnType.GetTypeInfo()->flags & asOBJ_NOCOUNT) );
- engine->CallObjectMethod(context->m_regs.objectRegister, CastToObjectType(descr->returnType.GetTypeInfo())->beh.addref);
- }
- }
- else
- {
- asASSERT( retPointer );
- if( !sysFunc->hostReturnInMemory )
- {
- // Copy the returned value to the pointer sent by the script engine
- if( sysFunc->hostReturnSize == 1 )
- {
- #if defined(AS_BIG_ENDIAN) && AS_PTR_SIZE == 1
- // Since we're treating the system function as if it is returning a QWORD we are
- // actually receiving the value in the high DWORD of retQW.
- retQW >>= 32;
- #endif
- *(asDWORD*)retPointer = (asDWORD)retQW;
- }
- else if( sysFunc->hostReturnSize == 2 )
- *(asQWORD*)retPointer = retQW;
- else if( sysFunc->hostReturnSize == 3 )
- {
- *(asQWORD*)retPointer = retQW;
- *(((asDWORD*)retPointer) + 2) = (asDWORD)retQW2;
- }
- else // if( sysFunc->hostReturnSize == 4 )
- {
- *(asQWORD*)retPointer = retQW;
- *(((asQWORD*)retPointer) + 1) = retQW2;
- }
- }
- if( context->m_status == asEXECUTION_EXCEPTION && !cppException )
- {
- // If the function raised a script exception it really shouldn't have
- // initialized the object. However, as it is a soft exception there is
- // no way for the application to not return a value, so instead we simply
- // destroy it here, to pretend it was never created.
- if(CastToObjectType(descr->returnType.GetTypeInfo())->beh.destruct )
- engine->CallObjectMethod(retPointer, CastToObjectType(descr->returnType.GetTypeInfo())->beh.destruct);
- }
- }
- }
- else
- {
- // Store value in value register
- if( sysFunc->hostReturnSize == 1 )
- {
- #if defined(AS_BIG_ENDIAN)
- // Since we're treating the system function as if it is returning a QWORD we are
- // actually receiving the value in the high DWORD of retQW.
- retQW >>= 32;
- // Due to endian issues we need to handle return values that are
- // less than a DWORD (32 bits) in size specially
- int numBytes = descr->returnType.GetSizeInMemoryBytes();
- if( descr->returnType.IsReference() ) numBytes = 4;
- switch( numBytes )
- {
- case 1:
- {
- // 8 bits
- asBYTE *val = (asBYTE*)&context->m_regs.valueRegister;
- val[0] = (asBYTE)retQW;
- val[1] = 0;
- val[2] = 0;
- val[3] = 0;
- val[4] = 0;
- val[5] = 0;
- val[6] = 0;
- val[7] = 0;
- }
- break;
- case 2:
- {
- // 16 bits
- asWORD *val = (asWORD*)&context->m_regs.valueRegister;
- val[0] = (asWORD)retQW;
- val[1] = 0;
- val[2] = 0;
- val[3] = 0;
- }
- break;
- default:
- {
- // 32 bits
- asDWORD *val = (asDWORD*)&context->m_regs.valueRegister;
- val[0] = (asDWORD)retQW;
- val[1] = 0;
- }
- break;
- }
- #else
- *(asDWORD*)&context->m_regs.valueRegister = (asDWORD)retQW;
- #endif
- }
- else
- context->m_regs.valueRegister = retQW;
- }
- // Clean up arguments
- const asUINT cleanCount = sysFunc->cleanArgs.GetLength();
- if( cleanCount )
- {
- args = context->m_regs.stackPointer;
- // 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->auxiliary == 0 )
- args += AS_PTR_SIZE;
- asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf();
- for( asUINT n = 0; n < cleanCount; n++, clean++ )
- {
- void **addr = (void**)&args[clean->off];
- if( clean->op == 0 )
- {
- if( *addr != 0 )
- {
- engine->CallObjectMethod(*addr, clean->ot->beh.release);
- *addr = 0;
- }
- }
- else
- {
- asASSERT( clean->op == 1 || clean->op == 2 );
- asASSERT( *addr );
- if( clean->op == 2 )
- engine->CallObjectMethod(*addr, clean->ot->beh.destruct);
- engine->CallFree(*addr);
- }
- }
- }
- return popSize;
- }
- #endif // AS_MAX_PORTABILITY
- END_AS_NAMESPACE
|