GenericMetadata.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. #include "il2cpp-config.h"
  2. #include "il2cpp-runtime-stats.h"
  3. #include "os/Mutex.h"
  4. #include "vm/Class.h"
  5. #include "vm/GenericClass.h"
  6. #include "vm/Image.h"
  7. #include "vm/Runtime.h"
  8. #include "vm/Type.h"
  9. #include "vm/MetadataLock.h"
  10. #include "metadata/GenericMetadata.h"
  11. #include "metadata/GenericMethod.h"
  12. #include "metadata/Il2CppGenericClassHash.h"
  13. #include "metadata/Il2CppGenericClassCompare.h"
  14. #include "metadata/Il2CppGenericInstCompare.h"
  15. #include "metadata/Il2CppGenericInstHash.h"
  16. #include "metadata/Il2CppTypeCompare.h"
  17. #include "metadata/Il2CppTypeHash.h"
  18. #include "utils/Memory.h"
  19. #include "utils/Il2CppHashMap.h"
  20. #include "utils/Il2CppHashSet.h"
  21. #include "utils/StringUtils.h"
  22. #include "vm/MetadataAlloc.h"
  23. #include "vm/MetadataCache.h"
  24. #include "il2cpp-class-internals.h"
  25. #include "il2cpp-tabledefs.h"
  26. #include <vector>
  27. #include "Baselib.h"
  28. #include "Cpp/ReentrantLock.h"
  29. #include "hybridclr/metadata/MetadataUtil.h"
  30. #include "hybridclr/metadata/MetadataPool.h"
  31. using namespace il2cpp::vm;
  32. using il2cpp::metadata::GenericMethod;
  33. using il2cpp::os::FastAutoLock;
  34. using il2cpp::utils::StringUtils;
  35. using std::vector;
  36. using std::pair;
  37. namespace il2cpp
  38. {
  39. namespace metadata
  40. {
  41. struct RGCTXContextCollectData {
  42. const Il2CppGenericContext* context;
  43. RGCTXCollection collection;
  44. };
  45. static Il2CppHashMap<const Il2CppRGCTXData* , RGCTXContextCollectData> s_RGCTXDataToClassMap;
  46. const Il2CppType** GenericMetadata::InflateParameters(const Il2CppType** parameters, uint8_t parameterCount, const Il2CppGenericContext* context, bool inflateMethodVars)
  47. {
  48. const Il2CppType** inflatedParameters = (const Il2CppType**)MetadataCalloc(parameterCount, sizeof(Il2CppType*), IL2CPP_MSTAT_TYPE);
  49. for (uint8_t j = 0; j < parameterCount; j++)
  50. {
  51. inflatedParameters[j] = InflateIfNeeded(parameters[j], context, inflateMethodVars);
  52. }
  53. return inflatedParameters;
  54. }
  55. static const Il2CppType* InflateGenericParameterIfNeeded(const Il2CppType* type, const Il2CppGenericInst* inst)
  56. {
  57. IL2CPP_ASSERT(inst);
  58. Il2CppGenericParameterInfo gp = Type::GetGenericParameterInfo(type);
  59. IL2CPP_ASSERT(gp.num < inst->type_argc);
  60. const Il2CppType* genericArgument = inst->type_argv[gp.num];
  61. if (genericArgument->attrs == type->attrs && genericArgument->byref == type->byref)
  62. return genericArgument;
  63. //Il2CppType* inflatedType = (Il2CppType*)MetadataMalloc(sizeof(Il2CppType));
  64. //memcpy(inflatedType, genericArgument, sizeof(Il2CppType));
  65. //inflatedType->byref = type->byref;
  66. //inflatedType->attrs = type->attrs;
  67. Il2CppType tempInflatedType = *genericArgument;
  68. //Il2CppType* inflatedType = &tempInflatedType; //(Il2CppType*)MetadataMalloc(sizeof(Il2CppType));
  69. tempInflatedType.byref = type->byref;
  70. tempInflatedType.attrs = type->attrs;
  71. const Il2CppType* inflatedType = (Il2CppType*)hybridclr::metadata::MetadataPool::GetPooledIl2CppType(tempInflatedType);
  72. ++il2cpp_runtime_stats.inflated_type_count;
  73. return inflatedType;
  74. }
  75. const Il2CppType* GenericMetadata::InflateIfNeeded(const Il2CppType* type, const Il2CppGenericContext* context, bool inflateMethodVars)
  76. {
  77. switch (type->type)
  78. {
  79. case IL2CPP_TYPE_VAR:
  80. return InflateGenericParameterIfNeeded(type, context->class_inst);
  81. case IL2CPP_TYPE_MVAR:
  82. {
  83. if (context->method_inst)
  84. return InflateGenericParameterIfNeeded(type, context->method_inst);
  85. return type;
  86. }
  87. case IL2CPP_TYPE_ARRAY:
  88. {
  89. const Il2CppType* inflatedElementType = InflateIfNeeded(type->data.array->etype, context, inflateMethodVars);
  90. if (!Il2CppTypeEqualityComparer::AreEqual(inflatedElementType, type->data.array->etype))
  91. {
  92. Il2CppArrayType* arrType = type->data.array;
  93. if (arrType->numlobounds == 0 && arrType->numsizes == 0)
  94. {
  95. Il2CppType tempType = *type;
  96. tempType.data.array = (Il2CppArrayType*)hybridclr::metadata::MetadataPool::GetPooledIl2CppArrayType(inflatedElementType, arrType->rank);
  97. return hybridclr::metadata::MetadataPool::GetPooledIl2CppType(tempType);
  98. }
  99. else
  100. {
  101. Il2CppType* inflatedType = (Il2CppType*)MetadataMalloc(sizeof(Il2CppType), IL2CPP_MSTAT_TYPE);
  102. memcpy(inflatedType, type, sizeof(Il2CppType));
  103. Il2CppArrayType* arrayType = (Il2CppArrayType*)MetadataMalloc(sizeof(Il2CppArrayType), IL2CPP_MSTAT_TYPE);
  104. memcpy(arrayType, type->data.array, sizeof(Il2CppArrayType));
  105. arrayType->etype = inflatedElementType;
  106. inflatedType->data.array = arrayType;
  107. ++il2cpp_runtime_stats.inflated_type_count;
  108. return inflatedType;
  109. }
  110. }
  111. return type;
  112. }
  113. case IL2CPP_TYPE_PTR:
  114. case IL2CPP_TYPE_SZARRAY:
  115. {
  116. const Il2CppType* inflatedElementType = InflateIfNeeded(type->data.type, context, inflateMethodVars);
  117. if (!Il2CppTypeEqualityComparer::AreEqual(inflatedElementType, type->data.type))
  118. {
  119. Il2CppType tempType = *type;
  120. tempType.data.type = inflatedElementType;
  121. const Il2CppType* arrayType = hybridclr::metadata::MetadataPool::GetPooledIl2CppType(tempType);
  122. ++il2cpp_runtime_stats.inflated_type_count;
  123. return arrayType;
  124. }
  125. return type;
  126. }
  127. case IL2CPP_TYPE_GENERICINST:
  128. {
  129. const Il2CppGenericInst* inst = type->data.generic_class->context.class_inst;
  130. if (inst == NULL)
  131. return NULL; // This is a generic type that was too deeply nested to generate
  132. const Il2CppGenericInst* inflatedInst = GetInflatedGenericIntance(inst, context, inflateMethodVars);
  133. Il2CppGenericClass* genericClass = GenericMetadata::GetGenericClass(type->data.generic_class->type, inflatedInst);
  134. if (genericClass != type->data.generic_class)
  135. {
  136. Il2CppType* genericType = (Il2CppType*)MetadataMalloc(sizeof(Il2CppType), IL2CPP_MSTAT_TYPE);
  137. memcpy(genericType, type, sizeof(Il2CppType));
  138. genericType->data.generic_class = genericClass;
  139. ++il2cpp_runtime_stats.inflated_type_count;
  140. return genericType;
  141. }
  142. return type;
  143. }
  144. default:
  145. return type;
  146. }
  147. }
  148. static baselib::ReentrantLock s_GenericClassMutex;
  149. typedef Il2CppHashSet<Il2CppGenericClass*, Il2CppGenericClassHash, Il2CppGenericClassCompare> Il2CppGenericClassSet;
  150. static Il2CppGenericClassSet s_GenericClassSet;
  151. Il2CppGenericClass* GenericMetadata::GetGenericClass(const Il2CppClass* genericTypeDefinition, const Il2CppGenericInst* inst)
  152. {
  153. return GetGenericClass(&genericTypeDefinition->byval_arg, inst);
  154. }
  155. Il2CppGenericClass* GenericMetadata::GetGenericClass(const Il2CppType* genericTypeDefinition, const Il2CppGenericInst* inst)
  156. {
  157. // Assert that the element type is a non-inflated generic type defintion
  158. IL2CPP_ASSERT(genericTypeDefinition->type == IL2CPP_TYPE_CLASS || genericTypeDefinition->type == IL2CPP_TYPE_VALUETYPE);
  159. // temporary inst to lookup a permanent one that may already exist
  160. Il2CppGenericClass genericClass = { 0 };
  161. genericClass.type = genericTypeDefinition;
  162. genericClass.context.class_inst = inst;
  163. FastAutoLock lock(&s_GenericClassMutex);
  164. Il2CppGenericClassSet::const_iterator iter = s_GenericClassSet.find(&genericClass);
  165. if (iter != s_GenericClassSet.end())
  166. return *iter;
  167. Il2CppGenericClass* newClass = MetadataAllocGenericClass();
  168. newClass->type = genericTypeDefinition;
  169. newClass->context.class_inst = inst;
  170. s_GenericClassSet.insert(newClass);
  171. ++il2cpp_runtime_stats.generic_class_count;
  172. return newClass;
  173. }
  174. const MethodInfo* GenericMetadata::Inflate(const MethodInfo* methodDefinition, const Il2CppGenericContext* context)
  175. {
  176. return GenericMethod::GetMethod(methodDefinition, context->class_inst, context->method_inst);
  177. }
  178. static int RecursiveGenericDepthFor(const Il2CppGenericInst* inst);
  179. static int RecursiveGenericDepthFor(Il2CppGenericClass* genericClass)
  180. {
  181. int classInstDepth = RecursiveGenericDepthFor(genericClass->context.class_inst);
  182. int methodInstDepth = RecursiveGenericDepthFor(genericClass->context.method_inst);
  183. return std::max(classInstDepth, methodInstDepth);
  184. }
  185. static int RecursiveGenericDepthFor(const Il2CppGenericInst* inst)
  186. {
  187. if (inst == NULL)
  188. return 0;
  189. int maximumDepth = 0;
  190. for (size_t i = 0; i < inst->type_argc; i++)
  191. {
  192. if (inst->type_argv[i]->type == IL2CPP_TYPE_GENERICINST)
  193. {
  194. maximumDepth = std::max(maximumDepth, RecursiveGenericDepthFor(inst->type_argv[i]->data.generic_class));
  195. }
  196. }
  197. return maximumDepth + 1;
  198. }
  199. const Il2CppGenericMethod* GenericMetadata::Inflate(const Il2CppGenericMethod* genericMethod, const Il2CppGenericContext* context)
  200. {
  201. const Il2CppGenericInst* classInst = GetInflatedGenericIntance(genericMethod->context.class_inst, context, true);
  202. const Il2CppGenericInst* methodInst = GetInflatedGenericIntance(genericMethod->context.method_inst, context, true);
  203. // We have cases where we could infinitely recurse, inflating generics at runtime. This will lead to a stack overflow.
  204. // As we do for code generation, let's cut this off at an arbitrary level. If something tries to execute code at this
  205. // level, a crash will happen. We'll assume that this code won't actually be executed though.
  206. int maximumRuntimeGenericDepth = GetMaximumRuntimeGenericDepth();
  207. if (!il2cpp::vm::Runtime::IsLazyRGCTXInflationEnabled() && (RecursiveGenericDepthFor(classInst) > maximumRuntimeGenericDepth || RecursiveGenericDepthFor(methodInst) > maximumRuntimeGenericDepth))
  208. return NULL;
  209. return MetadataCache::GetGenericMethod(genericMethod->methodDefinition, classInst, methodInst);
  210. }
  211. const Il2CppGenericInst* GenericMetadata::GetInflatedGenericIntance(const Il2CppGenericInst* inst, const Il2CppGenericContext* context, bool inflateMethodVars)
  212. {
  213. if (inst == NULL)
  214. return NULL;
  215. const Il2CppType** inflatedArgs = (const Il2CppType**)alloca(inst->type_argc * sizeof(Il2CppType*));
  216. for (size_t i = 0; i < inst->type_argc; i++)
  217. inflatedArgs[i] = InflateIfNeeded(inst->type_argv[i], context, inflateMethodVars);
  218. return MetadataCache::GetGenericInst(inflatedArgs, inst->type_argc);
  219. }
  220. static void ConstrainedCallsToGenericInterfaceMethodsOnStructsAreNotSupported()
  221. {
  222. vm::Exception::Raise(vm::Exception::GetNotSupportedException("Cannot make a constrained call to a default interface method from a value type"));
  223. }
  224. static void ConstrainedCallsToGenericInterfaceMethodsOnStructsAreNotSupportedInvoker(Il2CppMethodPointer ptr, const MethodInfo* method, void* obj, void** args, void* ret)
  225. {
  226. ConstrainedCallsToGenericInterfaceMethodsOnStructsAreNotSupported();
  227. }
  228. Il2CppRGCTXData* GenericMetadata::InflateRGCTXLocked(const Il2CppImage* image, uint32_t token, const Il2CppGenericContext* context, const FastAutoLock& lock)
  229. {
  230. // This method assumes that it has the g_MetadataLock
  231. if (hybridclr::metadata::IsInterpreterImage(image))
  232. {
  233. return nullptr;
  234. }
  235. RGCTXCollection collection = MetadataCache::GetRGCTXs(image, token);
  236. if (collection.count == 0)
  237. return NULL;
  238. Il2CppRGCTXData* dataValues = (Il2CppRGCTXData*)MetadataCalloc(collection.count, sizeof(Il2CppRGCTXData), IL2CPP_MSTAT_RGCTX);
  239. RGCTXContextCollectData data = { context,collection };
  240. s_RGCTXDataToClassMap.add(dataValues, data);
  241. //[WL]remove here because we will do lazy init.
  242. /*
  243. for (RGCTXIndex rgctxIndex = 0; rgctxIndex < collection.count; rgctxIndex++)
  244. {
  245. const Il2CppRGCTXDefinition* definitionData = collection.items + rgctxIndex;
  246. switch (definitionData->type)
  247. {
  248. case IL2CPP_RGCTX_DATA_TYPE:
  249. dataValues[rgctxIndex].type = GenericMetadata::InflateIfNeeded(MetadataCache::GetTypeFromRgctxDefinition(definitionData), context, true);
  250. break;
  251. case IL2CPP_RGCTX_DATA_CLASS:
  252. dataValues[rgctxIndex].klass = Class::FromIl2CppType(GenericMetadata::InflateIfNeeded(MetadataCache::GetTypeFromRgctxDefinition(definitionData), context, true));
  253. break;
  254. case IL2CPP_RGCTX_DATA_METHOD:
  255. dataValues[rgctxIndex].method = GenericMethod::GetMethod(Inflate(MetadataCache::GetGenericMethodFromRgctxDefinition(definitionData), context));
  256. break;
  257. case IL2CPP_RGCTX_DATA_CONSTRAINED:
  258. {
  259. const Il2CppType* type;
  260. const MethodInfo* method;
  261. std::tie(type, method) = MetadataCache::GetConstrainedCallFromRgctxDefinition(definitionData);
  262. const Il2CppType* inflatedType = GenericMetadata::InflateIfNeeded(type, context, true);
  263. if (method->is_inflated)
  264. method = GenericMethod::GetMethod(Inflate(method->genericMethod, context));
  265. if (inflatedType->valuetype)
  266. {
  267. Il2CppClass* inflatedClass = Class::FromIl2CppType(inflatedType);
  268. Class::InitLocked(inflatedClass, lock);
  269. //[WL]
  270. Class::SetupVTable(inflatedClass);
  271. Class::InitLocked(method->klass, lock);
  272. method = Class::GetVirtualMethod(inflatedClass, method);
  273. }
  274. dataValues[rgctxIndex].method = method;
  275. }
  276. break;
  277. default:
  278. IL2CPP_ASSERT(0);
  279. }
  280. }
  281. */
  282. return dataValues;
  283. }
  284. void GenericMetadata::InflateRGCTXClass(const Il2CppRGCTXData* rgctxVar, RGCTXIndex index)
  285. {
  286. FastAutoLock lock(&g_MetadataLock);
  287. RGCTXContextCollectData& data = s_RGCTXDataToClassMap[rgctxVar];
  288. const Il2CppRGCTXDefinition* definitionData = data.collection.items + index;
  289. IL2CPP_ASSERT(definitionData->type == IL2CPP_RGCTX_DATA_CLASS);
  290. Il2CppRGCTXData* rgctx = const_cast<Il2CppRGCTXData*>(rgctxVar);
  291. rgctx[index].klass = Class::FromIl2CppType(GenericMetadata::InflateIfNeeded(MetadataCache::GetTypeFromRgctxDefinition(definitionData), data.context, true));
  292. Class::InitSizeAndFieldLayoutLocked(rgctx[index].klass, lock);
  293. }
  294. void GenericMetadata::InflateRGCTXType(const Il2CppRGCTXData* rgctxVar, RGCTXIndex index)
  295. {
  296. FastAutoLock lock(&g_MetadataLock);
  297. RGCTXContextCollectData& data = s_RGCTXDataToClassMap[rgctxVar];
  298. const Il2CppRGCTXDefinition* definitionData = data.collection.items + index;
  299. IL2CPP_ASSERT(definitionData->type == IL2CPP_RGCTX_DATA_TYPE);
  300. Il2CppRGCTXData* rgctx = const_cast<Il2CppRGCTXData*>(rgctxVar);
  301. rgctx[index].type = GenericMetadata::InflateIfNeeded(MetadataCache::GetTypeFromRgctxDefinition(definitionData), data.context, true);
  302. }
  303. void GenericMetadata::InflateRGCTXMethod(const Il2CppRGCTXData* rgctxVar, RGCTXIndex index)
  304. {
  305. FastAutoLock lock(&g_MetadataLock);
  306. RGCTXContextCollectData& data = s_RGCTXDataToClassMap[rgctxVar];
  307. const Il2CppRGCTXDefinition* definitionData = data.collection.items + index;
  308. const MethodInfo* retMethod = NULL;
  309. switch (definitionData->type)
  310. {
  311. case IL2CPP_RGCTX_DATA_METHOD:
  312. retMethod = GenericMethod::GetMethod(Inflate(MetadataCache::GetGenericMethodFromRgctxDefinition(definitionData), data.context));
  313. break;
  314. case IL2CPP_RGCTX_DATA_CONSTRAINED:
  315. {
  316. const Il2CppType* type;
  317. const MethodInfo* method;
  318. std::tie(type, method) = MetadataCache::GetConstrainedCallFromRgctxDefinition(definitionData);
  319. const Il2CppType* inflatedType = GenericMetadata::InflateIfNeeded(type, data.context, true);
  320. if (method->is_inflated)
  321. method = GenericMethod::GetMethod(Inflate(method->genericMethod, data.context));
  322. if (inflatedType->valuetype)
  323. {
  324. Il2CppClass* inflatedClass = Class::FromIl2CppType(inflatedType);
  325. Class::InitLocked(inflatedClass, lock);
  326. Class::SetupVTable(inflatedClass);
  327. Class::InitLocked(method->klass, lock);
  328. method = Class::GetVirtualMethod(inflatedClass, method);
  329. }
  330. retMethod = method;
  331. }
  332. break;
  333. default:
  334. IL2CPP_ASSERT(0);
  335. }
  336. Il2CppRGCTXData* rgctx = const_cast<Il2CppRGCTXData*>(rgctxVar);
  337. rgctx[index].method = retMethod;
  338. }
  339. // temporary while we generate generics
  340. void GenericMetadata::RegisterGenericClasses(Il2CppGenericClass* const * genericClasses, int32_t genericClassesCount)
  341. {
  342. s_GenericClassSet.resize(genericClassesCount / 2 + 1);
  343. // don't lock, this should only be called from startup and temporarily
  344. for (int32_t i = 0; i < genericClassesCount; i++)
  345. {
  346. if (genericClasses[i]->type != NULL)
  347. s_GenericClassSet.insert(genericClasses[i]);
  348. }
  349. }
  350. bool GenericMetadata::ContainsGenericParameters(const Il2CppClass* klass)
  351. {
  352. if (!klass->generic_class)
  353. return false;
  354. return ContainsGenericParameters(klass->generic_class->context.class_inst);
  355. }
  356. bool GenericMetadata::ContainsGenericParameters(const MethodInfo* method)
  357. {
  358. if (!method->is_inflated)
  359. return false;
  360. if (ContainsGenericParameters(method->genericMethod->context.method_inst))
  361. return true;
  362. if (method->genericMethod->context.class_inst == NULL)
  363. return false;
  364. return ContainsGenericParameters(method->genericMethod->context.class_inst);
  365. }
  366. bool GenericMetadata::ContainsGenericParameters(const Il2CppGenericInst* inst)
  367. {
  368. for (uint32_t i = 0; i < inst->type_argc; i++)
  369. {
  370. if (ContainsGenericParameters(inst->type_argv[i]))
  371. return true;
  372. }
  373. return false;
  374. }
  375. bool GenericMetadata::ContainsGenericParameters(const Il2CppType* type)
  376. {
  377. switch (type->type)
  378. {
  379. case IL2CPP_TYPE_VAR:
  380. case IL2CPP_TYPE_MVAR:
  381. return true;
  382. case IL2CPP_TYPE_GENERICINST:
  383. return ContainsGenericParameters(type->data.generic_class->context.class_inst);
  384. case IL2CPP_TYPE_ARRAY:
  385. return ContainsGenericParameters(type->data.array->etype);
  386. case IL2CPP_TYPE_SZARRAY:
  387. case IL2CPP_TYPE_PTR:
  388. case IL2CPP_TYPE_BYREF:
  389. return ContainsGenericParameters(type->data.type);
  390. default:
  391. return false;
  392. }
  393. return false;
  394. }
  395. void GenericMetadata::WalkAllGenericClasses(GenericClassWalkCallback callback, void* context)
  396. {
  397. FastAutoLock lock(&s_GenericClassMutex);
  398. for (Il2CppGenericClassSet::iterator it = s_GenericClassSet.begin(); it != s_GenericClassSet.end(); it++)
  399. {
  400. if ((*it).key->cached_class != NULL)
  401. callback((*it).key->cached_class, context);
  402. }
  403. }
  404. void GenericMetadata::Clear()
  405. {
  406. for (Il2CppGenericClassSet::iterator genericClass = s_GenericClassSet.begin(); genericClass != s_GenericClassSet.end(); genericClass++)
  407. (*genericClass).key->cached_class = NULL;
  408. s_GenericClassSet.clear();
  409. s_RGCTXDataToClassMap.clear();
  410. }
  411. static int s_MaximumRuntimeGenericDepth;
  412. static int s_GenericVirtualIterations;
  413. int GenericMetadata::GetMaximumRuntimeGenericDepth()
  414. {
  415. return s_MaximumRuntimeGenericDepth;
  416. }
  417. void GenericMetadata::SetMaximumRuntimeGenericDepth(int depth)
  418. {
  419. s_MaximumRuntimeGenericDepth = depth;
  420. }
  421. int GenericMetadata::GetGenericVirtualIterations()
  422. {
  423. return s_GenericVirtualIterations;
  424. }
  425. void GenericMetadata::SetGenericVirtualIterations(int iterations)
  426. {
  427. s_GenericVirtualIterations = iterations;
  428. }
  429. } /* namespace vm */
  430. } /* namespace il2cpp */