RCW.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. #include "il2cpp-config.h"
  2. #include "il2cpp-object-internals.h"
  3. #include "il2cpp-class-internals.h"
  4. #include "il2cpp-tabledefs.h"
  5. #include "gc/GCHandle.h"
  6. #include "metadata/GenericMetadata.h"
  7. #include "vm/Exception.h"
  8. #include "vm/Field.h"
  9. #include "vm/GenericClass.h"
  10. #include "vm/MetadataCache.h"
  11. #include "vm/Object.h"
  12. #include "vm/PlatformInvoke.h"
  13. #include "vm/RCW.h"
  14. #include "vm/Runtime.h"
  15. #include "os/Atomic.h"
  16. #include "os/COM.h"
  17. #include "vm/Monitor.h"
  18. #include "os/Mutex.h"
  19. #include "os/WindowsRuntime.h"
  20. #include "utils/Il2CppError.h"
  21. #include "utils/Il2CppHashMap.h"
  22. #include "utils/HashUtils.h"
  23. #include "utils/StringUtils.h"
  24. #include "Baselib.h"
  25. #include "Cpp/ReentrantLock.h"
  26. const Il2CppGuid Il2CppIUnknown::IID = { 0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
  27. const Il2CppGuid Il2CppISequentialStream::IID = { 0x0c733a30, 0x2a1c, 0x11ce, 0xad, 0xe5, 0x00, 0xaa, 0x00, 0x44, 0x77, 0x3d };
  28. const Il2CppGuid Il2CppIStream::IID = { 0x0000000c, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
  29. const Il2CppGuid Il2CppIMarshal::IID = { 0x00000003, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
  30. const Il2CppGuid Il2CppIManagedObject::IID = { 0xc3fcc19e, 0xa970, 0x11d2, 0x8b, 0x5a, 0x00, 0xa0, 0xc9, 0xb7, 0xc9, 0xc4 };
  31. const Il2CppGuid Il2CppIManagedObjectHolder::IID = { 0xd4bbc1c8, 0xf5bf, 0x4647, 0x94, 0x95, 0x2e, 0x5c, 0xf, 0x20, 0xf7, 0x5d };
  32. const Il2CppGuid Il2CppIInspectable::IID = { 0xaf86e2e0, 0xb12d, 0x4c6a, 0x9c, 0x5a, 0xd7, 0xaa, 0x65, 0x10, 0x1E, 0x90 };
  33. const Il2CppGuid Il2CppIActivationFactory::IID = { 0x00000035, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
  34. const Il2CppGuid Il2CppIRestrictedErrorInfo::IID = { 0x82ba7092, 0x4c88, 0x427d, 0xa7, 0xbc, 0x16, 0xdd, 0x93, 0xfe, 0xb6, 0x7e };
  35. const Il2CppGuid Il2CppILanguageExceptionErrorInfo::IID = { 0x04a2dbf3, 0xdf83, 0x116c, 0x09, 0x46, 0x08, 0x12, 0xab, 0xf6, 0xe0, 0x7d };
  36. const Il2CppGuid Il2CppIAgileObject::IID = { 0x94ea2b94, 0xe9cc, 0x49e0, 0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90 };
  37. const Il2CppGuid Il2CppIWeakReference::IID = { 0x00000037, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
  38. const Il2CppGuid Il2CppIWeakReferenceSource::IID = { 0x00000038, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
  39. namespace il2cpp
  40. {
  41. namespace vm
  42. {
  43. #if !IL2CPP_TRIM_COM
  44. typedef Il2CppHashMap<Il2CppIUnknown*, /* Weak GC Handle */ uint32_t, il2cpp::utils::PointerHash<Il2CppIUnknown> > RCWCache;
  45. static baselib::ReentrantLock s_RCWCacheMutex;
  46. static RCWCache s_RCWCache;
  47. #endif
  48. void RCW::Register(Il2CppComObject* rcw)
  49. {
  50. #if IL2CPP_TRIM_COM
  51. IL2CPP_NOT_IMPLEMENTED(RCW::Register);
  52. #else
  53. os::FastAutoLock lock(&s_RCWCacheMutex);
  54. rcw->refCount = 1;
  55. auto weakRef = gc::GCHandle::NewWeakref(rcw, false);
  56. vm::Exception::RaiseIfError(weakRef.GetError());
  57. const bool inserted = s_RCWCache.insert(std::make_pair(rcw->identity, weakRef.Get())).second;
  58. Assert(inserted);
  59. #endif
  60. }
  61. static inline Il2CppIUnknown* GetIdentity(Il2CppIUnknown* unknown)
  62. {
  63. Il2CppIUnknown* identity;
  64. il2cpp_hresult_t hr = unknown->QueryInterface(Il2CppIUnknown::IID, reinterpret_cast<void**>(&identity));
  65. vm::Exception::RaiseIfFailed(hr, true);
  66. IL2CPP_ASSERT(identity);
  67. return identity;
  68. }
  69. // Shameless comment copycat from .NET Native (https://github.com/dotnet/corert/blob/374c3d47992a7c444ec7d1dfe94b1780de942a55/src/System.Private.Interop/src/Shared/McgComHelpers.cs#L557):
  70. // 1. Prefer using the class returned from GetRuntimeClassName
  71. // 2. Otherwise use the class (if there) in the signature
  72. // 3. Out of options - create Il2CppComObject
  73. static inline Il2CppClass* GetClassForRCW(Il2CppIInspectable* inspectable, Il2CppClass* fallbackClass)
  74. {
  75. Il2CppHString className;
  76. il2cpp_hresult_t hr = inspectable->GetRuntimeClassName(&className);
  77. if (IL2CPP_HR_FAILED(hr) || className == NULL)
  78. return fallbackClass;
  79. uint32_t classNameLength;
  80. auto classNamePtr = os::WindowsRuntime::GetHStringBuffer(className, &classNameLength);
  81. vm::Exception::RaiseIfError(classNamePtr.GetError());
  82. std::string classNameUtf8 = utils::StringUtils::Utf16ToUtf8(classNamePtr.Get(), classNameLength);
  83. os::WindowsRuntime::DeleteHString(className);
  84. Il2CppClass* rcwClass = MetadataCache::GetWindowsRuntimeClass(classNameUtf8.c_str());
  85. return rcwClass != NULL ? rcwClass : fallbackClass;
  86. }
  87. static inline Il2CppClass* GetClassForRCW(Il2CppIUnknown* unknown, Il2CppClass* fallbackClass)
  88. {
  89. Il2CppIInspectable* inspectable;
  90. il2cpp_hresult_t hr = unknown->QueryInterface(Il2CppIInspectable::IID, reinterpret_cast<void**>(&inspectable));
  91. if (IL2CPP_HR_FAILED(hr))
  92. return fallbackClass;
  93. Il2CppClass* result = GetClassForRCW(inspectable, fallbackClass);
  94. inspectable->Release();
  95. return result;
  96. }
  97. Il2CppObject* ReboxIReference(Il2CppIUnknown* comObject, Il2CppClass* objectClass);
  98. Il2CppObject* ReboxKeyValuePair(Il2CppIUnknown* comObject, Il2CppClass* keyValuePairGenericInstance);
  99. Il2CppObject* ReboxUri(Il2CppIUnknown* comObject);
  100. Il2CppObject* ReboxIfBoxed(Il2CppIUnknown* comObject, Il2CppClass* objectClass)
  101. {
  102. if (strcmp(objectClass->namespaze, "Windows.Foundation") == 0)
  103. {
  104. if (strcmp(objectClass->name, "IReference`1") == 0 || strcmp(objectClass->name, "IReferenceArray`1") == 0)
  105. return ReboxIReference(comObject, objectClass);
  106. }
  107. else if (strcmp(objectClass->namespaze, "System.Collections.Generic") == 0 && strcmp(objectClass->name, "KeyValuePair`2") == 0)
  108. {
  109. return ReboxKeyValuePair(comObject, objectClass);
  110. }
  111. else if (objectClass == il2cpp_defaults.system_uri_class)
  112. {
  113. return ReboxUri(comObject);
  114. }
  115. return NULL;
  116. }
  117. Il2CppObject* ReboxIReference(Il2CppIUnknown* comObject, Il2CppClass* objectClass)
  118. {
  119. #if IL2CPP_TRIM_COM
  120. return NULL;
  121. #else
  122. Class::Init(objectClass);
  123. Class::SetupVTable(objectClass);
  124. // Sanity checks
  125. IL2CPP_ASSERT(Class::IsInflated(objectClass));
  126. IL2CPP_ASSERT(objectClass->vtable_count == 1); // IReference`1<T> only has get_Value method
  127. const MethodInfo* getValueMethod = objectClass->vtable[0].method;
  128. IL2CPP_ASSERT(strcmp(getValueMethod->name, "get_Value") == 0);
  129. // We don't really want to allocate it on the GC heap for this little invocation
  130. Il2CppComObject fakeRcw;
  131. memset(&fakeRcw, 0, sizeof(fakeRcw));
  132. fakeRcw.klass = objectClass;
  133. fakeRcw.identity = comObject;
  134. Il2CppException* exception = NULL;
  135. Il2CppObject* reboxed = Runtime::Invoke(getValueMethod, &fakeRcw, NULL, &exception);
  136. if (exception != NULL)
  137. Exception::Raise(exception);
  138. return reboxed;
  139. #endif
  140. }
  141. Il2CppObject* ReboxKeyValuePair(Il2CppIUnknown* comObject, Il2CppClass* keyValuePairGenericInstance)
  142. {
  143. #if IL2CPP_TRIM_COM
  144. return NULL;
  145. #else
  146. Class::Init(keyValuePairGenericInstance);
  147. Class::SetupVTable(keyValuePairGenericInstance);
  148. // Sanity checks
  149. IL2CPP_ASSERT(Class::IsInflated(keyValuePairGenericInstance));
  150. IL2CPP_ASSERT(il2cpp_defaults.ikey_value_pair_class != NULL);
  151. // Retrieve Windows.Foundation.Collections.IKeyValuePair`1<K, V> generic instance
  152. Il2CppGenericClass* iKeyValuePairGenericClass = metadata::GenericMetadata::GetGenericClass(il2cpp_defaults.ikey_value_pair_class, keyValuePairGenericInstance->generic_class->context.class_inst);
  153. Il2CppClass* iKeyValuePairGenericInstance = GenericClass::GetClass(iKeyValuePairGenericClass);
  154. Class::Init(iKeyValuePairGenericInstance);
  155. Class::SetupVTable(iKeyValuePairGenericInstance);
  156. IL2CPP_ASSERT(iKeyValuePairGenericInstance->vtable_count == 2);
  157. const MethodInfo* getKeyMethod = iKeyValuePairGenericInstance->vtable[0].method;
  158. IL2CPP_ASSERT(strcmp(getKeyMethod->name, "get_Key") == 0);
  159. const MethodInfo* getValueMethod = iKeyValuePairGenericInstance->vtable[1].method;
  160. IL2CPP_ASSERT(strcmp(getValueMethod->name, "get_Value") == 0);
  161. Il2CppComObject fakeRcw;
  162. memset(&fakeRcw, 0, sizeof(fakeRcw));
  163. fakeRcw.klass = il2cpp_defaults.il2cpp_com_object_class;
  164. fakeRcw.identity = comObject;
  165. // Create new boxed key value pair
  166. Il2CppObject* reboxed = Object::New(keyValuePairGenericInstance);
  167. for (uint16_t i = 0; i < 2; i++)
  168. {
  169. const MethodInfo* methodToInvoke = NULL;
  170. const FieldInfo& field = keyValuePairGenericInstance->fields[i];
  171. // Figure out which getter to call
  172. if (strcmp(field.name, "key") == 0)
  173. {
  174. methodToInvoke = getKeyMethod;
  175. }
  176. else if (strcmp(field.name, "value") == 0)
  177. {
  178. methodToInvoke = getValueMethod;
  179. }
  180. // Call the getter
  181. Il2CppException* exception = NULL;
  182. Il2CppObject* fieldValue = Runtime::Invoke(methodToInvoke, &fakeRcw, NULL, &exception);
  183. if (exception != NULL)
  184. Exception::Raise(exception);
  185. // Set the field in our reboxed key value pair instance
  186. if (Class::FromIl2CppType(field.type)->byval_arg.valuetype)
  187. {
  188. Field::SetValue(reboxed, &field, Object::Unbox(fieldValue));
  189. }
  190. else
  191. {
  192. Field::SetValue(reboxed, &field, fieldValue);
  193. }
  194. }
  195. return reboxed;
  196. #endif
  197. }
  198. Il2CppObject* ReboxUri(Il2CppIUnknown* comObject)
  199. {
  200. #if IL2CPP_TRIM_COM
  201. return NULL;
  202. #else
  203. Il2CppClass* systemUriClass = il2cpp_defaults.system_uri_class;
  204. Il2CppClass* iUriRuntimeClassClass = il2cpp_defaults.windows_foundation_iuri_runtime_class_class;
  205. Class::Init(systemUriClass);
  206. Class::SetupVTable(systemUriClass);
  207. Class::Init(iUriRuntimeClassClass);
  208. Class::SetupVTable(iUriRuntimeClassClass);
  209. const int kGetRawUriMethodIndex = 10; // IUriRuntimeClass::get_RawUri
  210. IL2CPP_ASSERT(iUriRuntimeClassClass->vtable_count > kGetRawUriMethodIndex);
  211. VirtualInvokeData getRawUriInvokeData = iUriRuntimeClassClass->vtable[kGetRawUriMethodIndex];
  212. IL2CPP_ASSERT(strcmp(getRawUriInvokeData.method->name, "get_RawUri") == 0);
  213. Il2CppComObject fakeRcw;
  214. memset(&fakeRcw, 0, sizeof(fakeRcw));
  215. fakeRcw.klass = il2cpp_defaults.il2cpp_com_object_class;
  216. fakeRcw.identity = comObject;
  217. Il2CppObject* rawUri = Runtime::InvokeWithThrow(getRawUriInvokeData.method, &fakeRcw, NULL);
  218. const MethodInfo* uriConstructor = NULL;
  219. uint16_t uriMethodCount = systemUriClass->method_count;
  220. for (uint16_t i = 0; i < uriMethodCount; i++)
  221. {
  222. const MethodInfo* method = systemUriClass->methods[i];
  223. if (strcmp(method->name, ".ctor") == 0 && method->parameters_count == 1 && method->parameters[0]->type == IL2CPP_TYPE_STRING)
  224. {
  225. uriConstructor = method;
  226. break;
  227. }
  228. }
  229. IL2CPP_ASSERT(uriConstructor);
  230. Il2CppObject* reboxedUri = Object::New(systemUriClass);
  231. void* constructorArgs[1] = { rawUri };
  232. Runtime::InvokeWithThrow(uriConstructor, reboxedUri, constructorArgs);
  233. return reboxedUri;
  234. #endif
  235. }
  236. template<typename T, bool isSealedClassInstance>
  237. static inline Il2CppObject* GetOrCreateRCW(T* comObject, Il2CppClass* objectClass)
  238. {
  239. #if IL2CPP_TRIM_COM
  240. IL2CPP_NOT_IMPLEMENTED(GetOrCreateRCW);
  241. return NULL;
  242. #else
  243. IL2CPP_ASSERT(comObject != NULL);
  244. if (!isSealedClassInstance)
  245. {
  246. // 1. Check if comObject is actually our COM Callable Wrapper
  247. Il2CppIManagedObjectHolder* managedHolder;
  248. il2cpp_hresult_t hr = comObject->QueryInterface(Il2CppIManagedObjectHolder::IID, reinterpret_cast<void**>(&managedHolder));
  249. if (IL2CPP_HR_SUCCEEDED(hr))
  250. {
  251. Il2CppObject* instance = managedHolder->GetManagedObject();
  252. managedHolder->Release();
  253. IL2CPP_ASSERT(instance);
  254. return instance;
  255. }
  256. }
  257. Il2CppIUnknown* identity = GetIdentity(comObject);
  258. // 2. Try to find it in RCW cache
  259. os::FastAutoLock lock(&s_RCWCacheMutex);
  260. RCWCache::iterator iter = s_RCWCache.find(identity);
  261. if (iter != s_RCWCache.end())
  262. {
  263. Il2CppComObject* obj = static_cast<Il2CppComObject*>(gc::GCHandle::GetTarget(iter->second));
  264. if (obj != NULL)
  265. {
  266. // Make sure the RCW isn't dead. If increment returns 1, it means
  267. // that the ref count had previous reached 0 and was released
  268. if (os::Atomic::Increment(&obj->refCount) > 1)
  269. {
  270. identity->Release();
  271. identity = NULL;
  272. return obj;
  273. }
  274. }
  275. // The RCW was already queued for finalization or destroyed by ref count reaching 0.
  276. // Erase it from the cache and let us create a new one.
  277. s_RCWCache.erase(iter);
  278. }
  279. // 3. Figure out the concrete RCW class
  280. if (!isSealedClassInstance)
  281. {
  282. Il2CppClass* fallbackClass = objectClass;
  283. objectClass = GetClassForRCW(comObject, fallbackClass);
  284. // If object class is one of the blessed unboxable classes,
  285. // unbox the object from its windows runtime representation,
  286. // unmarshal it, box it to Il2CppObject and return it
  287. //
  288. // Current list of unboxable classes:
  289. // Windows.Foundation.IReference`1<T>
  290. // Windows.Foundation.IReferenceArray`1<T>
  291. // System.Collections.Generic.KeyValuePair`2<K, V>
  292. // System.Uri
  293. Il2CppObject* reboxed = ReboxIfBoxed(comObject, objectClass);
  294. if (reboxed != NULL)
  295. return reboxed;
  296. if (objectClass->byval_arg.type != IL2CPP_TYPE_CLASS ||
  297. objectClass->flags & TYPE_ATTRIBUTE_INTERFACE ||
  298. objectClass->is_generic)
  299. {
  300. // We must be able to instantiate the type. If we can't, fallback to a caller passed in type
  301. objectClass = fallbackClass;
  302. }
  303. }
  304. IL2CPP_ASSERT(Class::HasParent(objectClass, il2cpp_defaults.il2cpp_com_object_class));
  305. // 4. Create RCW object
  306. Il2CppComObject* rcw = static_cast<Il2CppComObject*>(Object::New(objectClass));
  307. rcw->identity = identity;
  308. rcw->refCount = 1;
  309. // 5. Insert it into the cache
  310. auto weakRef = gc::GCHandle::NewWeakref(rcw, false);
  311. vm::Exception::RaiseIfError(weakRef.GetError());
  312. const bool inserted = s_RCWCache.insert(std::make_pair(identity, weakRef.Get())).second;
  313. Assert(inserted);
  314. return rcw;
  315. #endif
  316. }
  317. Il2CppObject* RCW::GetOrCreateFromIUnknown(Il2CppIUnknown* unknown, Il2CppClass* fallbackClass)
  318. {
  319. return GetOrCreateRCW<Il2CppIUnknown, false>(unknown, fallbackClass);
  320. }
  321. Il2CppObject* RCW::GetOrCreateFromIInspectable(Il2CppIInspectable* inspectable, Il2CppClass* fallbackClass)
  322. {
  323. return GetOrCreateRCW<Il2CppIInspectable, false>(inspectable, fallbackClass);
  324. }
  325. Il2CppObject* RCW::GetOrCreateForSealedClass(Il2CppIUnknown* unknown, Il2CppClass* objectClass)
  326. {
  327. return GetOrCreateRCW<Il2CppIUnknown, true>(unknown, objectClass);
  328. }
  329. void RCW::Cleanup(Il2CppComObject* rcw)
  330. {
  331. #if IL2CPP_TRIM_COM
  332. IL2CPP_NOT_IMPLEMENTED(RCW::Cleanup);
  333. #else
  334. if (rcw->klass->is_import_or_windows_runtime)
  335. {
  336. os::FastAutoLock lock(&s_RCWCacheMutex);
  337. RCWCache::iterator iter = s_RCWCache.find(rcw->identity);
  338. // It is possible for us to not find object in the cache if two RCWs for the same IUnknown get
  339. // finalized in a row: then, the first finalizer will remove the NULL object, and the second one
  340. // will not find it.
  341. if (iter != s_RCWCache.end())
  342. {
  343. Il2CppObject* obj = gc::GCHandle::GetTarget(iter->second);
  344. // If it's null, it means that the cache contains our object
  345. // but the weak GC handle has been invalidated by the GC already
  346. // If it's equal to our object, it means that RCW::Cleanup was
  347. // called manually, and we should also delete it from the cache
  348. // Otherwise, it's a different object. It means that we have already
  349. // created a new RCW in place of this one during the time
  350. // it had been queued for finalization
  351. if (obj == NULL || obj == rcw)
  352. s_RCWCache.erase(iter);
  353. }
  354. }
  355. int32_t shortCacheSize = rcw->qiShortCacheSize;
  356. for (int32_t i = 0; i < shortCacheSize; i++)
  357. rcw->qiShortCache[i].qiResult->Release();
  358. int32_t longCacheSize = rcw->qiLongCacheSize;
  359. if (longCacheSize > 0)
  360. {
  361. for (int32_t i = 0; i < longCacheSize; i++)
  362. rcw->qiLongCache[i].qiResult->Release();
  363. IL2CPP_FREE(rcw->qiLongCache, IL2CPP_MEM_RCW);
  364. }
  365. #endif
  366. }
  367. Il2CppIUnknown* RCW::QueryInterfaceCached(Il2CppComObject* rcw, const Il2CppGuid& iid)
  368. {
  369. MonitorHolder monitorHolder(rcw);
  370. int32_t shortCacheSize = rcw->qiShortCacheSize;
  371. for (int32_t i = 0; i < shortCacheSize; i++)
  372. {
  373. const Il2CppGuid* queriedInterface = rcw->qiShortCache[i].iid;
  374. if (queriedInterface == &iid)
  375. return rcw->qiShortCache[i].qiResult;
  376. }
  377. int32_t longCacheSize = rcw->qiLongCacheSize;
  378. for (int32_t i = 0; i < longCacheSize; i++)
  379. {
  380. const Il2CppGuid* queriedInterface = rcw->qiLongCache[i].iid;
  381. if (queriedInterface == &iid)
  382. return rcw->qiLongCache[i].qiResult;
  383. }
  384. return NULL;
  385. }
  386. bool RCW::CacheQueriedInterface(Il2CppComObject* rcw, const Il2CppGuid& iid, Il2CppIUnknown* queriedInterface)
  387. {
  388. MonitorHolder monitorHolder(rcw);
  389. QICache cache = { &iid, queriedInterface };
  390. // We need to rescan caches in case another thread got to cache it first
  391. int32_t shortCacheSize = rcw->qiShortCacheSize;
  392. IL2CPP_ASSERT(shortCacheSize <= IL2CPP_ARRAY_SIZE(rcw->qiShortCache));
  393. for (int32_t i = 0; i < shortCacheSize; i++)
  394. {
  395. const Il2CppGuid* queriedInterface = rcw->qiShortCache[i].iid;
  396. if (queriedInterface == &iid)
  397. return false;
  398. }
  399. if (shortCacheSize == IL2CPP_ARRAY_SIZE(rcw->qiShortCache))
  400. {
  401. // We only need to check long cache if short cache is full
  402. int32_t longCacheSize = rcw->qiLongCacheSize;
  403. for (int32_t i = 0; i < longCacheSize; i++)
  404. {
  405. const Il2CppGuid* queriedInterface = rcw->qiLongCache[i].iid;
  406. if (queriedInterface == &iid)
  407. return false;
  408. }
  409. }
  410. else
  411. {
  412. rcw->qiShortCache[shortCacheSize] = cache;
  413. rcw->qiShortCacheSize = shortCacheSize + 1;
  414. return true;
  415. }
  416. int32_t longCacheSize = rcw->qiLongCacheSize;
  417. int32_t longCacheCapacity = rcw->qiLongCacheCapacity;
  418. IL2CPP_ASSERT(longCacheSize <= longCacheCapacity);
  419. if (longCacheSize == longCacheCapacity)
  420. {
  421. longCacheCapacity *= 2;
  422. rcw->qiLongCache = static_cast<QICache*>(IL2CPP_REALLOC(rcw->qiLongCache, sizeof(QICache) * longCacheCapacity, IL2CPP_MEM_RCW));
  423. rcw->qiLongCacheCapacity = longCacheCapacity;
  424. }
  425. rcw->qiLongCache[longCacheSize] = cache;
  426. rcw->qiLongCacheSize = longCacheSize + 1;
  427. return true;
  428. }
  429. const VirtualInvokeData* RCW::GetComInterfaceInvokeData(Il2CppClass* queriedInterface, const Il2CppClass* targetInterface, Il2CppMethodSlot slot)
  430. {
  431. Class::Init(queriedInterface);
  432. Class::SetupVTable(queriedInterface);
  433. uint16_t vtableCount = queriedInterface->vtable_count;
  434. if (targetInterface->generic_class != NULL)
  435. {
  436. if (Class::IsGenericClassAssignableFrom(targetInterface, queriedInterface))
  437. return NULL;
  438. const Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets = queriedInterface->interfaceOffsets;
  439. uint16_t interfaceOffsetsCount = queriedInterface->interface_offsets_count;
  440. for (uint16_t i = 0; i < interfaceOffsetsCount; i++)
  441. {
  442. if (Class::IsGenericClassAssignableFrom(targetInterface, interfaceOffsets[i].interfaceType))
  443. {
  444. Il2CppMethodSlot slotWithOffset = interfaceOffsets[i].offset + slot;
  445. if (slotWithOffset < vtableCount)
  446. return &queriedInterface->vtable[slotWithOffset];
  447. }
  448. }
  449. }
  450. else
  451. {
  452. const Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets = queriedInterface->interfaceOffsets;
  453. uint16_t interfaceOffsetsCount = queriedInterface->interface_offsets_count;
  454. for (uint16_t i = 0; i < interfaceOffsetsCount; ++i)
  455. {
  456. if (interfaceOffsets[i].interfaceType == targetInterface)
  457. {
  458. Il2CppMethodSlot slotWithOffset = interfaceOffsets[i].offset + slot;
  459. if (slotWithOffset < vtableCount)
  460. return &queriedInterface->vtable[slotWithOffset];
  461. }
  462. }
  463. }
  464. Il2CppClass* const* implementedInterfaces = queriedInterface->implementedInterfaces;
  465. uint16_t implementedInterfacesCount = queriedInterface->interfaces_count;
  466. for (uint16_t i = 0; i < implementedInterfacesCount; i++)
  467. {
  468. Il2CppClass* implementedInterface = implementedInterfaces[i];
  469. const VirtualInvokeData* invokeData = GetComInterfaceInvokeData(implementedInterface, targetInterface, slot);
  470. if (invokeData != NULL)
  471. return invokeData;
  472. }
  473. return NULL;
  474. }
  475. const VirtualInvokeData* RCW::GetComInterfaceInvokeData(Il2CppComObject* rcw, const Il2CppClass* targetInterface, Il2CppMethodSlot slot)
  476. {
  477. uint16_t vtableCount = targetInterface->vtable_count;
  478. if (slot < vtableCount)
  479. {
  480. const Il2CppInteropData* itfInteropData = targetInterface->interopData;
  481. if (itfInteropData != NULL)
  482. {
  483. const Il2CppGuid* itfGuid = itfInteropData->guid;
  484. if (itfGuid != NULL)
  485. {
  486. // Try querying for the interface we were asked
  487. if (RCW::QueryInterfaceNoAddRef<false>(rcw, *itfGuid) != NULL)
  488. return &targetInterface->vtable[slot];
  489. }
  490. }
  491. }
  492. if (targetInterface->is_import_or_windows_runtime)
  493. return NULL;
  494. // For projected interfaces, we look in the cache for compatible interface in order to handle these scenarios:
  495. // * Covariable/Contravariance. For instance, we should be able to invoke IReadOnlyList<object> methods on IReadOnlyList<string>, even though if QI fails for IVectorView<object>
  496. // * Inherited interfaces on CLR but not Windows Runtime side. For instance, IEnumerable<T> implements IEnumerable but IIterable<T> does not implement IBindableIterable
  497. MonitorHolder monitorHolder(rcw);
  498. int32_t shortCacheSize = rcw->qiShortCacheSize;
  499. for (int32_t i = 0; i < shortCacheSize; i++)
  500. {
  501. Il2CppClass* queriedInterface = vm::MetadataCache::GetClassForGuid(rcw->qiShortCache[i].iid);
  502. if (queriedInterface != NULL)
  503. {
  504. const VirtualInvokeData* invokeData = GetComInterfaceInvokeData(queriedInterface, targetInterface, slot);
  505. if (invokeData != NULL)
  506. return invokeData;
  507. }
  508. }
  509. int32_t longCacheSize = rcw->qiLongCacheSize;
  510. for (int32_t i = 0; i < longCacheSize; i++)
  511. {
  512. Il2CppClass* queriedInterface = vm::MetadataCache::GetClassForGuid(rcw->qiLongCache[i].iid);
  513. if (queriedInterface != NULL)
  514. {
  515. const VirtualInvokeData* invokeData = GetComInterfaceInvokeData(queriedInterface, targetInterface, slot);
  516. if (invokeData != NULL)
  517. return invokeData;
  518. }
  519. }
  520. if (slot < vtableCount)
  521. return &targetInterface->vtable[slot];
  522. return NULL;
  523. }
  524. } /* namespace vm */
  525. } /* namespace il2cpp */