int DetectCallingConvention(bool isMethod, const asUPtr &ptr, int callConv, asSSystemFunctionInterface *internal)
{
	memset(internal, 0, sizeof(asSSystemFunctionInterface));

	internal->func = (size_t)ptr.f.func;

	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_GENERIC )
			internal->callConv = ICC_GENERIC_FUNC;
		else
			return asNOT_SUPPORTED;
	}
	else
	{
#ifndef AS_NO_CLASS_METHODS
		if( base == asCALL_THISCALL )
		{
			internal->callConv = ICC_THISCALL;
#ifdef GNU_STYLE_VIRTUAL_METHOD
			if( (size_t(ptr.f.func) & 1) )
				internal->callConv = ICC_VIRTUAL_THISCALL;
#endif
			internal->baseOffset = MULTI_BASE_OFFSET(ptr);

#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;
		else
			return asNOT_SUPPORTED;
	}

	return 0;
}
Beispiel #2
0
BEGIN_AS_NAMESPACE

// ref: Member Function Pointers and the Fastest Possible C++ Delegates
//      describes the structure of class method pointers for most compilers
//      http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

// ref: The code comments for ItaniumCXXABI::EmitLoadOfMemberFunctionPointer in the LLVM compiler
//      describes the structure for class method pointers on Itanium and arm64 ABI
//      http://clang.llvm.org/doxygen/CodeGen_2ItaniumCXXABI_8cpp_source.html#l00937

int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, void *objForThiscall, asSSystemFunctionInterface *internal)
{
	memset(internal, 0, sizeof(asSSystemFunctionInterface));

	internal->func           = ptr.ptr.f.func;
	internal->objForThiscall = 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( objForThiscall == 0 )
				return asINVALID_ARG;
			internal->objForThiscall = objForThiscall;
			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;
		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 && objForThiscall )
					return asINVALID_ARG;

				thisCallConv = ICC_THISCALL;
			}
			else
			{
#ifdef AS_NO_THISCALL_FUNCTOR_METHOD
				return asNOT_SUPPORTED;
#else
				if( objForThiscall == 0 )
					return asINVALID_ARG;

				internal->objForThiscall = objForThiscall;
				if( base == asCALL_THISCALL_OBJFIRST )
					thisCallConv = ICC_THISCALL_OBJFIRST;
				else //if( base == asCALL_THISCALL_OBJLAST )
					thisCallConv = ICC_THISCALL_OBJLAST;
#endif
			}

			internal->callConv = thisCallConv;
#ifdef GNU_STYLE_VIRTUAL_METHOD
			if( (size_t(ptr.ptr.f.func) & 1) )
				internal->callConv = (internalCallConv)(thisCallConv + 2);
#endif
			internal->baseOffset = ( int )MULTI_BASE_OFFSET(ptr);
#if defined(AS_ARM) && (defined(__GNUC__) || defined(AS_PSVITA))
			// As the least significant bit in func is used to switch to THUMB mode
			// on ARM processors, the LSB in the __delta variable is used instead of
			// the one in __pfn on ARM processors.
			if( (size_t(internal->baseOffset) & 1) )
				internal->callConv = (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;
		else
			return asNOT_SUPPORTED;
	}

	return 0;
}