2025-07-03 12:53:49 +04:00

837 lines
33 KiB
C++

#include "il2cpp-config.h"
#include "metadata/GenericMetadata.h"
#include "os/LibraryLoader.h"
#include "os/WindowsRuntime.h"
#include "utils/StringUtils.h"
#include "utils/StringViewUtils.h"
#include "vm/AssemblyName.h"
#include "vm/Class.h"
#include "vm/Exception.h"
#include "vm/GenericClass.h"
#include "vm/Il2CppHStringReference.h"
#include "vm/Image.h"
#include "vm/MetadataCache.h"
#include "vm/Type.h"
#include "vm/WindowsRuntime.h"
namespace il2cpp
{
namespace vm
{
const char kArrayTypePrefixUtf8[] = "Windows.Foundation.IReferenceArray`1<";
const Il2CppNativeChar kArrayTypePrefix[] = IL2CPP_NATIVE_STRING("Windows.Foundation.IReferenceArray`1<");
const Il2CppNativeChar kIReferencePrefix[] = IL2CPP_NATIVE_STRING("Windows.Foundation.IReference`1<");
const Il2CppNativeChar kArrayTypeOrIReferencePrefix[] = IL2CPP_NATIVE_STRING("Windows.Foundation.IReference");
const Il2CppNativeChar kArrayTypePostprefix[] = IL2CPP_NATIVE_STRING("Array`1<");
const Il2CppNativeChar kIReferencePostprefix[] = IL2CPP_NATIVE_STRING("`1<");
Il2CppIActivationFactory* WindowsRuntime::GetActivationFactory(const utils::StringView<Il2CppNativeChar>& runtimeClassName)
{
Il2CppHStringReference className(runtimeClassName);
Il2CppIActivationFactory* factory = NULL;
il2cpp_hresult_t hr = os::WindowsRuntime::GetActivationFactory(className, &factory);
if (IL2CPP_HR_SUCCEEDED(hr))
return factory;
if (hr != IL2CPP_REGDB_E_CLASSNOTREG)
Exception::Raise(hr, false);
// If GetActivationFactory doesn't find the class, we can still try to find it manually
// All Windows runtime classes must be in namespaces, and that class has to be in a DLL
// that is named after the namespace of a part of it.
// For example, MyNamespace.MySubNamespace.MyClass can be in either
// MyNamespace.MySubNamespace.dll or MyNamespace.dll
IL2CPP_ASSERT(runtimeClassName.Length() > 1);
size_t namespaceEnd = runtimeClassName.Length() - 1;
do
{
namespaceEnd--;
}
while (namespaceEnd > 0 && runtimeClassName[namespaceEnd] != '.');
Il2CppNativeChar* nativeDll = static_cast<Il2CppNativeChar*>(alloca((namespaceEnd + 5) * sizeof(Il2CppNativeChar)));
memcpy(nativeDll, runtimeClassName.Str(), namespaceEnd * sizeof(Il2CppNativeChar));
std::string detailedError;
while (namespaceEnd > 0)
{
memcpy(nativeDll + namespaceEnd, IL2CPP_NATIVE_STRING(".dll"), 4 * sizeof(Il2CppNativeChar));
nativeDll[namespaceEnd + 4] = 0;
Baselib_DynamicLibrary_Handle dynamicLibrary = os::LibraryLoader::LoadDynamicLibrary(utils::StringView<Il2CppNativeChar>(nativeDll, namespaceEnd + 4), detailedError);
if (dynamicLibrary != Baselib_DynamicLibrary_Handle_Invalid)
{
typedef il2cpp_hresult_t(STDCALL * DllGetActivationFactory)(Il2CppHString activatableClassId, Il2CppIActivationFactory** factory);
DllGetActivationFactory dllGetActivationFactory = reinterpret_cast<DllGetActivationFactory>(os::LibraryLoader::GetFunctionPointer(dynamicLibrary, "DllGetActivationFactory", detailedError));
if (dllGetActivationFactory != NULL)
{
hr = dllGetActivationFactory(className, &factory);
if (IL2CPP_HR_SUCCEEDED(hr))
return factory;
if (hr != IL2CPP_REGDB_E_CLASSNOTREG)
Exception::Raise(hr, false);
}
}
do
{
namespaceEnd--;
}
while (namespaceEnd > 0 && runtimeClassName[namespaceEnd] != '.');
}
Exception::Raise(IL2CPP_REGDB_E_CLASSNOTREG, false);
return NULL;
}
static bool IsWindowsRuntimePrimitiveType(const Il2CppType* type, Il2CppClass*& outCachedNonPrimitiveClass)
{
if (type == NULL)
return false;
switch (type->type)
{
case IL2CPP_TYPE_BOOLEAN:
case IL2CPP_TYPE_CHAR:
case IL2CPP_TYPE_U1:
case IL2CPP_TYPE_I2:
case IL2CPP_TYPE_U2:
case IL2CPP_TYPE_I4:
case IL2CPP_TYPE_U4:
case IL2CPP_TYPE_I8:
case IL2CPP_TYPE_U8:
case IL2CPP_TYPE_R4:
case IL2CPP_TYPE_R8:
case IL2CPP_TYPE_OBJECT:
case IL2CPP_TYPE_STRING:
return true;
default:
break;
}
Il2CppClass* klass = Class::FromIl2CppType(type);
if (klass == il2cpp_defaults.system_guid_class)
return true;
outCachedNonPrimitiveClass = klass;
return false;
}
static Il2CppWindowsRuntimeTypeKind GetWindowsRuntimeTypeKind(const Il2CppType* type, Il2CppClass*& outCachedNonPrimitiveClass)
{
if (type == NULL)
return kTypeKindCustom;
switch (type->type)
{
case IL2CPP_TYPE_BOOLEAN:
case IL2CPP_TYPE_CHAR:
case IL2CPP_TYPE_U1:
case IL2CPP_TYPE_I2:
case IL2CPP_TYPE_U2:
case IL2CPP_TYPE_I4:
case IL2CPP_TYPE_U4:
case IL2CPP_TYPE_I8:
case IL2CPP_TYPE_U8:
case IL2CPP_TYPE_R4:
case IL2CPP_TYPE_R8:
case IL2CPP_TYPE_OBJECT:
case IL2CPP_TYPE_STRING:
return kTypeKindPrimitive;
default:
break;
}
Il2CppClass* klass = Class::FromIl2CppType(type);
if (klass == il2cpp_defaults.system_guid_class)
return kTypeKindPrimitive;
outCachedNonPrimitiveClass = klass;
if (klass->rank > 0)
{
Il2CppClass* cachedElementClass;
if (GetWindowsRuntimeTypeKind(&klass->element_class->byval_arg, cachedElementClass) != kTypeKindCustom)
return kTypeKindMetadata;
}
else
{
const char* windowsRuntimeTypeName = MetadataCache::GetWindowsRuntimeClassName(klass);
if (windowsRuntimeTypeName != NULL)
return kTypeKindMetadata;
if (strcmp(klass->image->name, "WindowsRuntimeMetadata") == 0)
{
Il2CppGenericClass* genericClass = klass->generic_class;
if (genericClass == NULL)
return kTypeKindMetadata;
const Il2CppGenericInst* classInst = genericClass->context.class_inst;
IL2CPP_ASSERT(classInst != NULL);
uint32_t genericArgumentCount = classInst->type_argc;
for (uint32_t i = 0; i < genericArgumentCount; i++)
{
Il2CppClass* cachedGenericArgumentClass;
if (GetWindowsRuntimeTypeKind(classInst->type_argv[i], cachedGenericArgumentClass) == kTypeKindCustom)
return kTypeKindCustom;
}
return kTypeKindMetadata;
}
}
return kTypeKindCustom;
}
static utils::StringView<Il2CppNativeChar> GetWindowsRuntimePrimitiveTypeName(const Il2CppType* type)
{
switch (type->type)
{
case IL2CPP_TYPE_BOOLEAN:
return IL2CPP_NATIVE_STRING("Boolean");
case IL2CPP_TYPE_CHAR:
return IL2CPP_NATIVE_STRING("Char16");
case IL2CPP_TYPE_U1:
return IL2CPP_NATIVE_STRING("UInt8");
case IL2CPP_TYPE_I2:
return IL2CPP_NATIVE_STRING("Int16");
case IL2CPP_TYPE_U2:
return IL2CPP_NATIVE_STRING("UInt16");
case IL2CPP_TYPE_I4:
return IL2CPP_NATIVE_STRING("Int32");
case IL2CPP_TYPE_U4:
return IL2CPP_NATIVE_STRING("UInt32");
case IL2CPP_TYPE_I8:
return IL2CPP_NATIVE_STRING("Int64");
case IL2CPP_TYPE_U8:
return IL2CPP_NATIVE_STRING("UInt64");
case IL2CPP_TYPE_R4:
return IL2CPP_NATIVE_STRING("Single");
case IL2CPP_TYPE_R8:
return IL2CPP_NATIVE_STRING("Double");
case IL2CPP_TYPE_OBJECT:
return IL2CPP_NATIVE_STRING("Object");
case IL2CPP_TYPE_STRING:
return IL2CPP_NATIVE_STRING("String");
case IL2CPP_TYPE_VALUETYPE:
return IL2CPP_NATIVE_STRING("Guid");
default:
IL2CPP_UNREACHABLE;
return IL2CPP_NATIVE_STRING("");
}
}
// This is code duplication... but there isn't a better name to achieve good performance for both primitive and metadata types otherwise
static utils::StringView<char> GetWindowsRuntimePrimitiveTypeNameUtf8(const Il2CppType* type)
{
switch (type->type)
{
case IL2CPP_TYPE_BOOLEAN:
return "Boolean";
case IL2CPP_TYPE_CHAR:
return "Char16";
case IL2CPP_TYPE_U1:
return "UInt8";
case IL2CPP_TYPE_I2:
return "Int16";
case IL2CPP_TYPE_U2:
return "UInt16";
case IL2CPP_TYPE_I4:
return "Int32";
case IL2CPP_TYPE_U4:
return "UInt32";
case IL2CPP_TYPE_I8:
return "Int64";
case IL2CPP_TYPE_U8:
return "UInt64";
case IL2CPP_TYPE_R4:
return "Single";
case IL2CPP_TYPE_R8:
return "Double";
case IL2CPP_TYPE_OBJECT:
return "Object";
case IL2CPP_TYPE_STRING:
return "String";
case IL2CPP_TYPE_VALUETYPE:
return "Guid";
default:
IL2CPP_UNREACHABLE;
return "";
}
}
static std::string GetWindowsRuntimeTypeNameFromWinmdReference(Il2CppClass* klass);
static std::string GetWindowsRuntimeMetadataTypeNameUtf8(Il2CppClass* klass)
{
if (klass->rank > 0)
{
std::string typeName, elementMetadataTypeName;
const Il2CppType* elementType = &klass->element_class->byval_arg;
Il2CppClass* elementClass = NULL;
bool elementIsPrimitive = IsWindowsRuntimePrimitiveType(elementType, elementClass);
utils::StringView<char> elementTypeName(utils::StringView<char>::Empty());
// Optimization: time spent in GetWindowsRuntimeMetadataTypeName is dominated by string allocations,
// so try to reserve needed space on a string in advance.
if (elementIsPrimitive)
{
elementTypeName = GetWindowsRuntimePrimitiveTypeNameUtf8(elementType);
}
else
{
elementMetadataTypeName = GetWindowsRuntimeMetadataTypeNameUtf8(elementClass);
elementTypeName = STRING_TO_STRINGVIEW(elementMetadataTypeName);
}
size_t spaceRequired = IL2CPP_ARRAY_SIZE(kArrayTypePrefixUtf8) + elementTypeName.Length() + 1 /* '>' */ - 1 /* minus null terminator from IL2CPP_ARRAY_SIZE */;
typeName.reserve(spaceRequired);
typeName.append(kArrayTypePrefixUtf8);
typeName.append(elementTypeName.Str(), elementTypeName.Length());
typeName.push_back('>');
return typeName;
}
const char* windowsRuntimeTypeName = MetadataCache::GetWindowsRuntimeClassName(klass);
if (windowsRuntimeTypeName != NULL)
return windowsRuntimeTypeName;
return GetWindowsRuntimeTypeNameFromWinmdReference(klass);
}
static std::string GetWindowsRuntimeTypeNameFromWinmdReference(Il2CppClass* klass)
{
IL2CPP_ASSERT(strcmp(klass->image->name, "WindowsRuntimeMetadata") == 0 && "Windows Runtime type kind was Metadata but it did not come from a Windows Runtime component.");
std::string typeName;
size_t namespaceLength = strlen(klass->namespaze);
size_t nameLength = strlen(klass->name);
typeName.reserve(namespaceLength + 1 + nameLength);
typeName.append(klass->namespaze, namespaceLength);
typeName.push_back('.');
typeName.append(klass->name, nameLength);
Il2CppGenericClass* genericClass = klass->generic_class;
if (genericClass == NULL)
return typeName;
const Il2CppGenericInst* classInst = genericClass->context.class_inst;
IL2CPP_ASSERT(classInst != NULL);
typeName += '<';
uint32_t genericArgumentCount = classInst->type_argc;
for (uint32_t i = 0; i < genericArgumentCount; i++)
{
if (i != 0)
typeName += ',';
const Il2CppType* genericArgumentType = classInst->type_argv[i];
Il2CppClass* genericArgumentClass = NULL;
if (IsWindowsRuntimePrimitiveType(genericArgumentType, genericArgumentClass))
{
utils::StringView<char> primitiveTypeName = GetWindowsRuntimePrimitiveTypeNameUtf8(genericArgumentType);
typeName.append(primitiveTypeName.Str(), primitiveTypeName.Length());
}
else
{
// Windows Runtime metadata types can't have generic arguments of Custom type, thus the argument is metadata type too
typeName += GetWindowsRuntimeMetadataTypeNameUtf8(genericArgumentClass);
}
}
typeName += '>';
return typeName;
}
static Il2CppHString GetWindowsRuntimeMetadataTypeName(Il2CppClass* klass)
{
// Optimization: for type arrays we can construct native string in place and avoid extra UTF8 -> UTF16 conversion
// This makes type name retrieval 4 times faster!
if (klass->rank > 0)
{
const Il2CppType* elementType = &klass->element_class->byval_arg;
Il2CppClass* elementClass = NULL;
utils::StringView<Il2CppNativeChar> elementTypeName(utils::StringView<Il2CppNativeChar>::Empty());
Il2CppHString elementMetadataTypeName = NULL;
bool isElementTypePrimitive = IsWindowsRuntimePrimitiveType(elementType, elementClass);
if (isElementTypePrimitive)
{
elementTypeName = GetWindowsRuntimePrimitiveTypeName(elementType);
}
else
{
elementMetadataTypeName = GetWindowsRuntimeMetadataTypeName(elementClass);
uint32_t elementTypeNameLength;
auto elementMetadataTypeNamePtr = os::WindowsRuntime::GetNativeHStringBuffer(elementMetadataTypeName, &elementTypeNameLength);
vm::Exception::RaiseIfError(elementMetadataTypeNamePtr.GetError());
elementTypeName = utils::StringView<Il2CppNativeChar>(elementMetadataTypeNamePtr.Get(), elementTypeNameLength);
}
size_t offsetInChars = 0;
size_t spaceRequired = IL2CPP_ARRAY_SIZE(kArrayTypePrefix) + elementTypeName.Length() + 1 /* '>' */ - 1 /* minus null terminator from IL2CPP_ARRAY_SIZE */;
Il2CppNativeChar* buffer;
void* hstringBufferHandle = WindowsRuntime::PreallocateHStringBuffer(static_cast<uint32_t>(spaceRequired), &buffer);
memcpy(buffer, kArrayTypePrefix, sizeof(kArrayTypePrefix) - sizeof(Il2CppNativeChar));
offsetInChars += IL2CPP_ARRAY_SIZE(kArrayTypePrefix) - 1;
memcpy(buffer + offsetInChars, elementTypeName.Str(), elementTypeName.Length() * sizeof(Il2CppNativeChar));
offsetInChars += elementTypeName.Length();
buffer[offsetInChars] = static_cast<Il2CppNativeChar>('>');
if (!isElementTypePrimitive)
WindowsRuntime::DeleteHString(elementMetadataTypeName);
return WindowsRuntime::PromoteHStringBuffer(hstringBufferHandle);
}
// Note: don't put 'windowsRuntimeTypeName' into an std::string to save an allocation
const char* windowsRuntimeTypeName = MetadataCache::GetWindowsRuntimeClassName(klass);
if (windowsRuntimeTypeName != NULL)
{
Il2CppNativeString typeName = utils::StringUtils::Utf8ToNativeString(windowsRuntimeTypeName);
return WindowsRuntime::CreateHString(STRING_TO_STRINGVIEW(typeName));
}
std::string typeNameUtf8 = GetWindowsRuntimeTypeNameFromWinmdReference(klass);
Il2CppNativeString typeName = utils::StringUtils::Utf8ToNativeString(typeNameUtf8);
return WindowsRuntime::CreateHString(STRING_TO_STRINGVIEW(typeName));
}
static Il2CppHString GetWindowsRuntimeCustomTypeName(const Il2CppType* type)
{
std::string typeNameUtf8 = Type::GetName(type, IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED);
Il2CppNativeString typeName = utils::StringUtils::Utf8ToNativeString(typeNameUtf8);
return WindowsRuntime::CreateHString(STRING_TO_STRINGVIEW(typeName));
}
void WindowsRuntime::MarshalTypeToNative(const Il2CppType* type, Il2CppWindowsRuntimeTypeName& nativeType)
{
if (type == NULL)
{
nativeType.typeKind = kTypeKindCustom;
nativeType.typeName = NULL;
return;
}
Il2CppClass* cachedClass = NULL;
nativeType.typeKind = GetWindowsRuntimeTypeKind(type, cachedClass);
switch (nativeType.typeKind)
{
case kTypeKindPrimitive:
nativeType.typeName = CreateHString(GetWindowsRuntimePrimitiveTypeName(type));
break;
case kTypeKindMetadata:
nativeType.typeName = GetWindowsRuntimeMetadataTypeName(cachedClass);
break;
case kTypeKindCustom:
nativeType.typeName = GetWindowsRuntimeCustomTypeName(type);
break;
default:
IL2CPP_UNREACHABLE;
}
}
static REAL_NORETURN IL2CPP_NO_INLINE void ThrowUnexpectedTypeKindException()
{
const char kMessage[] = "Unexpected TypeKind when marshaling Windows.Foundation.TypeName. ";
Il2CppException* exception = Exception::GetArgumentException("", kMessage);
Exception::Raise(exception);
IL2CPP_UNREACHABLE;
}
static REAL_NORETURN IL2CPP_NO_INLINE void ThrowWindowsRuntimeTypeNotFoundException(utils::StringView<Il2CppNativeChar> typeName)
{
std::string typeNameUtf8 = utils::StringUtils::NativeStringToUtf8(typeName.Str(), static_cast<uint32_t>(typeName.Length()));
Il2CppException* typeLoadException = Exception::GetTypeLoadExceptionForWindowsRuntimeType(utils::StringView<char>::Empty(), STRING_TO_STRINGVIEW(typeNameUtf8));
Exception::Raise(typeLoadException);
IL2CPP_UNREACHABLE;
}
static Il2CppClass* GetClassFromPrimitiveTypeName(utils::StringView<Il2CppNativeChar> typeName, bool throwOnFailure);
static Il2CppClass* GetClassFromMetadataTypeName(utils::StringView<Il2CppNativeChar> typeName, bool throwOnFailure);
static inline Il2CppClass* GetClassFromPrimitiveOrMetadataTypeName(utils::StringView<Il2CppNativeChar> typeName, bool throwOnFailure)
{
// Try finding type as primitive type first
// If that fails, try finding it as metadata type
Il2CppClass* klass = GetClassFromPrimitiveTypeName(typeName, throwOnFailure);
if (klass != NULL)
return klass;
return GetClassFromMetadataTypeName(typeName, throwOnFailure);
}
static Il2CppClass* GetClassFromPrimitiveTypeName(utils::StringView<Il2CppNativeChar> typeName, bool throwOnFailure)
{
uint32_t characterSum = 0;
for (uint32_t i = 0; i < typeName.Length(); i++)
characterSum += typeName[i];
// Nothing like an (almost) perfect hash function. Thanks for the idea, @andrei!
switch (characterSum)
{
case 393:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Guid")))
return il2cpp_defaults.system_guid_class;
break;
case 400:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Int32")))
return il2cpp_defaults.int32_class;
break;
case 402:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Int16")))
return il2cpp_defaults.int16_class;
break;
case 405:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Int64")))
return il2cpp_defaults.int64_class;
break;
case 440:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("UInt8")))
return il2cpp_defaults.byte_class;
break;
case 485:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Char16")))
return il2cpp_defaults.char_class;
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("UInt32")))
return il2cpp_defaults.uint32_class;
break;
case 487:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("UInt16")))
return il2cpp_defaults.uint16_class;
break;
case 490:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("UInt64")))
return il2cpp_defaults.uint64_class;
break;
case 599:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Object")))
return il2cpp_defaults.object_class;
break;
case 603:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Double")))
return il2cpp_defaults.double_class;
break;
case 610:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Single")))
return il2cpp_defaults.single_class;
break;
case 631:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("String")))
return il2cpp_defaults.string_class;
break;
case 704:
if (utils::StringUtils::Equals(typeName, IL2CPP_NATIVE_STRING("Boolean")))
return il2cpp_defaults.boolean_class;
break;
}
if (throwOnFailure)
{
// Is this actually a metadata type? If so, throw unexpected type kind exception
if (GetClassFromMetadataTypeName(typeName, false) != NULL)
ThrowUnexpectedTypeKindException();
ThrowWindowsRuntimeTypeNotFoundException(typeName);
}
return NULL;
}
static Il2CppClass* GetGenericInstanceClassFromMetadataTypeName(utils::StringView<Il2CppNativeChar> typeName)
{
IL2CPP_ASSERT(typeName[typeName.Length() - 1] == '>');
size_t backtickIndex = typeName.Find('`');
if (backtickIndex == utils::StringView<Il2CppNativeChar>::NPos())
return NULL;
size_t genericArgumentStartIndex = typeName.Find('<', backtickIndex + 1);
if (genericArgumentStartIndex == utils::StringView<Il2CppNativeChar>::NPos())
return NULL;
int genericArgumentCount;
utils::StringView<Il2CppNativeChar> genericArgumentCountStr = typeName.SubStr(backtickIndex + 1, genericArgumentStartIndex - backtickIndex - 1);
if (!genericArgumentCountStr.TryParseAsInt(genericArgumentCount) || genericArgumentCount < 1)
return NULL;
utils::StringView<Il2CppNativeChar> typeDefinitionName = typeName.SubStr(0, genericArgumentStartIndex);
Il2CppClass* classDefinition = GetClassFromMetadataTypeName(typeDefinitionName, false);
if (classDefinition == NULL || !classDefinition->is_generic)
return NULL;
const Il2CppType** genericArguments = (const Il2CppType**)alloca(genericArgumentCount * sizeof(const Il2CppType*));
int genericDepth = 0;
int genericArgumentsAdded = 0;
const Il2CppNativeChar* genericArgumentsPtr = typeName.Str() + genericArgumentStartIndex + 1;
const Il2CppNativeChar* currentGenericArgumentStart = genericArgumentsPtr;
const Il2CppNativeChar* genericArgumentsEnd = typeName.Str() + typeName.Length() - 1;
for (; genericArgumentsPtr <= genericArgumentsEnd; genericArgumentsPtr++)
{
Il2CppNativeChar currentChar = *genericArgumentsPtr;
switch (currentChar)
{
case '<':
genericDepth++;
break;
case '>':
if (genericArgumentsPtr < genericArgumentsEnd)
{
genericDepth--;
break;
}
// fallthrough
case ',':
{
if (genericDepth == 0)
{
il2cpp::utils::StringView<Il2CppNativeChar> genericArgumentTypeName(currentGenericArgumentStart, genericArgumentsPtr - currentGenericArgumentStart);
Il2CppClass* genericArgumentClass = GetClassFromPrimitiveOrMetadataTypeName(genericArgumentTypeName, false);
if (genericArgumentClass == NULL)
return NULL;
genericArguments[genericArgumentsAdded] = &genericArgumentClass->byval_arg;
currentGenericArgumentStart = genericArgumentsPtr + 1;
genericArgumentsAdded++;
}
}
}
}
if (genericArgumentsAdded != genericArgumentCount)
return NULL;
const Il2CppGenericInst* genericInst = MetadataCache::GetGenericInst(genericArguments, genericArgumentCount);
Il2CppGenericClass* genericClass = metadata::GenericMetadata::GetGenericClass(classDefinition, genericInst);
return GenericClass::GetClass(genericClass);
}
static Il2CppClass* GetClassFromMetadataTypeName(utils::StringView<Il2CppNativeChar> typeName, bool throwOnFailure)
{
// Does this type involve generics?
if (typeName[typeName.Length() - 1] == '>')
{
// Is it an array/boxed value?
if (utils::StringUtils::StartsWith(typeName, kArrayTypeOrIReferencePrefix))
{
if (utils::StringUtils::StartsWith(typeName.SubStr(IL2CPP_ARRAY_SIZE(kArrayTypeOrIReferencePrefix) - 1), kArrayTypePostprefix))
{
// We have an array
utils::StringView<Il2CppNativeChar> elementTypeName = typeName.SubStr(IL2CPP_ARRAY_SIZE(kArrayTypePrefix) - 1, typeName.Length() - IL2CPP_ARRAY_SIZE(kArrayTypePrefix));
Il2CppClass* elementClass = GetClassFromPrimitiveOrMetadataTypeName(elementTypeName, false);
if (elementClass != NULL)
return Class::GetArrayClass(elementClass, 1);
}
else if (utils::StringUtils::StartsWith(typeName.SubStr(IL2CPP_ARRAY_SIZE(kArrayTypeOrIReferencePrefix) - 1), kIReferencePostprefix))
{
// We have a boxed value
utils::StringView<Il2CppNativeChar> boxedTypeName = typeName.SubStr(IL2CPP_ARRAY_SIZE(kIReferencePrefix) - 1, typeName.Length() - IL2CPP_ARRAY_SIZE(kIReferencePrefix));
Il2CppClass* boxedClass = GetClassFromPrimitiveOrMetadataTypeName(boxedTypeName, false);
if (boxedClass != NULL)
{
const Il2CppType* boxedType = &boxedClass->byval_arg;
const Il2CppGenericInst* genericInst = MetadataCache::GetGenericInst(&boxedType, 1);
Il2CppGenericClass* genericClass = metadata::GenericMetadata::GetGenericClass(il2cpp_defaults.generic_nullable_class, genericInst);
return GenericClass::GetClass(genericClass);
}
}
}
// This could be a generic type
Il2CppClass* klass = GetGenericInstanceClassFromMetadataTypeName(typeName);
if (klass != NULL)
return klass;
}
// It's not an generic array, or boxed type. Look in Windows Runtime class type map
const std::string typeNameUtf8 = utils::StringUtils::NativeStringToUtf8(typeName.Str(), static_cast<uint32_t>(typeName.Length()));
Il2CppClass* windowsRuntimeClass = MetadataCache::GetWindowsRuntimeClass(typeNameUtf8.c_str());
if (windowsRuntimeClass != NULL)
return windowsRuntimeClass;
// We don't have it in Windows Runtime class type map. Look in WindowsRuntimeMetadata assembly
size_t lastDotIndex = typeNameUtf8.rfind('.');
if (lastDotIndex != std::string::npos)
{
const std::string namespaze = typeNameUtf8.substr(0, lastDotIndex);
const char* name = typeNameUtf8.c_str() + lastDotIndex + 1;
const Il2CppAssembly* windowsRuntimeMetadataAssembly = Assembly::Load("WindowsRuntimeMetadata");
if (windowsRuntimeMetadataAssembly != NULL)
{
Il2CppClass* windowsRuntimeClass = Image::ClassFromName(windowsRuntimeMetadataAssembly->image, namespaze.c_str(), name);
if (windowsRuntimeClass != NULL)
return windowsRuntimeClass;
}
}
if (throwOnFailure)
{
// We couldn't find metadata type with given name, so we must now throw an exception.
// Here's the catch: if a type name is actually a primitive type name, we need to
// throw a special saying that the type kind was unexpected. Otherwise, we need to
// throw the same exception as when we cannot find a primitive type.
if (GetClassFromPrimitiveTypeName(typeName, false) != NULL)
ThrowUnexpectedTypeKindException();
// We want to start the generic part of the name from the exception message
if (typeName[typeName.Length() - 1] == '>')
{
size_t genericArgumentStart = typeName.Find('<');
if (genericArgumentStart != utils::StringView<Il2CppNativeChar>::NPos())
typeName = typeName.SubStr(0, genericArgumentStart);
}
ThrowWindowsRuntimeTypeNotFoundException(typeName);
}
return NULL;
}
static const Il2CppType* GetTypeFromCustomTypeName(utils::StringView<Il2CppNativeChar> typeName)
{
const std::string str = utils::StringUtils::NativeStringToUtf8(typeName.Str(), static_cast<uint32_t>(typeName.Length()));
TypeNameParseInfo info;
TypeNameParser parser(str, info, false);
if (!parser.Parse())
{
utils::StringView<char>
name(utils::StringView<char>::Empty()),
assemblyName(utils::StringView<char>::Empty());
size_t commaIndex = str.find_last_of(',');
if (commaIndex != std::string::npos)
{
name = utils::StringView<char>(str.c_str(), commaIndex);
while (commaIndex < str.length() && (str[commaIndex] == ' ' || str[commaIndex] == '\t'))
commaIndex++;
if (commaIndex < str.length())
assemblyName = utils::StringView<char>(str.c_str() + commaIndex + 1, str.length() - commaIndex - 1);
}
else
{
name = STRING_TO_STRINGVIEW(str);
}
// Splitting name and namespace is pretty complicated, and they're going to be mashed up together in
// the type load exception message anyway. Let's not bother.
Exception::Raise(Exception::GetTypeLoadException(utils::StringView<char>::Empty(), name, assemblyName));
}
return Class::il2cpp_type_from_type_info(info, static_cast<TypeSearchFlags>(kTypeSearchFlagThrowOnError | kTypeSearchFlagDontUseExecutingImage));
}
const Il2CppType* WindowsRuntime::MarshalTypeFromNative(Il2CppWindowsRuntimeTypeName& nativeType)
{
if (nativeType.typeName == NULL)
return NULL;
uint32_t typeNameLength;
auto typeNamePtr = os::WindowsRuntime::GetNativeHStringBuffer(nativeType.typeName, &typeNameLength);
vm::Exception::RaiseIfError(typeNamePtr.GetError());
utils::StringView<Il2CppNativeChar> typeNameView(typeNamePtr.Get(), typeNameLength);
switch (nativeType.typeKind)
{
case kTypeKindPrimitive:
return &GetClassFromPrimitiveTypeName(typeNameView, true)->byval_arg;
case kTypeKindMetadata:
return &GetClassFromMetadataTypeName(typeNameView, true)->byval_arg;
case kTypeKindCustom:
return GetTypeFromCustomTypeName(typeNameView);
default:
ThrowUnexpectedTypeKindException();
}
}
}
}