#if RUNTIME_MONO
|
|
#include <cassert>
|
|
#include "il2cpp-tokentype.h"
|
#include "il2cpp-class-internals.h"
|
#include "il2cpp-object-internals.h"
|
|
#include "il2cpp-mapping.h"
|
#include "il2cpp-mono-support.h"
|
|
#include "../libmono/mono-api.h"
|
#include "utils/dynamic_array.h"
|
#include "utils/StringUtils.h"
|
#if IL2CPP_ENABLE_NATIVE_STACKTRACES
|
#include "vm-utils/NativeSymbol.h"
|
#endif // IL2CPP_ENABLE_NATIVE_STACKTRACES
|
#include "utils/MarshalingUtils.h"
|
#include "../libmono/vm/MetadataCache.h"
|
#include <string>
|
#include "utils/Il2CppHashMap.h"
|
#include "utils/HashUtils.h"
|
#include "utils/PathUtils.h"
|
#include "utils/StringUtils.h"
|
#include "os/Mutex.h"
|
|
extern const Il2CppCodeRegistration g_CodeRegistration IL2CPP_ATTRIBUTE_WEAK;
|
extern "C" const Il2CppMethodSpec g_Il2CppMethodSpecTable[] IL2CPP_ATTRIBUTE_WEAK;
|
#if IL2CPP_ENABLE_NATIVE_STACKTRACES
|
#endif // IL2CPP_ENABLE_NATIVE_STACKTRACES
|
|
// Mono-specific metadata emitted by IL2CPP
|
extern "C" void** const g_MetadataUsages[] IL2CPP_ATTRIBUTE_WEAK;
|
extern "C" const MonoGenericInstMetadata* const g_MonoGenericInstMetadataTable[] IL2CPP_ATTRIBUTE_WEAK;
|
extern "C" const Il2CppCodeGenOptions s_Il2CppCodeGenOptions IL2CPP_ATTRIBUTE_WEAK;
|
|
extern const int g_Il2CppInteropDataCount IL2CPP_ATTRIBUTE_WEAK;
|
extern Il2CppInteropData g_Il2CppInteropData[] IL2CPP_ATTRIBUTE_WEAK;
|
|
static MonoGenericContext GetSharedContext(const MonoGenericContext* context);
|
static MonoAssembly** s_MonoAssemblies;
|
static int s_MonoAssembliesCount = 0;
|
|
MonoAssembly* il2cpp_mono_assembly_from_name(const char* name);
|
|
typedef Il2CppHashMap<uint64_t, const Il2CppInteropData*, il2cpp::utils::PassThroughHash<uint64_t> > InteropDataMap;
|
static InteropDataMap s_InteropDataMap;
|
|
typedef Il2CppHashMap<MonoClass*, const Il2CppInteropData*, il2cpp::utils::PointerHash<MonoClass> > InteropDataPointerCacheMap;
|
static InteropDataPointerCacheMap s_InteropDataPointerCacheMap;
|
static il2cpp::os::FastMutex s_InteropPointerCacheMutex;
|
|
MonoDomain *g_MonoDomain;
|
|
void il2cpp_mono_error_init(MonoError *oerror)
|
{
|
MonoErrorInternal *error = (MonoErrorInternal*)oerror;
|
error->error_code = MONO_ERROR_NONE;
|
error->flags = 0;
|
}
|
|
bool il2cpp_mono_error_ok(MonoError *error)
|
{
|
return error->error_code == MONO_ERROR_NONE;
|
}
|
|
static MonoGenericInst* GetSharedGenericInst(MonoGenericInst* inst)
|
{
|
std::vector<MonoType*> types;
|
unsigned int type_argc = mono_unity_generic_inst_get_type_argc(inst);
|
for (uint32_t i = 0; i < type_argc; ++i)
|
{
|
MonoType *type = mono_unity_generic_inst_get_type_argument(inst, i);
|
|
if (mono_type_is_reference(type))
|
{
|
types.push_back(mono_class_get_type(mono_get_object_class()));
|
}
|
else if (mono_unity_type_is_generic_instance(type))
|
{
|
MonoGenericClass *gclass = mono_unity_type_get_generic_class(type);
|
MonoGenericContext context = mono_unity_generic_class_get_context(gclass);
|
MonoGenericContext sharedContext = GetSharedContext(&context);
|
MonoClass *container_class = mono_unity_generic_class_get_container_class(gclass);
|
MonoError unused;
|
MonoClass *klass = mono_class_inflate_generic_class_checked(container_class, &sharedContext, &unused);
|
types.push_back(mono_class_get_type(klass));
|
}
|
else if (mono_unity_type_is_enum_type(type) && s_Il2CppCodeGenOptions.enablePrimitiveValueTypeGenericSharing)
|
{
|
MonoType* underlyingType = mono_type_get_underlying_type(type);
|
switch ((Il2CppTypeEnum)underlyingType->type)
|
{
|
case IL2CPP_TYPE_I1:
|
type = mono_class_get_type(mono_class_from_name(mono_get_corlib(), "System", "SByteEnum"));
|
break;
|
case IL2CPP_TYPE_I2:
|
type = mono_class_get_type(mono_class_from_name(mono_get_corlib(), "System", "Int16Enum"));
|
break;
|
case IL2CPP_TYPE_I4:
|
type = mono_class_get_type(mono_class_from_name(mono_get_corlib(), "System", "Int32Enum"));
|
break;
|
case IL2CPP_TYPE_I8:
|
type = mono_class_get_type(mono_class_from_name(mono_get_corlib(), "System", "Int64Enum"));
|
break;
|
case IL2CPP_TYPE_U1:
|
type = mono_class_get_type(mono_class_from_name(mono_get_corlib(), "System", "ByteEnum"));
|
break;
|
case IL2CPP_TYPE_U2:
|
type = mono_class_get_type(mono_class_from_name(mono_get_corlib(), "System", "UInt16Enum"));
|
break;
|
case IL2CPP_TYPE_U4:
|
type = mono_class_get_type(mono_class_from_name(mono_get_corlib(), "System", "UInt32Enum"));
|
break;
|
case IL2CPP_TYPE_U8:
|
type = mono_class_get_type(mono_class_from_name(mono_get_corlib(), "System", "UInt64Enum"));
|
break;
|
default:
|
IL2CPP_ASSERT(0 && "Invalid enum underlying type");
|
break;
|
}
|
types.push_back(type);
|
}
|
else
|
{
|
types.push_back(mono_unity_generic_inst_get_type_argument(inst, i));
|
}
|
}
|
|
return mono_metadata_get_generic_inst(type_argc, &types[0]);
|
}
|
|
static MonoGenericContext GetSharedContext(const MonoGenericContext* context)
|
{
|
MonoGenericContext newContext = {0};
|
if (context->class_inst != NULL)
|
newContext.class_inst = GetSharedGenericInst(context->class_inst);
|
|
if (context->method_inst != NULL)
|
newContext.method_inst = GetSharedGenericInst(context->method_inst);
|
|
return newContext;
|
}
|
|
static MonoMethod* InflateGenericMethodWithContext(MonoMethod* method, MonoGenericContext* context, bool useSharedVersion)
|
{
|
if (useSharedVersion)
|
{
|
MonoGenericContext sharedContext = GetSharedContext(context);
|
return mono_class_inflate_generic_method(method, &sharedContext);
|
}
|
|
return mono_class_inflate_generic_method(method, context);
|
}
|
|
void initialize_interop_data_map()
|
{
|
for (int i = 0; i < g_Il2CppInteropDataCount; ++i)
|
s_InteropDataMap.add(g_Il2CppInteropData[i].hash, &g_Il2CppInteropData[i]);
|
}
|
|
const Il2CppInteropData* FindInteropDataFor(MonoClass* klass)
|
{
|
il2cpp::os::FastAutoLock cacheLock(&s_InteropPointerCacheMutex);
|
InteropDataPointerCacheMap::const_iterator itCache = s_InteropDataPointerCacheMap.find(klass);
|
if (itCache != s_InteropDataPointerCacheMap.end())
|
return itCache->second;
|
|
uint64_t hash = mono_unity_type_get_hash(mono_class_get_type(klass), true);
|
|
InteropDataMap::const_iterator it = s_InteropDataMap.find(hash);
|
if (it != s_InteropDataMap.end())
|
{
|
const Il2CppInteropData *value = it->second;
|
s_InteropDataPointerCacheMap.add(klass, value);
|
return value;
|
}
|
|
return NULL;
|
}
|
|
static void StructureToPtr(MonoObject* structure, void* ptr, bool deleteOld)
|
{
|
if (structure == NULL)
|
mono_raise_exception(mono_get_exception_argument_null("structure"));
|
|
if (ptr == NULL)
|
mono_raise_exception(mono_get_exception_argument_null("ptr"));
|
|
MonoClass* klass = mono_object_get_class(structure);
|
const Il2CppInteropData* interopData = FindInteropDataFor(klass);
|
|
if (interopData != NULL && interopData->pinvokeMarshalToNativeFunction != NULL)
|
{
|
if (deleteOld)
|
il2cpp::utils::MarshalingUtils::MarshalFreeStruct(ptr, interopData);
|
|
void* objectPtr = mono_unity_class_is_class_type(klass) ? structure : mono_object_unbox(structure);
|
il2cpp::utils::MarshalingUtils::MarshalStructToNative(objectPtr, ptr, interopData);
|
return;
|
}
|
|
// If there's no custom marshal function, it means it's either a primitive, or invalid argument
|
|
uint32_t nativeSize = mono_unity_class_get_native_size(klass);
|
if (nativeSize != -1)
|
{
|
// StructureToPtr is supposed to throw on strings and enums
|
if (!mono_class_is_enum(klass) && !mono_unity_class_is_string(klass))
|
{
|
memcpy(ptr, mono_object_unbox(structure), nativeSize);
|
return;
|
}
|
}
|
|
// If we got this far, throw an exception
|
MonoException* exception;
|
|
if (mono_class_is_generic(klass))
|
{
|
exception = mono_get_exception_argument("structure", "The specified object must not be an instance of a generic type.");
|
}
|
else
|
{
|
exception = mono_get_exception_argument("structure", "The specified structure must be blittable or have layout information.");
|
}
|
|
mono_raise_exception(exception);
|
}
|
|
static void* MarshalInvokerMethod(Il2CppMethodPointer func, const MonoMethod* method, void* thisArg, void** params)
|
{
|
if (strcmp(mono_unity_method_get_name(method), "PtrToStructure") == 0)
|
{
|
const Il2CppInteropData* interopData = FindInteropDataFor(mono_unity_method_get_class(method));
|
if (interopData != NULL && mono_unity_class_is_class_type(mono_unity_method_get_class(method)))
|
il2cpp::utils::MarshalingUtils::MarshalStructFromNative((void*)(*(intptr_t*)params[0]), params[1], interopData);
|
else if (interopData != NULL)
|
il2cpp::utils::MarshalingUtils::MarshalStructFromNative((void*)(*(intptr_t*)params[0]), mono_object_unbox((MonoObject*)params[1]), interopData);
|
else // The type is blittable
|
memcpy(mono_object_unbox((MonoObject*)params[1]), (void*)(*(intptr_t*)params[0]), mono_unity_class_get_native_size(mono_unity_method_get_class(method)));
|
}
|
else if (strcmp(mono_unity_method_get_name(method), "StructureToPtr") == 0)
|
StructureToPtr((MonoObject*)params[0], (void*)(*(intptr_t*)params[1]), *(bool*)params[2]);
|
else
|
assert(0 && "Handle another special marshaling method");
|
|
return (MonoObject*)params[1];
|
}
|
|
Il2CppCodeGenModule* InitializeCodeGenHandle(MonoImage* image)
|
{
|
if (image->il2cpp_codegen_handle)
|
return (Il2CppCodeGenModule*)image->il2cpp_codegen_handle;
|
|
for (uint32_t codeGenModuleIndex = 0; codeGenModuleIndex < g_CodeRegistration.codeGenModulesCount; ++codeGenModuleIndex)
|
{
|
std::string name = il2cpp::utils::PathUtils::PathNoExtension(g_CodeRegistration.codeGenModules[codeGenModuleIndex]->moduleName);
|
if (strcmp(image->assembly_name, name.c_str()) == 0)
|
{
|
image->il2cpp_codegen_handle = (void*)g_CodeRegistration.codeGenModules[codeGenModuleIndex];
|
return (Il2CppCodeGenModule*)image->il2cpp_codegen_handle;
|
}
|
}
|
|
return NULL;
|
}
|
|
static il2cpp::os::FastMutex s_MonoMethodFunctionPointerInitializationMutex;
|
|
void il2cpp_mono_method_initialize_function_pointers(MonoMethod* method, MonoError* error)
|
{
|
il2cpp::os::FastAutoLock lock(&s_MonoMethodFunctionPointerInitializationMutex);
|
|
bool* isSpecialMarshalingMethod = NULL;
|
assert(method != NULL);
|
|
int32_t invokerIndex = -1;
|
int32_t methodPointerIndex = -1;
|
|
il2cpp_mono_error_init(error);
|
|
if (method->invoke_pointer || method->method_pointer)
|
return;
|
|
//char *methodName = mono_method_get_name_full(method, true, false, MONO_TYPE_NAME_FORMAT_IL);
|
|
if (unity_mono_method_is_inflated(method))
|
{
|
// Use the fully shared version to look up the method pointer, as we will invoke the fully shared version.
|
MonoMethod* definition = mono_method_get_method_definition(method);
|
MonoMethod* sharedMethod = InflateGenericMethodWithContext(definition, mono_method_get_context(method), true);
|
|
//char *newMethodName = mono_method_get_name_full(sharedMethod, true, false, MONO_TYPE_NAME_FORMAT_IL);
|
|
uint64_t hash = mono_unity_method_get_hash(sharedMethod, true);
|
const MonoMethodInfoMetadata *methodInfo = mono::vm::MetadataCache::GetMonoGenericMethodInfoFromMethodHash(hash);
|
|
if (methodInfo)
|
{
|
invokerIndex = methodInfo->invoker_index;
|
methodPointerIndex = methodInfo->method_pointer_index;
|
}
|
else
|
{
|
mono_error_set_execution_engine(error, "Unable to create the method '%s' at runtime", mono_unity_method_get_name(method));
|
return;
|
}
|
|
assert(invokerIndex != -1 && "The method does not have an invoker, is it a generic method?");
|
assert((uint32_t)invokerIndex < g_CodeRegistration.invokerPointersCount);
|
mono_unity_method_set_invoke_pointer(method, (void*)g_CodeRegistration.invokerPointers[invokerIndex]);
|
|
assert((uint32_t)methodPointerIndex < g_CodeRegistration.genericMethodPointersCount);
|
mono_unity_method_set_method_pointer(method, (void*)g_CodeRegistration.genericMethodPointers[methodPointerIndex]);
|
}
|
else
|
{
|
InitializeCodeGenHandle(method->klass->image);
|
Il2CppCodeGenModule* codeGenModule = (Il2CppCodeGenModule*)method->klass->image->il2cpp_codegen_handle;
|
|
if (!codeGenModule)
|
{
|
// Throw a missing method exception if we did not convert it. Once we know this code works for all runtime invoke cases,
|
// we should throw this exception more often. Until then, we will leave theassert below when we don't find a method.
|
mono_error_set_method_load(error, mono_unity_method_get_class(method), mono_unity_method_get_name(method), NULL, "This method was not converted ahead-of-time by IL2CPP.");
|
return;
|
}
|
else if (strcmp(mono_unity_method_get_name(method), "PtrToStructure") == 0 || strcmp(mono_unity_method_get_name(method), "StructureToPtr") == 0)
|
{
|
mono_unity_method_set_invoke_pointer(method, (void*)&MarshalInvokerMethod);
|
return;
|
}
|
|
invokerIndex = method->token & 0x00FFFFFF;
|
methodPointerIndex = method->token & 0x00FFFFFF;
|
|
invokerIndex = method->token & 0x00FFFFFF;
|
assert((uint32_t)invokerIndex <= codeGenModule->methodPointerCount);
|
invokerIndex = codeGenModule->invokerIndices[invokerIndex - 1];
|
methodPointerIndex = method->token & 0x00FFFFFF;
|
|
assert(invokerIndex != -1 && "The method does not have an invoker, is it a generic method?");
|
assert((uint32_t)invokerIndex < g_CodeRegistration.invokerPointersCount);
|
mono_unity_method_set_invoke_pointer(method, (void*)g_CodeRegistration.invokerPointers[invokerIndex]);
|
|
assert((uint32_t)methodPointerIndex <= codeGenModule->methodPointerCount);
|
mono_unity_method_set_method_pointer(method, (void*)codeGenModule->methodPointers[methodPointerIndex - 1]);
|
}
|
}
|
|
void il2cpp_mono_init_assemblies()
|
{
|
s_MonoAssembliesCount = mono::vm::MetadataCache::GetMonoAssemblyCount();
|
s_MonoAssemblies = new MonoAssembly*[mono::vm::MetadataCache::GetMonoAssemblyCount()];
|
for (int i = 0; i < s_MonoAssembliesCount; ++i)
|
s_MonoAssemblies[i] = il2cpp_mono_assembly_from_name(mono::vm::MetadataCache::GetMonoAssemblyNameFromIndex(i));
|
}
|
|
MonoAssembly* il2cpp_mono_assembly_from_index(AssemblyIndex index)
|
{
|
if (s_MonoAssembliesCount == 0)
|
il2cpp_mono_init_assemblies();
|
|
assert(index < s_MonoAssembliesCount && "assembly index is out of range");
|
return s_MonoAssemblies[index];
|
}
|
|
MonoAssembly* il2cpp_mono_assembly_from_name(const char* name)
|
{
|
// First look for the cached assembly in the domain.
|
MonoAssembly* assembly = mono_domain_assembly_open(g_MonoDomain, name);
|
if (assembly == NULL)
|
{
|
// If we can't find it, look in the path we've set to find assemblies.
|
MonoImageOpenStatus unused;
|
assembly = mono_assembly_load_with_partial_name(name, &unused);
|
}
|
|
return assembly;
|
}
|
|
MonoMethod* il2cpp_mono_get_virtual_target_method(MonoMethod* method, MonoObject* obj)
|
{
|
// This is *very* slow, and likely not good enough for production. It is good enough for proof of concept though.
|
MonoMethod* target_method = mono_object_get_virtual_method(obj, method);
|
|
if (mono_unity_class_is_array(mono_unity_method_get_class(target_method)))
|
target_method = mono_unity_method_get_aot_array_helper_from_wrapper(target_method);
|
|
return target_method;
|
}
|
|
MonoMethod* il2cpp_mono_get_virtual_target_method_fast(MonoMethod* method, MonoObject* obj)
|
{
|
if (method->flags & METHOD_ATTRIBUTE_FINAL)
|
return method;
|
|
MonoClass *klass = obj->vtable->klass;
|
if (!klass->vtable)
|
mono_class_setup_vtable(klass);
|
|
MonoMethod* target_method = klass->vtable[method->slot];
|
|
if (target_method->klass->rank > 0)
|
target_method = mono_unity_method_get_aot_array_helper_from_wrapper(target_method);
|
|
return target_method;
|
}
|
|
MonoMethod* il2cpp_mono_get_interface_target_method_fast(MonoMethod* method, MonoObject* obj)
|
{
|
if (method->flags & METHOD_ATTRIBUTE_FINAL)
|
return method;
|
|
MonoClass *klass = obj->vtable->klass;
|
if (!klass->vtable)
|
mono_class_setup_vtable(klass);
|
|
gboolean variance_used = 0;
|
int iface_offset = mono_class_interface_offset_with_variance(klass, method->klass, &variance_used);
|
MonoMethod* target_method = klass->vtable[iface_offset + method->slot];
|
|
if (target_method->klass->rank > 0)
|
target_method = mono_unity_method_get_aot_array_helper_from_wrapper(target_method);
|
|
return target_method;
|
}
|
|
void il2cpp_mono_get_invoke_data(MonoMethod* method, void* obj, VirtualInvokeData* invokeData)
|
{
|
MonoMethod* target_method = il2cpp_mono_get_virtual_target_method(method, (MonoObject*)obj);
|
|
MonoError error;
|
il2cpp_mono_method_initialize_function_pointers(target_method, &error);
|
if (!il2cpp_mono_error_ok(&error))
|
mono_error_raise_exception_deprecated(&error);
|
invokeData->methodPtr = (Il2CppMethodPointer)mono_unity_method_get_method_pointer(target_method);
|
invokeData->method = target_method;
|
}
|
|
void il2cpp_mono_get_virtual_invoke_data(MonoMethod* method, void* obj, VirtualInvokeData* invokeData)
|
{
|
MonoMethod* target_method = il2cpp_mono_get_virtual_target_method_fast(method, (MonoObject*)obj);
|
|
MonoError error;
|
il2cpp_mono_method_initialize_function_pointers(target_method, &error);
|
if (!il2cpp_mono_error_ok(&error))
|
mono_error_raise_exception_deprecated(&error);
|
invokeData->methodPtr = (Il2CppMethodPointer)mono_unity_method_get_method_pointer(target_method);
|
invokeData->method = target_method;
|
}
|
|
void il2cpp_mono_get_interface_invoke_data(MonoMethod* method, void* obj, VirtualInvokeData* invokeData)
|
{
|
MonoMethod* target_method = il2cpp_mono_get_interface_target_method_fast(method, (MonoObject*)obj);
|
|
MonoError error;
|
il2cpp_mono_method_initialize_function_pointers(target_method, &error);
|
if (!il2cpp_mono_error_ok(&error))
|
mono_error_raise_exception_deprecated(&error);
|
invokeData->methodPtr = (Il2CppMethodPointer)mono_unity_method_get_method_pointer(target_method);
|
invokeData->method = target_method;
|
}
|
|
static MonoClass* InflateGenericParameter(MonoGenericContainer* genericContainer, MonoGenericContext* context, const MonoRGCTXDefinition* rgctxDefintion)
|
{
|
MonoClass* genericParameter = mono_unity_generic_container_get_parameter_class(genericContainer, rgctxDefintion->generic_parameter_index);
|
return mono_unity_class_inflate_generic_class(genericParameter, context);
|
}
|
|
static MonoClass* LookupGenericClassMetadata(MonoGenericContext* context, const MonoRGCTXDefinition* rgctxDefintion)
|
{
|
return mono_class_get_full(mono_assembly_get_image(il2cpp_mono_assembly_from_index(rgctxDefintion->assemblyIndex)), rgctxDefintion->token, context);
|
}
|
|
static void* LookupMetadataFromRGCTX(const MonoRGCTXDefinition* definition, bool useSharedVersion, MonoGenericContext* context, MonoGenericContainer* genericContainer)
|
{
|
if (definition->type == IL2CPP_RGCTX_DATA_CLASS)
|
{
|
if (mono_metadata_token_table(definition->token) == MONO_TABLE_GENERICPARAM)
|
return InflateGenericParameter(genericContainer, context, definition);
|
else if (mono_metadata_token_table(definition->token) == MONO_TABLE_TYPESPEC)
|
return LookupGenericClassMetadata(context, definition);
|
else
|
assert(0 && "RGCTX case is not implemented yet.");
|
}
|
else if (definition->type == IL2CPP_RGCTX_DATA_ARRAY)
|
{
|
if (mono_metadata_token_table(definition->token) == MONO_TABLE_GENERICPARAM)
|
return mono_array_class_get(InflateGenericParameter(genericContainer, context, definition), 1);
|
else if (mono_metadata_token_table(definition->token) == MONO_TABLE_TYPESPEC)
|
return mono_array_class_get(LookupGenericClassMetadata(context, definition), 1);
|
else
|
assert(0 && "RGCTX case is not implemented yet.");
|
}
|
else if (definition->type == IL2CPP_RGCTX_DATA_METHOD)
|
{
|
MonoMethod* methodDefinition = mono_get_method(mono_assembly_get_image(il2cpp_mono_assembly_from_index(definition->assemblyIndex)), definition->token, NULL);
|
return InflateGenericMethodWithContext(methodDefinition, context, useSharedVersion);
|
}
|
else if (definition->type == IL2CPP_RGCTX_DATA_TYPE)
|
{
|
if (mono_metadata_token_table(definition->token) == MONO_TABLE_GENERICPARAM)
|
return mono_class_get_type(InflateGenericParameter(genericContainer, context, definition));
|
else if (mono_metadata_token_table(definition->token) == MONO_TABLE_TYPESPEC)
|
return mono_class_get_type(LookupGenericClassMetadata(context, definition));
|
else
|
assert(0 && "RGCTX case is not implemented yet.");
|
}
|
else
|
{
|
assert(0 && "RGCTX case is not implemented yet.");
|
}
|
|
return NULL;
|
}
|
|
static int CompareIl2CppTokenRangePair(const void* pkey, const void* pelem)
|
{
|
return (int)(((Il2CppTokenRangePair*)pkey)->token - ((Il2CppTokenRangePair*)pelem)->token);
|
}
|
|
typedef struct
|
{
|
int32_t count;
|
const MonoRGCTXDefinition* items;
|
} RGCTXCollection;
|
|
static RGCTXCollection GetRGCTXs(MonoImage* image, uint32_t token)
|
{
|
InitializeCodeGenHandle(image);
|
Il2CppCodeGenModule* codegenModule = (Il2CppCodeGenModule*)image->il2cpp_codegen_handle;
|
RGCTXCollection collection = { 0, NULL };
|
if (codegenModule->rgctxRangesCount == 0)
|
return collection;
|
|
Il2CppTokenRangePair key;
|
memset(&key, 0, sizeof(Il2CppTokenRangePair));
|
key.token = token;
|
|
const Il2CppTokenRangePair* res = (const Il2CppTokenRangePair*)bsearch(&key, codegenModule->rgctxRanges, codegenModule->rgctxRangesCount, sizeof(Il2CppTokenRangePair), CompareIl2CppTokenRangePair);
|
|
if (res == NULL)
|
return collection;
|
|
collection.count = res->range.length;
|
collection.items = codegenModule->rgctxs + res->range.start;
|
|
return collection;
|
}
|
|
void* LookupMetadataFromHash(MonoImage* image, uint32_t token, Il2CppRGCTXDataType rgctxType, int rgctxIndex, bool useSharedVersion, MonoGenericContext* context, MonoGenericContainer* genericContainer)
|
{
|
RGCTXCollection collection = GetRGCTXs(image, token);
|
if (rgctxIndex >= 0 && rgctxIndex < collection.count)
|
{
|
const MonoRGCTXDefinition* definition = collection.items + rgctxIndex;
|
if (definition->type == rgctxType)
|
return LookupMetadataFromRGCTX(definition, useSharedVersion, context, genericContainer);
|
}
|
|
return NULL;
|
}
|
|
void* il2cpp_mono_class_rgctx(MonoClass* klass, Il2CppRGCTXDataType rgctxType, int rgctxIndex, bool useSharedVersion)
|
{
|
return LookupMetadataFromHash(klass->image, klass->type_token, rgctxType, rgctxIndex, useSharedVersion,
|
mono_class_get_context(klass), mono_class_get_generic_container(mono_unity_class_get_generic_definition(klass)));
|
}
|
|
void* il2cpp_mono_method_rgctx(MonoMethod* method, Il2CppRGCTXDataType rgctxType, int rgctxIndex, bool useSharedVersion)
|
{
|
return LookupMetadataFromHash(method->klass->image, method->token, rgctxType, rgctxIndex, useSharedVersion,
|
mono_method_get_context(method), mono_method_get_generic_container(mono_unity_method_get_generic_definition(method)));
|
}
|
|
static MonoClass* ClassFromIndex(TypeIndex index);
|
|
static MonoClass* InflateGenericClass(MonoClass* generic_class, uint32_t num_indices, const TypeIndex *generic_indices)
|
{
|
std::vector<MonoType*> arguments(num_indices);
|
for (uint32_t i = 0; i < num_indices; ++i)
|
arguments[i] = mono_class_get_type(ClassFromIndex(generic_indices[i]));
|
|
MonoGenericInst* generic_instance = mono_metadata_get_generic_inst(num_indices, &arguments[0]);
|
MonoGenericContext context;
|
context.class_inst = generic_instance;
|
context.method_inst = NULL;
|
MonoError unused;
|
MonoClass* inflated_class = mono_class_inflate_generic_class_checked(generic_class, &context, &unused);
|
mono_class_init(inflated_class);
|
|
return inflated_class;
|
}
|
|
static MonoClass* ClassFromMetadata(const MonoClassMetadata *classMetadata)
|
{
|
if (classMetadata->rank > 0)
|
{
|
MonoClass *klass = ClassFromIndex(classMetadata->elementTypeIndex);
|
MonoClass *aklass = mono_array_class_get(klass, classMetadata->rank);
|
mono_class_init(aklass);
|
return aklass;
|
}
|
|
MonoClass *klass = mono_class_get(mono_assembly_get_image(il2cpp_mono_assembly_from_index(classMetadata->metadataToken.assemblyIndex)), classMetadata->metadataToken.token);
|
mono_class_init(klass);
|
return klass;
|
}
|
|
static MonoClass* ClassFromIndex(TypeIndex index)
|
{
|
if (index == kTypeIndexInvalid)
|
return NULL;
|
|
const MonoClassMetadata* classMetadata = mono::vm::MetadataCache::GetClassMetadataFromIndex(index);
|
|
MonoClass* klass = ClassFromMetadata(classMetadata);
|
if (classMetadata->genericParametersCount != 0)
|
klass = InflateGenericClass(klass, classMetadata->genericParametersCount, mono::vm::MetadataCache::GetGenericArgumentIndices(classMetadata->genericParametersOffset));
|
|
if (classMetadata->isPointer)
|
klass = mono_ptr_class_get(mono_class_get_type(klass));
|
|
return klass;
|
}
|
|
static MonoClassField* FieldFromIndex(EncodedMethodIndex index)
|
{
|
const MonoFieldMetadata* fieldMetadata = mono::vm::MetadataCache::GetFieldMetadataFromIndex(index);
|
return mono_class_get_field(ClassFromIndex(fieldMetadata->parentTypeIndex), fieldMetadata->token);
|
}
|
|
static MonoType* TypeFromIndex(TypeIndex index)
|
{
|
if (index == kTypeIndexInvalid)
|
return NULL;
|
|
return mono_class_get_type(ClassFromIndex(index));
|
}
|
|
MonoMethod* MethodFromIndex(MethodIndex index)
|
{
|
const MonoMetadataToken* method = &mono::vm::MetadataCache::GetMonoMethodMetadataFromIndex(index)->metadataToken;
|
return mono_get_method(mono_assembly_get_image(il2cpp_mono_assembly_from_index(method->assemblyIndex)), method->token, NULL);
|
}
|
|
MonoMethod* MethodFromToken(uint32_t token, Il2CppMethodPointer methodPtr)
|
{
|
uint32_t rid = 0x00FFFFFF & token;
|
for (uint32_t codeGenModuleIndex = 0; codeGenModuleIndex < g_CodeRegistration.codeGenModulesCount; ++codeGenModuleIndex)
|
{
|
const Il2CppCodeGenModule* codeGenModule = g_CodeRegistration.codeGenModules[codeGenModuleIndex];
|
if (rid <= codeGenModule->methodPointerCount && codeGenModule->methodPointers[rid - 1] == methodPtr)
|
{
|
MonoAssembly* assembly = il2cpp_mono_assembly_from_name(il2cpp::utils::PathUtils::PathNoExtension(codeGenModule->moduleName).c_str());
|
|
return mono_get_method(mono_assembly_get_image(assembly), token, NULL);
|
}
|
}
|
|
return NULL;
|
}
|
|
static std::vector<MonoType*> GenericArgumentsFromInst(const MonoGenericInstMetadata *inst)
|
{
|
std::vector<MonoType*> arguments;
|
|
uint32_t numberOfGenericArguments = inst->type_argc;
|
if (numberOfGenericArguments == 0)
|
return arguments;
|
|
for (uint32_t i = 0; i < numberOfGenericArguments; ++i)
|
{
|
const TypeIndex argIndex = inst->type_argv_indices[i];
|
arguments.push_back(mono_class_get_type(ClassFromIndex(argIndex)));
|
}
|
|
return arguments;
|
}
|
|
static MonoGenericInst* GenericInstFromIndex(GenericInstIndex index)
|
{
|
if (index == -1)
|
return NULL;
|
|
const MonoGenericInstMetadata* inst = g_MonoGenericInstMetadataTable[index];
|
|
// Replace this with dynamic_array later when we get the libil2cpp utilities build sorted out.
|
std::vector<MonoType*> genericArguments = GenericArgumentsFromInst(inst);
|
|
size_t numberOfGenericArguments = genericArguments.size();
|
if (numberOfGenericArguments == 0)
|
return NULL;
|
|
return mono_metadata_get_generic_inst((int)numberOfGenericArguments, &genericArguments[0]);
|
}
|
|
MonoMethod* GenericMethodFromIndex(MethodIndex index)
|
{
|
const Il2CppMethodSpec* methodSpec = &g_Il2CppMethodSpecTable[index];
|
|
MonoMethod* methodDefinition = MethodFromIndex(methodSpec->methodDefinitionIndex);
|
MonoGenericContext generic_context = { GenericInstFromIndex(methodSpec->classIndexIndex) , GenericInstFromIndex(methodSpec->methodIndexIndex) };
|
|
return mono_class_inflate_generic_method(methodDefinition, &generic_context);
|
}
|
|
static MonoString* StringFromIndex(StringIndex index)
|
{
|
const MonoMetadataToken* stringMetadata = mono::vm::MetadataCache::GetMonoStringTokenFromIndex(index);
|
return mono_ldstr(g_MonoDomain, mono_assembly_get_image(il2cpp_mono_assembly_from_index(stringMetadata->assemblyIndex)), mono_metadata_token_index(stringMetadata->token));
|
}
|
|
void il2cpp_mono_initialize_method_metadata(uint32_t index)
|
{
|
const Il2CppMetadataUsageList* usageList = mono::vm::MetadataCache::GetMetadataUsageList(index);
|
|
uint32_t start = usageList->start;
|
uint32_t count = usageList->count;
|
|
for (uint32_t i = 0; i < count; i++)
|
{
|
uint32_t offset = start + i;
|
|
const Il2CppMetadataUsagePair* usagePair = mono::vm::MetadataCache::GetMetadataUsagePair(offset);
|
uint32_t destinationIndex = usagePair->destinationIndex;
|
uint32_t encodedSourceIndex = usagePair->encodedSourceIndex;
|
|
Il2CppMetadataUsage usage = GetEncodedIndexType(encodedSourceIndex);
|
uint32_t decodedIndex = GetDecodedMethodIndex(encodedSourceIndex);
|
switch (usage)
|
{
|
case kIl2CppMetadataUsageTypeInfo:
|
*g_MetadataUsages[destinationIndex] = ClassFromIndex(decodedIndex);
|
break;
|
case kIl2CppMetadataUsageFieldInfo:
|
*g_MetadataUsages[destinationIndex] = FieldFromIndex(decodedIndex);
|
break;
|
case kIl2CppMetadataUsageIl2CppType:
|
*g_MetadataUsages[destinationIndex] = TypeFromIndex(decodedIndex);
|
break;
|
case kIl2CppMetadataUsageMethodDef:
|
*g_MetadataUsages[destinationIndex] = MethodFromIndex(decodedIndex);
|
break;
|
case kIl2CppMetadataUsageMethodRef:
|
*g_MetadataUsages[destinationIndex] = GenericMethodFromIndex(decodedIndex);
|
break;
|
case kIl2CppMetadataUsageStringLiteral:
|
*g_MetadataUsages[destinationIndex] = StringFromIndex(decodedIndex);
|
break;
|
default:
|
assert(0 && "Unimplemented case for method metadata usage.");
|
break;
|
}
|
}
|
}
|
|
void il2cpp_mono_raise_execution_engine_exception_if_method_is_not_found(MonoMethod* method)
|
{
|
MonoError unused;
|
il2cpp_mono_method_initialize_function_pointers(method, &unused);
|
|
if (mono_unity_method_get_method_pointer(method) == NULL)
|
{
|
MonoException *exc;
|
|
if (mono_unity_method_get_class(method) != NULL)
|
exc = mono_get_exception_execution_engine(mono_method_get_name_full(method, true, false, MONO_TYPE_NAME_FORMAT_IL));
|
else
|
exc = mono_get_exception_execution_engine(mono_unity_method_get_name(method));
|
|
mono_raise_exception(exc);
|
}
|
}
|
|
Il2CppAsyncResult* il2cpp_mono_delegate_begin_invoke(MonoDelegate* delegate, void** params, MonoDelegate* asyncCallback, MonoObject* state)
|
{
|
int numParams = mono_signature_get_param_count(mono_method_signature((MonoMethod*)delegate->method));
|
MonoObject *nullState = NULL;
|
MonoDelegate *nullCallback = NULL;
|
|
std::vector<void*> newParams(numParams + 2);
|
for (int i = 0; i < numParams; ++i)
|
newParams[i] = params[i];
|
|
//If the state and the callback are both null and there are no other params, we will send null for the params array
|
newParams[numParams] = &nullCallback;
|
newParams[numParams + 1] = &nullState;
|
int numNewParams = numParams;
|
|
if (asyncCallback)
|
{
|
newParams[numParams] = &asyncCallback;
|
++numNewParams;
|
}
|
|
if (state)
|
{
|
newParams[numParams + 1] = &state;
|
++numNewParams;
|
}
|
|
MonoError unused;
|
return (Il2CppAsyncResult*)mono_threadpool_begin_invoke(g_MonoDomain, (MonoObject*)delegate, (MonoMethod*)delegate->method, numNewParams != 0 ? &newParams[0] : NULL, &unused);
|
}
|
|
static void MonoArrayGetGenericValue(MonoArray* array, int32_t pos, void* value)
|
{
|
MonoObject *obj = mono_array_get(array, MonoObject*, pos);
|
MonoClass *klass = mono_unity_object_get_class(obj);
|
|
if (mono_class_is_valuetype(klass))
|
memcpy(value, mono_object_unbox(obj), mono_class_instance_size(klass) - sizeof(MonoObject));
|
else
|
memcpy(value, &obj, mono_unity_array_get_element_size(array));
|
}
|
|
MonoObject* il2cpp_mono_delegate_end_invoke(Il2CppAsyncResult* asyncResult, void **out_args)
|
{
|
MonoError unused;
|
MonoObject *exc = NULL;
|
MonoArray *mono_out_args = NULL;
|
MonoObject *retVal = (MonoObject*)mono_threadpool_end_invoke((MonoAsyncResult*)asyncResult, &mono_out_args, &exc, &unused);
|
|
if (exc)
|
mono_raise_exception((MonoException*)exc);
|
|
uint32_t numArgs = mono_unity_array_get_max_length(mono_out_args);
|
|
for (uint32_t i = 0; i < numArgs; ++i)
|
MonoArrayGetGenericValue(mono_out_args, i, out_args[i]);
|
|
return retVal;
|
}
|
|
MonoArray* MonoArrayNew(MonoClass* elementType, uintptr_t length)
|
{
|
MonoError unused;
|
return mono_array_new_specific_checked(il2cpp_mono_class_vtable(g_MonoDomain, mono_array_class_get(elementType, 1)), length, &unused);
|
}
|
|
#if IL2CPP_ENABLE_NATIVE_STACKTRACES
|
void RegisterAllManagedMethods()
|
{
|
uint32_t numberOfMethods = 0;
|
uint32_t numberOfGenericMethods = g_CodeRegistration.genericMethodPointersCount;
|
|
for (uint32_t codeGenModuleIndex = 0; codeGenModuleIndex < g_CodeRegistration.codeGenModulesCount; ++codeGenModuleIndex)
|
{
|
numberOfMethods += g_CodeRegistration.codeGenModules[codeGenModuleIndex]->methodPointerCount;
|
}
|
|
std::vector<MethodDefinitionKey> managedMethods(numberOfMethods + numberOfGenericMethods);
|
|
for (uint32_t codeGenModuleIndex = 0; codeGenModuleIndex < g_CodeRegistration.codeGenModulesCount; ++codeGenModuleIndex)
|
{
|
const Il2CppCodeGenModule* codeGenModule = g_CodeRegistration.codeGenModules[codeGenModuleIndex];
|
for (uint32_t i = 0; i < codeGenModule->methodPointerCount; ++i)
|
{
|
MethodDefinitionKey currentMethod;
|
currentMethod.methodIndex = IL2CPP_TOKEN_METHOD_DEF | (i + 1); // produce method token
|
currentMethod.method = codeGenModule->methodPointers[i];
|
currentMethod.isGeneric = false;
|
managedMethods.push_back(currentMethod);
|
}
|
}
|
|
for (uint32_t i = 0; i < numberOfGenericMethods; ++i)
|
{
|
MethodDefinitionKey currentMethod;
|
currentMethod.methodIndex = mono::vm::MetadataCache::GetGenericMethodIndex(i);
|
currentMethod.method = g_CodeRegistration.genericMethodPointers[i];
|
currentMethod.isGeneric = true;
|
managedMethods.push_back(currentMethod);
|
}
|
|
il2cpp::utils::NativeSymbol::RegisterMethods(managedMethods);
|
}
|
|
#endif // IL2CPP_ENABLE_NATIVE_STACKTRACES
|
|
void RuntimeInit(MonoClass* klass)
|
{
|
MonoError error;
|
mono_runtime_class_init_full(il2cpp_mono_class_vtable(g_MonoDomain, klass), &error);
|
if (!il2cpp_mono_error_ok(&error))
|
mono_error_raise_exception_deprecated(&error);
|
}
|
|
std::string il2cpp_mono_format_exception(const MonoException *exc)
|
{
|
MonoClass *klass = mono_object_get_class((MonoObject*)exc);
|
std::string exception_namespace = mono_class_get_namespace(klass);
|
std::string exception_type = mono_class_get_name(klass);
|
MonoString *message = mono_unity_exception_get_message(const_cast<MonoException*>(exc));
|
if (message)
|
return exception_namespace + "." + exception_type + ": " + mono_string_to_utf8(message);
|
else
|
return exception_namespace + "." + exception_type;
|
}
|
|
void* il2cpp_mono_get_static_field_address(MonoClass *klass, MonoClassField *field)
|
{
|
MonoVTable *vt = il2cpp_mono_class_vtable(g_MonoDomain, klass);
|
return (char*)vt->vtable[vt->klass->vtable_size] + field->offset;
|
}
|
|
void* il2cpp_mono_get_thread_static_field_address(MonoClass *klass, MonoClassField *field)
|
{
|
MonoVTable *vt = il2cpp_mono_class_vtable(g_MonoDomain, klass);
|
return mono_unity_get_field_address(NULL, vt, field);
|
}
|
|
MonoVTable* il2cpp_mono_class_vtable(MonoDomain *domain, MonoClass *klass)
|
{
|
MonoClassRuntimeInfo *runtime_info;
|
|
runtime_info = klass->runtime_info;
|
if (runtime_info)
|
return runtime_info->domain_vtables[0];
|
|
MonoError unused;
|
return mono_class_vtable_full(domain, klass, &unused);
|
}
|
|
ArgvMono il2cpp_mono_convert_args(int argc, const Il2CppChar* const* argv)
|
{
|
ArgvMono args;
|
|
args.argc = argc;
|
|
args.argvMonoObj = new std::string*[argc];
|
for (int i = 0; i < argc; ++i)
|
args.argvMonoObj[i] = new std::string();
|
|
for (int i = 0; i < argc; ++i)
|
*(args.argvMonoObj[i]) = il2cpp::utils::StringUtils::Utf16ToUtf8(argv[i]);
|
|
args.argvMono = new char*[argc];
|
for (int i = 0; i < argc; ++i)
|
args.argvMono[i] = const_cast<char*>(args.argvMonoObj[i]->c_str());
|
|
return args;
|
}
|
|
void il2cpp_mono_free_args(ArgvMono& args)
|
{
|
delete[] args.argvMono;
|
|
for (int i = 0; i < args.argc; ++i)
|
delete args.argvMonoObj[i];
|
|
delete[] args.argvMonoObj;
|
}
|
|
extern "C"
|
{
|
void il2cpp_set_temp_dir(char *temp_dir)
|
{
|
assert(0 && "This needs to be implemented for Android");
|
}
|
}
|
|
#endif //RUNTIME_MONO
|