123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- #include "il2cpp-config.h"
- #include "metadata/GenericMetadata.h"
- #include "metadata/GenericMethod.h"
- #include "metadata/GenericSharing.h"
- #include "metadata/Il2CppGenericMethodCompare.h"
- #include "metadata/Il2CppGenericMethodHash.h"
- #include "os/Mutex.h"
- #include "utils/Memory.h"
- #include "vm/Class.h"
- #include "vm/Exception.h"
- #include "vm/GenericClass.h"
- #include "vm/MetadataAlloc.h"
- #include "vm/MetadataCache.h"
- #include "vm/MetadataLock.h"
- #include "vm/Method.h"
- #include "vm/Runtime.h"
- #include "vm/Type.h"
- #include "utils/Il2CppHashMap.h"
- #include "il2cpp-class-internals.h"
- #include "il2cpp-runtime-metadata.h"
- #include "il2cpp-runtime-stats.h"
- #include <string>
- #include "hybridclr/metadata/MetadataUtil.h"
- #include "hybridclr/metadata/MetadataModule.h"
- #include "hybridclr/interpreter/InterpreterModule.h"
- using il2cpp::metadata::GenericMetadata;
- using il2cpp::metadata::GenericSharing;
- using il2cpp::os::FastAutoLock;
- using il2cpp::vm::Class;
- using il2cpp::vm::GenericClass;
- using il2cpp::vm::MetadataCalloc;
- using il2cpp::vm::MetadataCache;
- using il2cpp::vm::Method;
- using il2cpp::vm::Runtime;
- using il2cpp::vm::Type;
- struct SharedGenericMethodInfo : public MethodInfo
- {
- SharedGenericMethodInfo() { memset(this, 0, sizeof(*this)); }
- Il2CppMethodPointer virtualCallMethodPointer;
- };
- static size_t SizeOfGenericMethodInfo()
- {
- // If full generic sharing is enabled we track an additional method pointer, virtualCallMethodPointer
- // that allows virtual calls from non-FGS code to FGS code to be done directly (via unresolved virtual calls)
- if (!il2cpp::vm::Runtime::IsFullGenericSharingEnabled())
- return sizeof(MethodInfo);
- return sizeof(SharedGenericMethodInfo);
- }
- static MethodInfo* AllocGenericMethodInfo()
- {
- return (MethodInfo*)MetadataCalloc(1, SizeOfGenericMethodInfo());
- }
- static MethodInfo* AllocCopyGenericMethodInfo(const MethodInfo* sourceMethodInfo)
- {
- MethodInfo* newMethodInfo = AllocGenericMethodInfo();
- memcpy(newMethodInfo, sourceMethodInfo, SizeOfGenericMethodInfo());
- return newMethodInfo;
- }
- namespace il2cpp
- {
- namespace metadata
- {
- typedef Il2CppReaderWriterLockedHashMap<const Il2CppGenericMethod*, MethodInfo*, Il2CppGenericMethodHash, Il2CppGenericMethodCompare> Il2CppGenericMethodMap;
- static Il2CppGenericMethodMap s_GenericMethodMap;
- static Il2CppGenericMethodMap s_PendingGenericMethodMap;
- static bool HasFullGenericSharedParametersOrReturn(const MethodInfo* methodDefinition)
- {
- if (Type::HasVariableRuntimeSizeWhenFullyShared(methodDefinition->return_type))
- return true;
- for (int i = 0; i < methodDefinition->parameters_count; i++)
- {
- if (Type::HasVariableRuntimeSizeWhenFullyShared(methodDefinition->parameters[i]))
- return true;
- }
- return false;
- }
- static void AGenericMethodWhichIsTooDeeplyNestedWasInvoked()
- {
- vm::Exception::Raise(vm::Exception::GetMaximumNestedGenericsException());
- }
- static void AGenericMethodWhichIsTooDeeplyNestedWasInvokedInvoker(Il2CppMethodPointer ptr, const MethodInfo* method, void* obj, void** args, void* ret)
- {
- AGenericMethodWhichIsTooDeeplyNestedWasInvoked();
- }
- static SharedGenericMethodInfo ambiguousMethodInfo;
- bool GenericMethod::IsGenericAmbiguousMethodInfo(const MethodInfo* method)
- {
- return method == &ambiguousMethodInfo;
- }
- const MethodInfo* GenericMethod::GetGenericVirtualMethod(const MethodInfo* vtableSlotMethod, const MethodInfo* genericVirtualMethod)
- {
- IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(GetGenericVirtualMethod, "We should only do the following slow method lookup once and then cache on type itself.");
- const Il2CppGenericInst* classInst = NULL;
- if (vtableSlotMethod->is_inflated)
- {
- classInst = vtableSlotMethod->genericMethod->context.class_inst;
- vtableSlotMethod = vtableSlotMethod->genericMethod->methodDefinition;
- }
- return metadata::GenericMethod::GetMethod(vtableSlotMethod, classInst, genericVirtualMethod->genericMethod->context.method_inst);
- }
- const MethodInfo* GenericMethod::GetMethod(const MethodInfo* methodDefinition, const Il2CppGenericInst* classInst, const Il2CppGenericInst* methodInst)
- {
- Il2CppGenericMethod gmethod = { 0 };
- gmethod.methodDefinition = methodDefinition;
- gmethod.context.class_inst = classInst;
- gmethod.context.method_inst = methodInst;
- return GetMethod(&gmethod, true);
- }
- MethodInfo* GenericMethod::AllocateNewMethodInfo(const MethodInfo* methodDefinition, const Il2CppGenericInst* classInst, const Il2CppGenericInst* methodInst)
- {
- const MethodInfo* methodInfo = GetMethod(methodDefinition, classInst, methodInst);
- return AllocCopyGenericMethodInfo(methodInfo);
- }
- const MethodInfo* GenericMethod::GetMethod(const Il2CppGenericMethod* gmethod)
- {
- return GetMethod(gmethod, false);
- }
- Il2CppMethodPointer GenericMethod::GetVirtualCallMethodPointer(const MethodInfo* method)
- {
- IL2CPP_ASSERT(method->is_inflated);
- if (il2cpp::vm::Runtime::IsFullGenericSharingEnabled())
- return ((const SharedGenericMethodInfo*)method)->virtualCallMethodPointer;
- else
- return method->virtualMethodPointer;
- }
- const MethodInfo* GenericMethod::GetMethod(const Il2CppGenericMethod* gmethod, bool copyMethodPtr)
- {
- // This can be NULL only when we have hit the generic recursion depth limit.
- if (gmethod == NULL)
- {
- MethodInfo* newMethod = AllocGenericMethodInfo();
- if (il2cpp::vm::Runtime::IsFullGenericSharingEnabled())
- ((SharedGenericMethodInfo*)newMethod)->virtualCallMethodPointer = AGenericMethodWhichIsTooDeeplyNestedWasInvoked;
- newMethod->methodPointer = AGenericMethodWhichIsTooDeeplyNestedWasInvoked;
- newMethod->virtualMethodPointer = AGenericMethodWhichIsTooDeeplyNestedWasInvoked;
- newMethod->invoker_method = AGenericMethodWhichIsTooDeeplyNestedWasInvokedInvoker;
- return newMethod;
- }
- // First check for an already constructed generic method using the shared/reader lock
- MethodInfo* existingMethod;
- if (s_GenericMethodMap.TryGet(gmethod, &existingMethod))
- return existingMethod;
- if (Method::IsAmbiguousMethodInfo(gmethod->methodDefinition))
- {
- // is_inflated is used as an initialized check
- if (!ambiguousMethodInfo.is_inflated)
- {
- memcpy(&ambiguousMethodInfo, gmethod->methodDefinition, sizeof(MethodInfo));
- ambiguousMethodInfo.is_inflated = true;
- // This method must have methodPointer null so that the test in RaiseExecutionEngineExceptionIfGenericVirtualMethodIsNotFound fails
- ambiguousMethodInfo.methodPointer = NULL;
- ambiguousMethodInfo.virtualCallMethodPointer = gmethod->methodDefinition->virtualMethodPointer;
- }
- return &ambiguousMethodInfo;
- }
- return CreateMethodLocked(gmethod, copyMethodPtr);
- }
- const MethodInfo* GenericMethod::CreateMethodLocked(const Il2CppGenericMethod* gmethod, bool copyMethodPtr)
- {
- // We need to inflate a new generic method, take the metadata mutex
- // All code below this point can and does assume mutual exclusion
- FastAutoLock lock(&il2cpp::vm::g_MetadataLock);
- // Recheck the s_GenericMethodMap in case there was a race to add this generic method
- MethodInfo* existingMethod;
- if (s_GenericMethodMap.TryGet(gmethod, &existingMethod))
- return existingMethod;
- // GetMethodLocked may be called recursively, we keep tracking of pending inflations
- if (s_PendingGenericMethodMap.TryGet(gmethod, &existingMethod))
- return existingMethod;
- if (copyMethodPtr)
- gmethod = MetadataCache::GetGenericMethod(gmethod->methodDefinition, gmethod->context.class_inst, gmethod->context.method_inst);
- const MethodInfo* methodDefinition = gmethod->methodDefinition;
- Il2CppClass* declaringClass = methodDefinition->klass;
- if (gmethod->context.class_inst)
- {
- Il2CppGenericClass* genericClassDeclaringType = GenericMetadata::GetGenericClass(methodDefinition->klass, gmethod->context.class_inst);
- declaringClass = GenericClass::GetClass(genericClassDeclaringType);
- // we may fail if we cannot construct generic type
- if (!declaringClass)
- return NULL;
- }
- MethodInfo* newMethod = AllocGenericMethodInfo();
- // we set the pending generic method map here because the initialization may recurse and try to retrieve the same generic method
- // this is safe because we *always* take the lock when retrieving the MethodInfo from a generic method.
- // if we move lock to only if MethodInfo needs constructed then we need to revisit this since we could return a partially initialized MethodInfo
- s_PendingGenericMethodMap.Add(gmethod, newMethod);
- newMethod->klass = declaringClass;
- newMethod->flags = methodDefinition->flags;
- newMethod->iflags = methodDefinition->iflags;
- newMethod->slot = methodDefinition->slot;
- newMethod->name = methodDefinition->name;
- newMethod->is_generic = false;
- newMethod->is_inflated = true;
- newMethod->token = methodDefinition->token;
- newMethod->return_type = GenericMetadata::InflateIfNeeded(methodDefinition->return_type, &gmethod->context, true);
- newMethod->parameters_count = methodDefinition->parameters_count;
- newMethod->parameters = GenericMetadata::InflateParameters(methodDefinition->parameters, methodDefinition->parameters_count, &gmethod->context, true);
- newMethod->genericMethod = gmethod;
- if (!gmethod->context.method_inst)
- {
- if (methodDefinition->is_generic)
- newMethod->is_generic = true;
- if (!declaringClass->generic_class)
- {
- newMethod->genericContainerHandle = methodDefinition->genericContainerHandle;
- }
- newMethod->methodMetadataHandle = methodDefinition->methodMetadataHandle;
- }
- else if (!il2cpp::vm::Runtime::IsLazyRGCTXInflationEnabled() && !il2cpp::metadata::GenericMetadata::ContainsGenericParameters(newMethod))
- {
- // we only need RGCTX for generic instance methods
- newMethod->rgctx_data = InflateRGCTXLocked(gmethod, lock);
- }
- il2cpp::vm::Il2CppGenericMethodPointers methodPointers = MetadataCache::GetGenericMethodPointers(methodDefinition, &gmethod->context);
- newMethod->virtualMethodPointer = methodPointers.virtualMethodPointer;
- newMethod->methodPointer = methodPointers.methodPointer;
- if (methodPointers.methodPointer)
- {
- newMethod->invoker_method = methodPointers.invoker_method;
- }
- else
- {
- newMethod->invoker_method = Runtime::GetMissingMethodInvoker();
- if (Method::IsInstance(newMethod))
- newMethod->virtualMethodPointer = MetadataCache::GetUnresolvedVirtualCallStub(newMethod);
- }
- bool isInterpMethod = hybridclr::metadata::IsInterpreterMethod(newMethod);
- if (!isInterpMethod)
- {
- newMethod->has_full_generic_sharing_signature = methodPointers.isFullGenericShared && HasFullGenericSharedParametersOrReturn(gmethod->methodDefinition);
- // Full generic sharing methods should be called via invoker
- // And invalid static methods can't use the unresolved virtual call stubs
- newMethod->indirect_call_via_invokers = newMethod->has_full_generic_sharing_signature || (!Method::IsInstance(newMethod) && newMethod->methodPointer == NULL);
- }
- ++il2cpp_runtime_stats.inflated_method_count;
- if (il2cpp::vm::Runtime::IsFullGenericSharingEnabled())
- {
- SharedGenericMethodInfo* sharedMethodInfo = reinterpret_cast<SharedGenericMethodInfo*>(newMethod);
- if (il2cpp::vm::Method::HasFullGenericSharingSignature(newMethod) && il2cpp::vm::Method::IsInstance(newMethod))
- sharedMethodInfo->virtualCallMethodPointer = MetadataCache::GetUnresolvedVirtualCallStub(newMethod);
- else
- sharedMethodInfo->virtualCallMethodPointer = newMethod->virtualMethodPointer;
- }
- bool isAotImplByInterp = hybridclr::metadata::MetadataModule::IsImplementedByInterpreter(newMethod);
- bool isAdjustorThunkMethod = IS_CLASS_VALUE_TYPE(newMethod->klass) && hybridclr::metadata::IsInstanceMethod(newMethod);
- if (methodPointers.methodPointer == nullptr)
- {
- if (isInterpMethod || isAotImplByInterp)
- {
- newMethod->invoker_method = hybridclr::interpreter::InterpreterModule::GetMethodInvoker(newMethod);
- newMethod->methodPointer = newMethod->methodPointerCallByInterp = hybridclr::interpreter::InterpreterModule::GetMethodPointer(newMethod);
- newMethod->virtualMethodPointer = newMethod->virtualMethodPointerCallByInterp = isAdjustorThunkMethod ?
- hybridclr::interpreter::InterpreterModule::GetAdjustThunkMethodPointer(newMethod) :
- (newMethod->methodPointerCallByInterp != hybridclr::interpreter::InterpreterModule::NotSupportNative2Managed ?
- newMethod->methodPointerCallByInterp : hybridclr::interpreter::InterpreterModule::NotSupportAdjustorThunk);
- newMethod->isInterpterImpl = true;
- newMethod->initInterpCallMethodPointer = true;
- }
- }
- else
- {
- if (newMethod->indirect_call_via_invokers && isAotImplByInterp)
- {
- newMethod->methodPointerCallByInterp = hybridclr::interpreter::InterpreterModule::GetMethodPointer(newMethod);
- newMethod->virtualMethodPointerCallByInterp = isAdjustorThunkMethod ?
- hybridclr::interpreter::InterpreterModule::GetAdjustThunkMethodPointer(newMethod) :
- (newMethod->methodPointerCallByInterp != hybridclr::interpreter::InterpreterModule::NotSupportNative2Managed ?
- newMethod->methodPointerCallByInterp : hybridclr::interpreter::InterpreterModule::NotSupportAdjustorThunk);
- newMethod->invoker_method = hybridclr::interpreter::InterpreterModule::GetMethodInvoker(newMethod);
- newMethod->methodPointer = newMethod->methodPointerCallByInterp;
- newMethod->virtualMethodPointer = newMethod->virtualMethodPointerCallByInterp;
- newMethod->isInterpterImpl = true;
- newMethod->initInterpCallMethodPointer = true;
- }
- }
- if (!newMethod->isInterpterImpl && !newMethod->indirect_call_via_invokers)
- {
- newMethod->methodPointerCallByInterp = newMethod->methodPointer;
- newMethod->virtualMethodPointerCallByInterp = GetVirtualCallMethodPointer(newMethod);
- }
- else
- {
- //newMethod->initInterpCallMethodPointer = true;
- newMethod->indirect_call_via_invokers = false;
- newMethod->has_full_generic_sharing_signature = false;
- }
- // If we are a default interface method on a generic instance interface we need to ensure that the interfaces rgctx is inflated
- if (Method::IsDefaultInterfaceMethodOnGenericInstance(newMethod))
- vm::Class::InitLocked(declaringClass, lock);
- // The generic method is fully created,
- // Update the generic method map, this needs to take an exclusive lock
- // **** This must happen with the metadata lock held and be released before the metalock is released ****
- // **** This prevents deadlocks and ensures that there is no race condition
- // **** creating a new method adding it to s_GenericMethodMap and removing it from s_PendingGenericMethodMap
- s_GenericMethodMap.Add(gmethod, newMethod);
- // Remove the method from the pending table
- s_PendingGenericMethodMap.Remove(gmethod);
- return newMethod;
- }
- const Il2CppRGCTXData* GenericMethod::InflateRGCTX(const MethodInfo* method)
- {
- IL2CPP_ASSERT(method->is_inflated);
- IL2CPP_ASSERT(method->genericMethod);
- IL2CPP_ASSERT(method->genericMethod->context.method_inst);
- FastAutoLock lock(&il2cpp::vm::g_MetadataLock);
- if (method->rgctx_data != NULL)
- return method->rgctx_data;
- const Il2CppRGCTXData* rgctx = InflateRGCTXLocked(method->genericMethod, lock);
- const_cast<MethodInfo*>(method)->rgctx_data = rgctx;
- return rgctx;
- }
- const Il2CppRGCTXData* GenericMethod::InflateRGCTXLocked(const Il2CppGenericMethod* gmethod, const il2cpp::os::FastAutoLock &lock)
- {
- return GenericMetadata::InflateRGCTXLocked(gmethod->methodDefinition->klass->image, gmethod->methodDefinition->token, &gmethod->context, lock);
- }
- const Il2CppGenericContext* GenericMethod::GetContext(const Il2CppGenericMethod* gmethod)
- {
- return &gmethod->context;
- }
- static std::string FormatGenericArguments(const Il2CppGenericInst* inst)
- {
- std::string output;
- if (inst)
- {
- output.append("<");
- for (size_t i = 0; i < inst->type_argc; ++i)
- {
- if (i != 0)
- output.append(", ");
- output.append(Type::GetName(inst->type_argv[i], IL2CPP_TYPE_NAME_FORMAT_FULL_NAME));
- }
- output.append(">");
- }
- return output;
- }
- std::string GenericMethod::GetFullName(const Il2CppGenericMethod* gmethod)
- {
- const MethodInfo* method = gmethod->methodDefinition;
- std::string output;
- output.append(Type::GetName(&gmethod->methodDefinition->klass->byval_arg, IL2CPP_TYPE_NAME_FORMAT_FULL_NAME));
- output.append(FormatGenericArguments(gmethod->context.class_inst));
- output.append("::");
- output.append(Method::GetName(method));
- output.append(FormatGenericArguments(gmethod->context.method_inst));
- return output;
- }
- void GenericMethod::ClearStatics()
- {
- s_GenericMethodMap.Clear();
- }
- } /* namespace vm */
- } /* namespace il2cpp */
|