Initial commit
This commit is contained in:
@@ -0,0 +1,809 @@
|
||||
/*
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user