GenericMethod.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. #include "il2cpp-config.h"
  2. #include "metadata/GenericMetadata.h"
  3. #include "metadata/GenericMethod.h"
  4. #include "metadata/GenericSharing.h"
  5. #include "metadata/Il2CppGenericMethodCompare.h"
  6. #include "metadata/Il2CppGenericMethodHash.h"
  7. #include "os/Mutex.h"
  8. #include "utils/Memory.h"
  9. #include "vm/Class.h"
  10. #include "vm/Exception.h"
  11. #include "vm/GenericClass.h"
  12. #include "vm/MetadataAlloc.h"
  13. #include "vm/MetadataCache.h"
  14. #include "vm/MetadataLock.h"
  15. #include "vm/Method.h"
  16. #include "vm/Runtime.h"
  17. #include "vm/Type.h"
  18. #include "utils/Il2CppHashMap.h"
  19. #include "il2cpp-class-internals.h"
  20. #include "il2cpp-runtime-metadata.h"
  21. #include "il2cpp-runtime-stats.h"
  22. #include <string>
  23. #include "hybridclr/metadata/MetadataUtil.h"
  24. #include "hybridclr/metadata/MetadataModule.h"
  25. #include "hybridclr/interpreter/InterpreterModule.h"
  26. using il2cpp::metadata::GenericMetadata;
  27. using il2cpp::metadata::GenericSharing;
  28. using il2cpp::os::FastAutoLock;
  29. using il2cpp::vm::Class;
  30. using il2cpp::vm::GenericClass;
  31. using il2cpp::vm::MetadataCalloc;
  32. using il2cpp::vm::MetadataCache;
  33. using il2cpp::vm::Method;
  34. using il2cpp::vm::Runtime;
  35. using il2cpp::vm::Type;
  36. struct SharedGenericMethodInfo : public MethodInfo
  37. {
  38. SharedGenericMethodInfo() { memset(this, 0, sizeof(*this)); }
  39. Il2CppMethodPointer virtualCallMethodPointer;
  40. };
  41. static size_t SizeOfGenericMethodInfo()
  42. {
  43. // If full generic sharing is enabled we track an additional method pointer, virtualCallMethodPointer
  44. // that allows virtual calls from non-FGS code to FGS code to be done directly (via unresolved virtual calls)
  45. if (!il2cpp::vm::Runtime::IsFullGenericSharingEnabled())
  46. return sizeof(MethodInfo);
  47. return sizeof(SharedGenericMethodInfo);
  48. }
  49. static MethodInfo* AllocGenericMethodInfo()
  50. {
  51. return (MethodInfo*)MetadataCalloc(1, SizeOfGenericMethodInfo());
  52. }
  53. static MethodInfo* AllocCopyGenericMethodInfo(const MethodInfo* sourceMethodInfo)
  54. {
  55. MethodInfo* newMethodInfo = AllocGenericMethodInfo();
  56. memcpy(newMethodInfo, sourceMethodInfo, SizeOfGenericMethodInfo());
  57. return newMethodInfo;
  58. }
  59. namespace il2cpp
  60. {
  61. namespace metadata
  62. {
  63. typedef Il2CppReaderWriterLockedHashMap<const Il2CppGenericMethod*, MethodInfo*, Il2CppGenericMethodHash, Il2CppGenericMethodCompare> Il2CppGenericMethodMap;
  64. static Il2CppGenericMethodMap s_GenericMethodMap;
  65. static Il2CppGenericMethodMap s_PendingGenericMethodMap;
  66. static bool HasFullGenericSharedParametersOrReturn(const MethodInfo* methodDefinition)
  67. {
  68. if (Type::HasVariableRuntimeSizeWhenFullyShared(methodDefinition->return_type))
  69. return true;
  70. for (int i = 0; i < methodDefinition->parameters_count; i++)
  71. {
  72. if (Type::HasVariableRuntimeSizeWhenFullyShared(methodDefinition->parameters[i]))
  73. return true;
  74. }
  75. return false;
  76. }
  77. static void AGenericMethodWhichIsTooDeeplyNestedWasInvoked()
  78. {
  79. vm::Exception::Raise(vm::Exception::GetMaximumNestedGenericsException());
  80. }
  81. static void AGenericMethodWhichIsTooDeeplyNestedWasInvokedInvoker(Il2CppMethodPointer ptr, const MethodInfo* method, void* obj, void** args, void* ret)
  82. {
  83. AGenericMethodWhichIsTooDeeplyNestedWasInvoked();
  84. }
  85. static SharedGenericMethodInfo ambiguousMethodInfo;
  86. bool GenericMethod::IsGenericAmbiguousMethodInfo(const MethodInfo* method)
  87. {
  88. return method == &ambiguousMethodInfo;
  89. }
  90. const MethodInfo* GenericMethod::GetGenericVirtualMethod(const MethodInfo* vtableSlotMethod, const MethodInfo* genericVirtualMethod)
  91. {
  92. IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(GetGenericVirtualMethod, "We should only do the following slow method lookup once and then cache on type itself.");
  93. const Il2CppGenericInst* classInst = NULL;
  94. if (vtableSlotMethod->is_inflated)
  95. {
  96. classInst = vtableSlotMethod->genericMethod->context.class_inst;
  97. vtableSlotMethod = vtableSlotMethod->genericMethod->methodDefinition;
  98. }
  99. return metadata::GenericMethod::GetMethod(vtableSlotMethod, classInst, genericVirtualMethod->genericMethod->context.method_inst);
  100. }
  101. const MethodInfo* GenericMethod::GetMethod(const MethodInfo* methodDefinition, const Il2CppGenericInst* classInst, const Il2CppGenericInst* methodInst)
  102. {
  103. Il2CppGenericMethod gmethod = { 0 };
  104. gmethod.methodDefinition = methodDefinition;
  105. gmethod.context.class_inst = classInst;
  106. gmethod.context.method_inst = methodInst;
  107. return GetMethod(&gmethod, true);
  108. }
  109. MethodInfo* GenericMethod::AllocateNewMethodInfo(const MethodInfo* methodDefinition, const Il2CppGenericInst* classInst, const Il2CppGenericInst* methodInst)
  110. {
  111. const MethodInfo* methodInfo = GetMethod(methodDefinition, classInst, methodInst);
  112. return AllocCopyGenericMethodInfo(methodInfo);
  113. }
  114. const MethodInfo* GenericMethod::GetMethod(const Il2CppGenericMethod* gmethod)
  115. {
  116. return GetMethod(gmethod, false);
  117. }
  118. Il2CppMethodPointer GenericMethod::GetVirtualCallMethodPointer(const MethodInfo* method)
  119. {
  120. IL2CPP_ASSERT(method->is_inflated);
  121. if (il2cpp::vm::Runtime::IsFullGenericSharingEnabled())
  122. return ((const SharedGenericMethodInfo*)method)->virtualCallMethodPointer;
  123. else
  124. return method->virtualMethodPointer;
  125. }
  126. const MethodInfo* GenericMethod::GetMethod(const Il2CppGenericMethod* gmethod, bool copyMethodPtr)
  127. {
  128. // This can be NULL only when we have hit the generic recursion depth limit.
  129. if (gmethod == NULL)
  130. {
  131. MethodInfo* newMethod = AllocGenericMethodInfo();
  132. if (il2cpp::vm::Runtime::IsFullGenericSharingEnabled())
  133. ((SharedGenericMethodInfo*)newMethod)->virtualCallMethodPointer = AGenericMethodWhichIsTooDeeplyNestedWasInvoked;
  134. newMethod->methodPointer = AGenericMethodWhichIsTooDeeplyNestedWasInvoked;
  135. newMethod->virtualMethodPointer = AGenericMethodWhichIsTooDeeplyNestedWasInvoked;
  136. newMethod->invoker_method = AGenericMethodWhichIsTooDeeplyNestedWasInvokedInvoker;
  137. return newMethod;
  138. }
  139. // First check for an already constructed generic method using the shared/reader lock
  140. MethodInfo* existingMethod;
  141. if (s_GenericMethodMap.TryGet(gmethod, &existingMethod))
  142. return existingMethod;
  143. if (Method::IsAmbiguousMethodInfo(gmethod->methodDefinition))
  144. {
  145. // is_inflated is used as an initialized check
  146. if (!ambiguousMethodInfo.is_inflated)
  147. {
  148. memcpy(&ambiguousMethodInfo, gmethod->methodDefinition, sizeof(MethodInfo));
  149. ambiguousMethodInfo.is_inflated = true;
  150. // This method must have methodPointer null so that the test in RaiseExecutionEngineExceptionIfGenericVirtualMethodIsNotFound fails
  151. ambiguousMethodInfo.methodPointer = NULL;
  152. ambiguousMethodInfo.virtualCallMethodPointer = gmethod->methodDefinition->virtualMethodPointer;
  153. }
  154. return &ambiguousMethodInfo;
  155. }
  156. return CreateMethodLocked(gmethod, copyMethodPtr);
  157. }
  158. const MethodInfo* GenericMethod::CreateMethodLocked(const Il2CppGenericMethod* gmethod, bool copyMethodPtr)
  159. {
  160. // We need to inflate a new generic method, take the metadata mutex
  161. // All code below this point can and does assume mutual exclusion
  162. FastAutoLock lock(&il2cpp::vm::g_MetadataLock);
  163. // Recheck the s_GenericMethodMap in case there was a race to add this generic method
  164. MethodInfo* existingMethod;
  165. if (s_GenericMethodMap.TryGet(gmethod, &existingMethod))
  166. return existingMethod;
  167. // GetMethodLocked may be called recursively, we keep tracking of pending inflations
  168. if (s_PendingGenericMethodMap.TryGet(gmethod, &existingMethod))
  169. return existingMethod;
  170. if (copyMethodPtr)
  171. gmethod = MetadataCache::GetGenericMethod(gmethod->methodDefinition, gmethod->context.class_inst, gmethod->context.method_inst);
  172. const MethodInfo* methodDefinition = gmethod->methodDefinition;
  173. Il2CppClass* declaringClass = methodDefinition->klass;
  174. if (gmethod->context.class_inst)
  175. {
  176. Il2CppGenericClass* genericClassDeclaringType = GenericMetadata::GetGenericClass(methodDefinition->klass, gmethod->context.class_inst);
  177. declaringClass = GenericClass::GetClass(genericClassDeclaringType);
  178. // we may fail if we cannot construct generic type
  179. if (!declaringClass)
  180. return NULL;
  181. }
  182. MethodInfo* newMethod = AllocGenericMethodInfo();
  183. // we set the pending generic method map here because the initialization may recurse and try to retrieve the same generic method
  184. // this is safe because we *always* take the lock when retrieving the MethodInfo from a generic method.
  185. // if we move lock to only if MethodInfo needs constructed then we need to revisit this since we could return a partially initialized MethodInfo
  186. s_PendingGenericMethodMap.Add(gmethod, newMethod);
  187. newMethod->klass = declaringClass;
  188. newMethod->flags = methodDefinition->flags;
  189. newMethod->iflags = methodDefinition->iflags;
  190. newMethod->slot = methodDefinition->slot;
  191. newMethod->name = methodDefinition->name;
  192. newMethod->is_generic = false;
  193. newMethod->is_inflated = true;
  194. newMethod->token = methodDefinition->token;
  195. newMethod->return_type = GenericMetadata::InflateIfNeeded(methodDefinition->return_type, &gmethod->context, true);
  196. newMethod->parameters_count = methodDefinition->parameters_count;
  197. newMethod->parameters = GenericMetadata::InflateParameters(methodDefinition->parameters, methodDefinition->parameters_count, &gmethod->context, true);
  198. newMethod->genericMethod = gmethod;
  199. if (!gmethod->context.method_inst)
  200. {
  201. if (methodDefinition->is_generic)
  202. newMethod->is_generic = true;
  203. if (!declaringClass->generic_class)
  204. {
  205. newMethod->genericContainerHandle = methodDefinition->genericContainerHandle;
  206. }
  207. newMethod->methodMetadataHandle = methodDefinition->methodMetadataHandle;
  208. }
  209. else if (!il2cpp::vm::Runtime::IsLazyRGCTXInflationEnabled() && !il2cpp::metadata::GenericMetadata::ContainsGenericParameters(newMethod))
  210. {
  211. // we only need RGCTX for generic instance methods
  212. newMethod->rgctx_data = InflateRGCTXLocked(gmethod, lock);
  213. }
  214. il2cpp::vm::Il2CppGenericMethodPointers methodPointers = MetadataCache::GetGenericMethodPointers(methodDefinition, &gmethod->context);
  215. newMethod->virtualMethodPointer = methodPointers.virtualMethodPointer;
  216. newMethod->methodPointer = methodPointers.methodPointer;
  217. if (methodPointers.methodPointer)
  218. {
  219. newMethod->invoker_method = methodPointers.invoker_method;
  220. }
  221. else
  222. {
  223. newMethod->invoker_method = Runtime::GetMissingMethodInvoker();
  224. if (Method::IsInstance(newMethod))
  225. newMethod->virtualMethodPointer = MetadataCache::GetUnresolvedVirtualCallStub(newMethod);
  226. }
  227. bool isInterpMethod = hybridclr::metadata::IsInterpreterMethod(newMethod);
  228. if (!isInterpMethod)
  229. {
  230. newMethod->has_full_generic_sharing_signature = methodPointers.isFullGenericShared && HasFullGenericSharedParametersOrReturn(gmethod->methodDefinition);
  231. // Full generic sharing methods should be called via invoker
  232. // And invalid static methods can't use the unresolved virtual call stubs
  233. newMethod->indirect_call_via_invokers = newMethod->has_full_generic_sharing_signature || (!Method::IsInstance(newMethod) && newMethod->methodPointer == NULL);
  234. }
  235. ++il2cpp_runtime_stats.inflated_method_count;
  236. if (il2cpp::vm::Runtime::IsFullGenericSharingEnabled())
  237. {
  238. SharedGenericMethodInfo* sharedMethodInfo = reinterpret_cast<SharedGenericMethodInfo*>(newMethod);
  239. if (il2cpp::vm::Method::HasFullGenericSharingSignature(newMethod) && il2cpp::vm::Method::IsInstance(newMethod))
  240. sharedMethodInfo->virtualCallMethodPointer = MetadataCache::GetUnresolvedVirtualCallStub(newMethod);
  241. else
  242. sharedMethodInfo->virtualCallMethodPointer = newMethod->virtualMethodPointer;
  243. }
  244. bool isAotImplByInterp = hybridclr::metadata::MetadataModule::IsImplementedByInterpreter(newMethod);
  245. bool isAdjustorThunkMethod = IS_CLASS_VALUE_TYPE(newMethod->klass) && hybridclr::metadata::IsInstanceMethod(newMethod);
  246. if (methodPointers.methodPointer == nullptr)
  247. {
  248. if (isInterpMethod || isAotImplByInterp)
  249. {
  250. newMethod->invoker_method = hybridclr::interpreter::InterpreterModule::GetMethodInvoker(newMethod);
  251. newMethod->methodPointer = newMethod->methodPointerCallByInterp = hybridclr::interpreter::InterpreterModule::GetMethodPointer(newMethod);
  252. newMethod->virtualMethodPointer = newMethod->virtualMethodPointerCallByInterp = isAdjustorThunkMethod ?
  253. hybridclr::interpreter::InterpreterModule::GetAdjustThunkMethodPointer(newMethod) :
  254. (newMethod->methodPointerCallByInterp != hybridclr::interpreter::InterpreterModule::NotSupportNative2Managed ?
  255. newMethod->methodPointerCallByInterp : hybridclr::interpreter::InterpreterModule::NotSupportAdjustorThunk);
  256. newMethod->isInterpterImpl = true;
  257. newMethod->initInterpCallMethodPointer = true;
  258. }
  259. }
  260. else
  261. {
  262. if (newMethod->indirect_call_via_invokers && isAotImplByInterp)
  263. {
  264. newMethod->methodPointerCallByInterp = hybridclr::interpreter::InterpreterModule::GetMethodPointer(newMethod);
  265. newMethod->virtualMethodPointerCallByInterp = isAdjustorThunkMethod ?
  266. hybridclr::interpreter::InterpreterModule::GetAdjustThunkMethodPointer(newMethod) :
  267. (newMethod->methodPointerCallByInterp != hybridclr::interpreter::InterpreterModule::NotSupportNative2Managed ?
  268. newMethod->methodPointerCallByInterp : hybridclr::interpreter::InterpreterModule::NotSupportAdjustorThunk);
  269. newMethod->invoker_method = hybridclr::interpreter::InterpreterModule::GetMethodInvoker(newMethod);
  270. newMethod->methodPointer = newMethod->methodPointerCallByInterp;
  271. newMethod->virtualMethodPointer = newMethod->virtualMethodPointerCallByInterp;
  272. newMethod->isInterpterImpl = true;
  273. newMethod->initInterpCallMethodPointer = true;
  274. }
  275. }
  276. if (!newMethod->isInterpterImpl && !newMethod->indirect_call_via_invokers)
  277. {
  278. newMethod->methodPointerCallByInterp = newMethod->methodPointer;
  279. newMethod->virtualMethodPointerCallByInterp = GetVirtualCallMethodPointer(newMethod);
  280. }
  281. else
  282. {
  283. //newMethod->initInterpCallMethodPointer = true;
  284. newMethod->indirect_call_via_invokers = false;
  285. newMethod->has_full_generic_sharing_signature = false;
  286. }
  287. // If we are a default interface method on a generic instance interface we need to ensure that the interfaces rgctx is inflated
  288. if (Method::IsDefaultInterfaceMethodOnGenericInstance(newMethod))
  289. vm::Class::InitLocked(declaringClass, lock);
  290. // The generic method is fully created,
  291. // Update the generic method map, this needs to take an exclusive lock
  292. // **** This must happen with the metadata lock held and be released before the metalock is released ****
  293. // **** This prevents deadlocks and ensures that there is no race condition
  294. // **** creating a new method adding it to s_GenericMethodMap and removing it from s_PendingGenericMethodMap
  295. s_GenericMethodMap.Add(gmethod, newMethod);
  296. // Remove the method from the pending table
  297. s_PendingGenericMethodMap.Remove(gmethod);
  298. return newMethod;
  299. }
  300. const Il2CppRGCTXData* GenericMethod::InflateRGCTX(const MethodInfo* method)
  301. {
  302. IL2CPP_ASSERT(method->is_inflated);
  303. IL2CPP_ASSERT(method->genericMethod);
  304. IL2CPP_ASSERT(method->genericMethod->context.method_inst);
  305. FastAutoLock lock(&il2cpp::vm::g_MetadataLock);
  306. if (method->rgctx_data != NULL)
  307. return method->rgctx_data;
  308. const Il2CppRGCTXData* rgctx = InflateRGCTXLocked(method->genericMethod, lock);
  309. const_cast<MethodInfo*>(method)->rgctx_data = rgctx;
  310. return rgctx;
  311. }
  312. const Il2CppRGCTXData* GenericMethod::InflateRGCTXLocked(const Il2CppGenericMethod* gmethod, const il2cpp::os::FastAutoLock &lock)
  313. {
  314. return GenericMetadata::InflateRGCTXLocked(gmethod->methodDefinition->klass->image, gmethod->methodDefinition->token, &gmethod->context, lock);
  315. }
  316. const Il2CppGenericContext* GenericMethod::GetContext(const Il2CppGenericMethod* gmethod)
  317. {
  318. return &gmethod->context;
  319. }
  320. static std::string FormatGenericArguments(const Il2CppGenericInst* inst)
  321. {
  322. std::string output;
  323. if (inst)
  324. {
  325. output.append("<");
  326. for (size_t i = 0; i < inst->type_argc; ++i)
  327. {
  328. if (i != 0)
  329. output.append(", ");
  330. output.append(Type::GetName(inst->type_argv[i], IL2CPP_TYPE_NAME_FORMAT_FULL_NAME));
  331. }
  332. output.append(">");
  333. }
  334. return output;
  335. }
  336. std::string GenericMethod::GetFullName(const Il2CppGenericMethod* gmethod)
  337. {
  338. const MethodInfo* method = gmethod->methodDefinition;
  339. std::string output;
  340. output.append(Type::GetName(&gmethod->methodDefinition->klass->byval_arg, IL2CPP_TYPE_NAME_FORMAT_FULL_NAME));
  341. output.append(FormatGenericArguments(gmethod->context.class_inst));
  342. output.append("::");
  343. output.append(Method::GetName(method));
  344. output.append(FormatGenericArguments(gmethod->context.method_inst));
  345. return output;
  346. }
  347. void GenericMethod::ClearStatics()
  348. {
  349. s_GenericMethodMap.Clear();
  350. }
  351. } /* namespace vm */
  352. } /* namespace il2cpp */