#include "InterpreterModule.h"
|
|
#include "Interpreter.h"
|
|
#include <unordered_map>
|
|
#include "vm/GlobalMetadata.h"
|
#include "vm/MetadataCache.h"
|
#include "vm/MetadataLock.h"
|
#include "vm/Class.h"
|
#include "vm/Object.h"
|
#include "vm/Method.h"
|
|
#include "../metadata/MetadataModule.h"
|
#include "../metadata/MetadataUtil.h"
|
#include "../metadata/InterpreterImage.h"
|
#include "../transform/Transform.h"
|
|
#include "MethodBridge.h"
|
#include "InterpreterUtil.h"
|
|
namespace hybridclr
|
{
|
namespace interpreter
|
{
|
il2cpp::os::ThreadLocalValue InterpreterModule::s_machineState;
|
|
static Il2CppHashMap<const char*, Managed2NativeCallMethod, CStringHash, CStringEqualTo> g_managed2natives;
|
static Il2CppHashMap<const char*, Il2CppMethodPointer, CStringHash, CStringEqualTo> g_native2manageds;
|
static Il2CppHashMap<const char*, Il2CppMethodPointer, CStringHash, CStringEqualTo> g_adjustThunks;
|
static Il2CppHashMap<const char*, const char*, CStringHash, CStringEqualTo> g_fullName2signature;
|
|
MachineState& InterpreterModule::GetCurrentThreadMachineState()
|
{
|
MachineState* state = nullptr;
|
s_machineState.GetValue((void**)&state);
|
if (!state)
|
{
|
state = new MachineState();
|
s_machineState.SetValue(state);
|
}
|
return *state;
|
}
|
|
void InterpreterModule::FreeThreadLocalMachineState()
|
{
|
MachineState* state = nullptr;
|
s_machineState.GetValue((void**)&state);
|
if (state)
|
{
|
delete state;
|
s_machineState.SetValue(nullptr);
|
}
|
}
|
|
void InterpreterModule::Initialize()
|
{
|
for (size_t i = 0; ; i++)
|
{
|
Managed2NativeMethodInfo& method = g_managed2nativeStub[i];
|
if (!method.signature)
|
{
|
break;
|
}
|
g_managed2natives.insert({ method.signature, method.method });
|
}
|
for (size_t i = 0; ; i++)
|
{
|
Native2ManagedMethodInfo& method = g_native2managedStub[i];
|
if (!method.signature)
|
{
|
break;
|
}
|
g_native2manageds.insert({ method.signature, method.method });
|
}
|
|
for (size_t i = 0; ; i++)
|
{
|
NativeAdjustThunkMethodInfo& method = g_adjustThunkStub[i];
|
if (!method.signature)
|
{
|
break;
|
}
|
g_adjustThunks.insert({ method.signature, method.method });
|
}
|
for (size_t i = 0 ; ; i++)
|
{
|
FullName2Signature& nameSig = g_fullName2SignatureStub[i];
|
if (!nameSig.fullName)
|
{
|
break;
|
}
|
g_fullName2signature.insert({ nameSig.fullName, nameSig.signature });
|
}
|
}
|
|
void InterpreterModule::NotSupportNative2Managed()
|
{
|
il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetExecutionEngineException("NotSupportNative2Managed"));
|
}
|
|
void InterpreterModule::NotSupportAdjustorThunk()
|
{
|
il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetExecutionEngineException("NotSupportAdjustorThunk"));
|
}
|
|
const char* InterpreterModule::GetValueTypeSignature(const char* fullName)
|
{
|
auto it = g_fullName2signature.find(fullName);
|
return it != g_fullName2signature.end() ? it->second : "$";
|
}
|
|
static void* NotSupportInvoke(Il2CppMethodPointer, const MethodInfo* method, void*, void**)
|
{
|
char sigName[1000];
|
ComputeSignature(method, true, sigName, sizeof(sigName) - 1);
|
TEMP_FORMAT(errMsg, "Invoke method missing. sinature:%s %s.%s::%s", sigName, method->klass->namespaze, method->klass->name, method->name);
|
il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetExecutionEngineException(errMsg));
|
return nullptr;
|
}
|
|
template<typename T>
|
const Managed2NativeCallMethod GetManaged2NativeMethod(const T* method, bool forceStatic)
|
{
|
char sigName[1000];
|
ComputeSignature(method, !forceStatic, sigName, sizeof(sigName) - 1);
|
auto it = g_managed2natives.find(sigName);
|
return it != g_managed2natives.end() ? it->second : nullptr;
|
}
|
|
template<typename T>
|
const Il2CppMethodPointer GetNative2ManagedMethod(const T* method, bool forceStatic)
|
{
|
char sigName[1000];
|
ComputeSignature(method, !forceStatic, sigName, sizeof(sigName) - 1);
|
auto it = g_native2manageds.find(sigName);
|
return it != g_native2manageds.end() ? it->second : InterpreterModule::NotSupportNative2Managed;
|
}
|
|
template<typename T>
|
const Il2CppMethodPointer GetNativeAdjustMethodMethod(const T* method, bool forceStatic)
|
{
|
char sigName[1000];
|
ComputeSignature(method, !forceStatic, sigName, sizeof(sigName) - 1);
|
auto it = g_adjustThunks.find(sigName);
|
return it != g_adjustThunks.end() ? it->second : InterpreterModule::NotSupportAdjustorThunk;
|
}
|
|
static void RaiseMethodNotSupportException(const MethodInfo* method, const char* desc)
|
{
|
TEMP_FORMAT(errMsg, "%s. %s.%s::%s", desc, method->klass->namespaze, method->klass->name, method->name);
|
il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetExecutionEngineException(errMsg));
|
}
|
|
static void RaiseMethodNotSupportException(const Il2CppMethodDefinition* method, const char* desc)
|
{
|
Il2CppClass* klass = il2cpp::vm::GlobalMetadata::GetTypeInfoFromTypeDefinitionIndex(method->declaringType);
|
TEMP_FORMAT(errMsg, "%s. %s.%s::%s", desc, klass->namespaze, klass->name, il2cpp::vm::GlobalMetadata::GetStringFromIndex(method->nameIndex));
|
il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetExecutionEngineException(errMsg));
|
}
|
|
Il2CppMethodPointer InterpreterModule::GetMethodPointer(const Il2CppMethodDefinition* method)
|
{
|
Il2CppMethodPointer ncm = GetNative2ManagedMethod(method, false);
|
return ncm ? ncm : (Il2CppMethodPointer)NotSupportNative2Managed;
|
}
|
|
Il2CppMethodPointer InterpreterModule::GetMethodPointer(const MethodInfo* method)
|
{
|
Il2CppMethodPointer ncm = GetNative2ManagedMethod(method, false);
|
return ncm ? ncm : (Il2CppMethodPointer)NotSupportNative2Managed;
|
}
|
|
Il2CppMethodPointer InterpreterModule::GetAdjustThunkMethodPointer(const Il2CppMethodDefinition* method)
|
{
|
return GetNativeAdjustMethodMethod(method, false);
|
}
|
|
Il2CppMethodPointer InterpreterModule::GetAdjustThunkMethodPointer(const MethodInfo* method)
|
{
|
return GetNativeAdjustMethodMethod(method, false);
|
}
|
|
void InterpreterModule::Managed2NativeCallByReflectionInvoke(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
|
{
|
if (hybridclr::metadata::IsInterpreterImplement(method))
|
{
|
Interpreter::Execute(method, localVarBase + argVarIndexs[0], ret);
|
return;
|
}
|
if (method->invoker_method == nullptr)
|
{
|
char sigName[1000];
|
ComputeSignature(method, true, sigName, sizeof(sigName) - 1);
|
|
TEMP_FORMAT(errMsg, "GetManaged2NativeMethodPointer. sinature:%s not support.", sigName);
|
RaiseMethodNotSupportException(method, errMsg);
|
}
|
if (!InitAndGetInterpreterDirectlyCallMethodPointer(method))
|
{
|
RaiseAOTGenericMethodNotInstantiatedException(method);
|
}
|
void* thisPtr;
|
uint16_t* argVarIndexBase;
|
if (hybridclr::metadata::IsInstanceMethod(method))
|
{
|
thisPtr = localVarBase[argVarIndexs[0]].obj;
|
argVarIndexBase = argVarIndexs + 1;
|
}
|
else
|
{
|
thisPtr = nullptr;
|
argVarIndexBase = argVarIndexs;
|
}
|
void* invokeParams[256];
|
for (uint8_t i = 0; i < method->parameters_count; i++)
|
{
|
const Il2CppType* argType = GET_METHOD_PARAMETER_TYPE(method->parameters[i]);
|
StackObject* argValue = localVarBase + argVarIndexBase[i];
|
if (!argType->byref && hybridclr::metadata::IsValueType(argType))
|
{
|
invokeParams[i] = argValue;
|
}
|
else
|
{
|
invokeParams[i] = argValue->ptr;
|
}
|
}
|
#if HYBRIDCLR_UNITY_2021_OR_NEW
|
method->invoker_method(method->methodPointerCallByInterp, method, thisPtr, invokeParams, ret);
|
#else
|
void* retObj = method->invoker_method(method->methodPointerCallByInterp, method, thisPtr, invokeParams);
|
if (ret)
|
{
|
const Il2CppType* returnType = method->return_type;
|
if (hybridclr::metadata::IsValueType(returnType))
|
{
|
Il2CppClass* returnKlass = il2cpp::vm::Class::FromIl2CppType(returnType);
|
if (il2cpp::vm::Class::IsNullable(returnKlass))
|
{
|
il2cpp::vm::Object::UnboxNullable((Il2CppObject*)retObj, returnKlass->element_class, ret);
|
}
|
else
|
{
|
std::memcpy(ret, il2cpp::vm::Object::Unbox((Il2CppObject*)retObj), il2cpp::vm::Class::GetValueSize(returnKlass, nullptr));
|
}
|
}
|
else
|
{
|
*(void**)ret = retObj;
|
}
|
}
|
#endif
|
}
|
|
Managed2NativeCallMethod InterpreterModule::GetManaged2NativeMethodPointer(const MethodInfo* method, bool forceStatic)
|
{
|
if (method->methodPointerCallByInterp == NotSupportNative2Managed
|
#if HYBRIDCLR_UNITY_2021_OR_NEW
|
|| method->has_full_generic_sharing_signature
|
#endif
|
)
|
{
|
return Managed2NativeCallByReflectionInvoke;
|
}
|
char sigName[1000];
|
ComputeSignature(method, !forceStatic, sigName, sizeof(sigName) - 1);
|
auto it = g_managed2natives.find(sigName);
|
return it != g_managed2natives.end() ? it->second : Managed2NativeCallByReflectionInvoke;
|
}
|
|
Managed2NativeCallMethod InterpreterModule::GetManaged2NativeMethodPointer(const metadata::ResolveStandAloneMethodSig& method)
|
{
|
char sigName[1000];
|
ComputeSignature(&method.returnType, method.params, method.paramCount, metadata::IsPrologHasThis(method.flags), sigName, sizeof(sigName) - 1);
|
auto it = g_managed2natives.find(sigName);
|
return it != g_managed2natives.end() ? it->second : Managed2NativeCallByReflectionInvoke;
|
}
|
|
static void RaiseExecutionEngineExceptionMethodIsNotFound(const MethodInfo* method)
|
{
|
if (il2cpp::vm::Method::GetClass(method))
|
RaiseExecutionEngineException(il2cpp::vm::Method::GetFullName(method).c_str());
|
else
|
RaiseExecutionEngineException(il2cpp::vm::Method::GetNameWithGenericTypes(method).c_str());
|
}
|
|
#ifdef HYBRIDCLR_UNITY_2021_OR_NEW
|
|
static void InterpreterInvoke(Il2CppMethodPointer methodPointer, const MethodInfo* method, void* __this, void** __args, void* __ret)
|
{
|
InterpMethodInfo* imi = method->interpData ? (InterpMethodInfo*)method->interpData : InterpreterModule::GetInterpMethodInfo(method);
|
bool isInstanceMethod = metadata::IsInstanceMethod(method);
|
StackObject* args = (StackObject*)alloca(sizeof(StackObject) * imi->argStackObjectSize);
|
if (isInstanceMethod)
|
{
|
if (IS_CLASS_VALUE_TYPE(method->klass))
|
{
|
__this = (Il2CppObject*)__this + (methodPointer != method->methodPointerCallByInterp);
|
}
|
args[0].ptr = __this;
|
}
|
|
MethodArgDesc* argDescs = imi->args + isInstanceMethod;
|
ConvertInvokeArgs(args + isInstanceMethod, method, argDescs, __args);
|
Interpreter::Execute(method, args, __ret);
|
}
|
|
static void InterpreterDelegateInvoke(Il2CppMethodPointer, const MethodInfo* method, void* __this, void** __args, void* __ret)
|
{
|
Il2CppMulticastDelegate* del = (Il2CppMulticastDelegate*)__this;
|
Il2CppDelegate** firstSubDel;
|
int32_t subDelCount;
|
if (del->delegates)
|
{
|
firstSubDel = (Il2CppDelegate**)il2cpp::vm::Array::GetFirstElementAddress(del->delegates);
|
subDelCount = il2cpp::vm::Array::GetLength(del->delegates);
|
}
|
else
|
{
|
firstSubDel = (Il2CppDelegate**)&del;
|
subDelCount = 1;
|
}
|
|
for (int32_t i = 0; i < subDelCount; i++)
|
{
|
Il2CppDelegate* cur = firstSubDel[i];
|
const MethodInfo* curMethod = cur->method;
|
Il2CppObject* curTarget = cur->target;
|
if (curMethod->invoker_method == nullptr)
|
{
|
RaiseExecutionEngineExceptionMethodIsNotFound(curMethod);
|
}
|
if (!InitAndGetInterpreterDirectlyCallMethodPointer(curMethod))
|
{
|
RaiseAOTGenericMethodNotInstantiatedException(curMethod);
|
}
|
switch ((int)(method->parameters_count - curMethod->parameters_count))
|
{
|
case 0:
|
{
|
if (metadata::IsInstanceMethod(curMethod) && !curTarget)
|
{
|
il2cpp::vm::Exception::RaiseNullReferenceException();
|
}
|
curTarget += (IS_CLASS_VALUE_TYPE(curMethod->klass));
|
curMethod->invoker_method(curMethod->methodPointerCallByInterp, curMethod, curTarget, __args, __ret);
|
break;
|
}
|
case -1:
|
{
|
IL2CPP_ASSERT(!hybridclr::metadata::IsInstanceMethod(curMethod));
|
void** newArgs = (void**)alloca(sizeof(void*) * (size_t)curMethod->parameters_count);
|
newArgs[0] = curTarget;
|
for (int k = 0, endK = curMethod->parameters_count; k < endK; k++)
|
{
|
newArgs[k + 1] = __args[k];
|
}
|
curMethod->invoker_method(curMethod->methodPointerCallByInterp, curMethod, nullptr, newArgs, __ret);
|
break;
|
}
|
case 1:
|
{
|
IL2CPP_ASSERT(hybridclr::metadata::IsInstanceMethod(curMethod) && curMethod->parameters_count);
|
curTarget = (Il2CppObject*)__args[0];
|
if (!curTarget)
|
{
|
il2cpp::vm::Exception::RaiseNullReferenceException();
|
}
|
curMethod->invoker_method(curMethod->methodPointerCallByInterp, curMethod, curTarget, __args + 1, __ret);
|
break;
|
}
|
default:
|
{
|
RaiseExecutionEngineException("bad delegate method");
|
break;
|
}
|
}
|
}
|
}
|
#else
|
static void* InterpreterInvoke(Il2CppMethodPointer methodPointer, const MethodInfo* method, void* __this, void** __args)
|
{
|
StackObject args[256];
|
bool isInstanceMethod = metadata::IsInstanceMethod(method);
|
if (isInstanceMethod)
|
{
|
if (IS_CLASS_VALUE_TYPE(method->klass))
|
{
|
__this = (Il2CppObject*)__this + (methodPointer != method->methodPointerCallByInterp);
|
}
|
args[0].ptr = __this;
|
}
|
InterpMethodInfo* imi = method->interpData ? (InterpMethodInfo*)method->interpData : InterpreterModule::GetInterpMethodInfo(method);
|
MethodArgDesc* argDescs = imi->args + isInstanceMethod;
|
ConvertInvokeArgs(args + isInstanceMethod, method, argDescs, __args);
|
if (method->return_type->type == IL2CPP_TYPE_VOID)
|
{
|
Interpreter::Execute(method, args, nullptr);
|
return nullptr;
|
}
|
else
|
{
|
IL2CPP_ASSERT(GetTypeArgDesc(method->return_type).stackObjectSize <= hybridclr::metadata::kMaxRetValueTypeStackObjectSize);
|
StackObject ret[hybridclr::metadata::kMaxRetValueTypeStackObjectSize];
|
Interpreter::Execute(method, args, ret);
|
return TranslateNativeValueToBoxValue(method->return_type, ret);
|
}
|
}
|
|
static void* InterpreterDelegateInvoke(Il2CppMethodPointer, const MethodInfo* method, void* __this, void** __args)
|
{
|
Il2CppMulticastDelegate* del = (Il2CppMulticastDelegate*)__this;
|
Il2CppDelegate** firstSubDel;
|
int32_t subDelCount;
|
if (del->delegates)
|
{
|
firstSubDel = (Il2CppDelegate**)il2cpp::vm::Array::GetFirstElementAddress(del->delegates);
|
subDelCount = il2cpp::vm::Array::GetLength(del->delegates);
|
}
|
else
|
{
|
firstSubDel = (Il2CppDelegate**)&del;
|
subDelCount = 1;
|
}
|
void* ret = nullptr;
|
for (int32_t i = 0; i < subDelCount; i++)
|
{
|
Il2CppDelegate* cur = firstSubDel[i];
|
const MethodInfo* curMethod = cur->method;
|
Il2CppObject* curTarget = cur->target;
|
if (curMethod->invoker_method == nullptr)
|
{
|
RaiseExecutionEngineExceptionMethodIsNotFound(curMethod);
|
}
|
if (!InitAndGetInterpreterDirectlyCallMethodPointer(curMethod))
|
{
|
RaiseAOTGenericMethodNotInstantiatedException(curMethod);
|
}
|
switch ((int)(method->parameters_count - curMethod->parameters_count))
|
{
|
case 0:
|
{
|
if (metadata::IsInstanceMethod(curMethod) && !curTarget)
|
{
|
il2cpp::vm::Exception::RaiseNullReferenceException();
|
}
|
curTarget += (IS_CLASS_VALUE_TYPE(curMethod->klass));
|
ret = curMethod->invoker_method(curMethod->methodPointerCallByInterp, curMethod, curTarget, __args);
|
break;
|
}
|
case -1:
|
{
|
IL2CPP_ASSERT(!hybridclr::metadata::IsInstanceMethod(curMethod));
|
void** newArgs = (void**)alloca(sizeof(void*) * (size_t)curMethod->parameters_count);
|
newArgs[0] = curTarget;
|
for (int k = 0, endK = curMethod->parameters_count; k < endK; k++)
|
{
|
newArgs[k + 1] = __args[k];
|
}
|
ret = curMethod->invoker_method(curMethod->methodPointerCallByInterp, curMethod, nullptr, newArgs);
|
break;
|
}
|
case 1:
|
{
|
IL2CPP_ASSERT(hybridclr::metadata::IsInstanceMethod(curMethod) && curMethod->parameters_count);
|
curTarget = (Il2CppObject*)__args[0];
|
if (!curTarget)
|
{
|
il2cpp::vm::Exception::RaiseNullReferenceException();
|
}
|
ret = curMethod->invoker_method(curMethod->methodPointerCallByInterp, curMethod, curTarget, __args + 1);
|
break;
|
}
|
default:
|
{
|
RaiseExecutionEngineException("bad delegate method");
|
break;
|
}
|
}
|
}
|
return ret;
|
}
|
#endif
|
|
InvokerMethod InterpreterModule::GetMethodInvoker(const Il2CppMethodDefinition* method)
|
{
|
Il2CppClass* klass = il2cpp::vm::GlobalMetadata::GetTypeInfoFromTypeDefinitionIndex(method->declaringType);
|
const char* methodName = il2cpp::vm::GlobalMetadata::GetStringFromIndex(method->nameIndex);
|
// special for Delegate::DynamicInvoke
|
return !klass || !metadata::IsChildTypeOfMulticastDelegate(klass) || strcmp(methodName, "Invoke") ? InterpreterInvoke : InterpreterDelegateInvoke;
|
}
|
|
InvokerMethod InterpreterModule::GetMethodInvoker(const MethodInfo* method)
|
{
|
Il2CppClass* klass = method->klass;
|
return !klass || !metadata::IsChildTypeOfMulticastDelegate(klass) || strcmp(method->name, "Invoke") ? InterpreterInvoke : InterpreterDelegateInvoke;
|
}
|
|
bool InterpreterModule::IsImplementsByInterpreter(const MethodInfo* method)
|
{
|
return method->invoker_method == InterpreterDelegateInvoke || method->invoker_method == InterpreterInvoke;
|
}
|
|
InterpMethodInfo* InterpreterModule::GetInterpMethodInfo(const MethodInfo* methodInfo)
|
{
|
RuntimeInitClassCCtor(methodInfo);
|
il2cpp::os::FastAutoLock lock(&il2cpp::vm::g_MetadataLock);
|
|
if (methodInfo->interpData)
|
{
|
return (InterpMethodInfo*)methodInfo->interpData;
|
}
|
IL2CPP_ASSERT(methodInfo->isInterpterImpl);
|
|
metadata::Image* image = metadata::IsInterpreterMethod(methodInfo) ? hybridclr::metadata::MetadataModule::GetImage(methodInfo->klass)
|
: (metadata::Image*)hybridclr::metadata::AOTHomologousImage::FindImageByAssembly(
|
methodInfo->klass->rank ? il2cpp_defaults.corlib->assembly : methodInfo->klass->image->assembly);
|
IL2CPP_ASSERT(image);
|
|
metadata::MethodBody* methodBody = image->GetMethodBody(methodInfo->token);
|
if (methodBody == nullptr || methodBody->ilcodes == nullptr)
|
{
|
TEMP_FORMAT(errMsg, "Method body is null. %s.%s::%s", methodInfo->klass->namespaze, methodInfo->klass->name, methodInfo->name);
|
il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetExecutionEngineException(errMsg));
|
}
|
InterpMethodInfo* imi = new (HYBRIDCLR_MALLOC_ZERO(sizeof(InterpMethodInfo))) InterpMethodInfo;
|
transform::HiTransform::Transform(image, methodInfo, *methodBody, *imi);
|
il2cpp::os::Atomic::FullMemoryBarrier();
|
const_cast<MethodInfo*>(methodInfo)->interpData = imi;
|
return imi;
|
}
|
}
|
}
|