#include "il2cpp-config.h" #include #include #include #include #include "metadata/Il2CppTypeCompare.h" #include "utils/StringUtils.h" #include "vm/Assembly.h" #include "vm/AssemblyName.h" #include "vm/Class.h" #include "vm/GenericClass.h" #include "vm/GenericContainer.h" #include "vm/MetadataCache.h" #include "vm/Reflection.h" #include "vm/String.h" #include "vm/Type.h" #include "vm-utils/VmStringUtils.h" #include "il2cpp-class-internals.h" #include "il2cpp-object-internals.h" #include "il2cpp-tabledefs.h" #include "vm/Array.h" static char* copy_name(const char* name) { const size_t len = strlen(name); char* cname = new char[len + 1]; strncpy(cname, name, len); cname[len] = '\0'; return cname; } namespace il2cpp { namespace vm { TypeNameParser::TypeNameParser(const std::string &name, TypeNameParseInfo &info, bool is_nested) : _info(info), _is_nested(is_nested), _accept_assembly_name(true), _p(name.begin()), _end(name.end()) { } TypeNameParser::TypeNameParser(std::string::const_iterator &begin, std::string::const_iterator &end, TypeNameParseInfo &info, bool is_nested) : _info(info), _is_nested(is_nested), _accept_assembly_name(true), _p(begin), _end(end) { } bool TypeNameParser::Parse(bool acceptAssemblyName) { _accept_assembly_name = acceptAssemblyName; int32_t arity = 0; InitializeParser(); if (IsEOL()) return false; if (!ParseTypeName(arity)) return false; if (!ParseNestedTypeOptional(arity)) return false; if (!ParseTypeArgumentsOptional(arity)) return false; if (!ParsePointerModifiersOptional()) return false; if (!ParseArrayModifierOptional()) return false; if (!ParseByRefModifiersOptional()) return false; if (!ParseAssemblyNameOptional()) return false; return (_p == _end) || _is_nested; } bool TypeNameParser::ParseAssembly() { InitializeParser(); if (IsEOL()) return false; if (!ParseAssemblyName()) return false; return true; } bool TypeNameParser::ParseTypeName(int32_t &arity) { std::string::const_iterator begin = _p; std::string::const_iterator last_dot = _end; while (true) { ConsumeIdentifier(); if (!CurrentIs('.')) break; last_dot = _p; if (!Next()) return false; // Invalid format } if (CurrentIs('`')) { if (!Next()) return false; // Invalid format if (!ConsumeNumber(arity)) return false; // Invalid format } if (last_dot == _end) { _info._name.assign(begin, _p); } else { _info._namespace.assign(begin, last_dot); _info._name.assign(last_dot + 1, _p); } return true; } bool TypeNameParser::ParseNestedTypeOptional(int32_t &arity) { while (CurrentIs('+')) { if (!Next()) return false; // Invalid format std::string::const_iterator begin = _p; ConsumeIdentifier(); if (CurrentIs('`')) { if (!Next()) return false; // Invalid format int32_t nested_arity = 0; if (!ConsumeNumber(nested_arity)) return false; // Invalid format arity += nested_arity; } _info._nested.push_back(std::string(begin, _p)); } return true; } bool TypeNameParser::ParsePointerModifiersOptional() { if (IsEOL()) return true; while (CurrentIs('*')) { _info._modifiers.push_back(-1); if (!Next(true)) break; } return true; } bool TypeNameParser::ParseByRefModifiersOptional() { if (IsEOL()) return true; if (CurrentIs('&')) { if (std::find(_info._modifiers.begin(), _info._modifiers.end(), 0) != _info._modifiers.end()) return false; // Invalid format, already marked as reference _info._modifiers.push_back(0); Next(true); } return true; } bool TypeNameParser::ParseTypeArgumentsOptional(int32_t &arity) { SkipWhites(); if (IsEOL()) return true; if (!CurrentIs('[')) return true; if (NextWillBe(']', true) || NextWillBe(',', true) || NextWillBe('*', true)) return true; if (!Next(true)) return false; // Invalid format _info._type_arguments.reserve(arity); while (true) { bool acceptAssemblyName = false; if (CurrentIs('[')) { acceptAssemblyName = true; if (!Next(true)) return false; // Invalid format } TypeNameParseInfo info; TypeNameParser parser(_p, _end, info, true); if (!parser.Parse(acceptAssemblyName)) return false; // Invalid format _p = parser._p; _info._type_arguments.push_back(info); SkipWhites(); if (IsEOL()) return false; // Invalid format if (acceptAssemblyName) { if (!CurrentIs(']')) return false; // Invalid format if (!Next(true)) return false; // Invalid format } if (CurrentIs(']')) break; if (CurrentIs(',')) { if (!Next(true)) return false; // Invalid format } else { return false; // Invalid format } } if (_info._type_arguments.size() != arity) return false; // Invalid format Next(true); return true; } bool TypeNameParser::ParseArrayModifierOptional() { SkipWhites(); if (IsEOL()) return true; if (!CurrentIs('[')) return true; if (!NextWillBe(']', true) && !NextWillBe(',', true) && !NextWillBe('*', true)) return true; if (!Next(true)) return false; // Invalid format int32_t rank = 1; while (true) { if (CurrentIs(']')) { Next(true); break; } else if (CurrentIs(',')) { ++rank; if (!Next(true)) return false; // invalid format } else if (CurrentIs('*')) { _info._modifiers.push_back(-2); if (!Next(true)) return false; // invalid format } else { return false; // invalid format } } _info._modifiers.push_back(rank); // This is to support arrays of array return ParseArrayModifierOptional(); } bool TypeNameParser::ParseAssemblyNameOptional() { if (!_accept_assembly_name) return true; if (!CurrentIs(',')) return true; if (!Next()) return false; // Invalid format SkipWhites(); return ParseAssemblyName(); } bool TypeNameParser::ParseAssemblyName() { std::string::const_iterator begin = _p; ConsumeAssemblyIdentifier(); _info._assembly_name.name.assign(begin, _p); SkipWhites(); ParsePropertiesOptional(); return true; } bool TypeNameParser::ParsePropertiesOptional() { while (CurrentIs(',')) { if (!Next(true)) return false; // Invalid format std::string::const_iterator begin = _p; ConsumePropertyIdentifier(); std::string propertyName(begin, _p); utils::VmStringUtils::CaseInsensitiveComparer propertyNameComparer; if (!CurrentIs('=')) return false; if (!Next()) return false; // Invalid format begin = _p; ConsumePropertyValue(); std::string propertyValue(begin, _p); if (propertyNameComparer(propertyName, "version")) { if (!ParseVersion(propertyValue, _info._assembly_name.major, _info._assembly_name.minor, _info._assembly_name.build, _info._assembly_name.revision)) return false; } else if (propertyNameComparer(propertyName.c_str(), "publickey")) { if (!propertyNameComparer(propertyValue, "null")) _info._assembly_name.public_key = propertyValue; } else if (propertyNameComparer(propertyName.c_str(), "publickeytoken")) { if (!propertyNameComparer(propertyValue, "null")) { if ((kPublicKeyTokenLength - 1) != propertyValue.size()) return false; strncpy(_info._assembly_name.public_key_token, propertyValue.c_str(), kPublicKeyTokenLength); } } else if (propertyNameComparer(propertyName.c_str(), "culture")) { _info._assembly_name.culture = propertyValue; } else if (propertyNameComparer(propertyName.c_str(), "contenttype")) { // If content type is WindowsRuntime, coerse assembly name into WindowsRuntimeMetadata if it's not that (otherwise preserve its casing) if (propertyNameComparer(propertyValue, "windowsruntime") && !propertyNameComparer(_info._assembly_name.name.c_str(), "windowsruntimemetadata")) _info._assembly_name.name = "WindowsRuntimeMetadata"; } else { return false; } } return true; } bool TypeNameParser::ParseVersion(const std::string& version, uint16_t& major, uint16_t& minor, uint16_t& build, uint16_t& revision) { size_t start = 0; size_t index = version.find('.'); if (-1 == index) return false; std::string component = version.substr(start, index - start); major = atoi(component.c_str()); start = index + 1; index = version.find('.', start); if (-1 == index) return false; component = version.substr(start, index - start); minor = atoi(component.c_str()); start = index + 1; index = version.find('.', start); if (-1 == index) return false; component = version.substr(start, index - start); build = atoi(component.c_str()); start = index + 1; component = version.substr(start, version.size() - start); revision = atoi(component.c_str()); return true; } bool TypeNameParser::NextWillBe(char v, bool skipWhites) const { if (IsEOL()) return false; int32_t i = 1; if (skipWhites) { if ((*(_p + i) == ' ') || (*(_p + i) == '\t')) ++i; if ((_p + i) >= _end) return false; } return *(_p + i) == v; } bool TypeNameParser::ConsumeNumber(int32_t &value) { if (!isdigit(*_p)) return false; std::string::const_iterator begin = _p; while (isdigit(*_p)) { if (!Next()) break; } value = (int32_t)strtol(&(*begin), NULL, 10); return true; } void TypeNameParser::ConsumeAssemblyIdentifier() { do { switch (*_p) { case ',': case '+': case '&': case '*': case '[': case ']': case '=': case '"': case '`': return; case '\\': Next(); break; } } while (Next()); } void TypeNameParser::ConsumePropertyIdentifier() { do { switch (*_p) { case '=': return; } } while (Next()); } void TypeNameParser::ConsumePropertyValue() { do { switch (*_p) { case ',': case ']': return; } } while (Next()); } void TypeNameParser::ConsumeIdentifier() { do { switch (*_p) { case ',': case '+': case '&': case '*': case '[': case ']': case '.': case '=': case '"': case '`': return; case '\\': Next(); break; } } while (Next()); } void TypeNameParser::InitializeParser() { SkipWhites(); } void TypeNameParser::SkipWhites() { while ((CurrentIs(' ') || CurrentIs('\t')) && !IsEOL()) ++_p; } TypeNameParseInfo::TypeNameParseInfo() { } TypeNameParseInfo::~TypeNameParseInfo() { _type_arguments.clear(); _modifiers.clear(); _nested.clear(); } int Type::GetType(const Il2CppType *type) { return type->type; } void Type::GetNameInternal(std::string &str, const Il2CppType *type, Il2CppTypeNameFormat format, bool is_nested) { switch (type->type) { case IL2CPP_TYPE_ARRAY: { Il2CppClass* arrayClass = Class::FromIl2CppType(type); Il2CppClass* elementClass = Class::GetElementClass(arrayClass); Type::GetNameInternal( str, &elementClass->byval_arg, format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? IL2CPP_TYPE_NAME_FORMAT_FULL_NAME : format, false); str += '['; if (arrayClass->rank == 1) str += '*'; for (int32_t i = 1; i < arrayClass->rank; i++) str += ','; str += ']'; if (type->byref) str += '&'; if (format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { const Il2CppAssembly *ta = elementClass->image->assembly; str += ", " + vm::AssemblyName::AssemblyNameToString(ta->aname); } break; } case IL2CPP_TYPE_SZARRAY: { Il2CppClass* elementClass = Class::FromIl2CppType(type->data.type); Type::GetNameInternal( str, &elementClass->byval_arg, format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? IL2CPP_TYPE_NAME_FORMAT_FULL_NAME : format, false); str += "[]"; if (type->byref) str += '&'; if (format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { const Il2CppAssembly *ta = elementClass->image->assembly; str += ", " + vm::AssemblyName::AssemblyNameToString(ta->aname); } break; } case IL2CPP_TYPE_PTR: { Type::GetNameInternal( str, type->data.type, format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? IL2CPP_TYPE_NAME_FORMAT_FULL_NAME : format, false); str += '*'; if (type->byref) str += '&'; if (format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { const Il2CppAssembly *ta = Class::FromIl2CppType(type->data.type)->image->assembly; str += ", " + vm::AssemblyName::AssemblyNameToString(ta->aname); } break; } case IL2CPP_TYPE_VAR: case IL2CPP_TYPE_MVAR: str += MetadataCache::GetStringFromIndex(Type::GetGenericParameter(type)->nameIndex); if (type->byref) str += '&'; break; default: { Il2CppClass *klass = Class::FromIl2CppType(type); Class::Init(klass); Il2CppClass* declaringType = Class::GetDeclaringType(klass); if (declaringType) { Type::GetNameInternal(str, &declaringType->byval_arg, format, true); str += (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '.' : '+'); } else if (*klass->namespaze) { str += klass->namespaze; str += '.'; } if (format == IL2CPP_TYPE_NAME_FORMAT_IL) { const char *s = strchr(klass->name, '`'); str += (s ? std::string(klass->name, s) : klass->name); } else str += klass->name; if (is_nested) break; if (klass->generic_class) { Il2CppGenericClass *gclass = klass->generic_class; const Il2CppGenericInst *inst = gclass->context.class_inst; Il2CppTypeNameFormat nested_format; nested_format = format == IL2CPP_TYPE_NAME_FORMAT_FULL_NAME ? IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED : format; str += (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '<' : '['); for (uint32_t i = 0; i < inst->type_argc; i++) { const Il2CppType *t = inst->type_argv[i]; if (i) str += ','; if ((nested_format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (t->type != IL2CPP_TYPE_VAR) && (type->type != IL2CPP_TYPE_MVAR)) str += '['; Type::GetNameInternal(str, inst->type_argv[i], nested_format, false); if ((nested_format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (t->type != IL2CPP_TYPE_VAR) && (type->type != IL2CPP_TYPE_MVAR)) str += ']'; } str += (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '>' : ']'); } else if (Class::IsGeneric(klass) && (format != IL2CPP_TYPE_NAME_FORMAT_FULL_NAME) && (format != IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)) { const Il2CppGenericContainer* container = Class::GetGenericContainer(klass); str += (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '<' : '['); for (int32_t i = 0; i < container->type_argc; i++) { if (i) str += ','; str += MetadataCache::GetStringFromIndex(GenericContainer::GetGenericParameter(container, i)->nameIndex); } str += (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '>' : ']'); } if (type->byref) str += '&'; if ((format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (type->type != IL2CPP_TYPE_VAR) && (type->type != IL2CPP_TYPE_MVAR)) { const Il2CppAssembly *ta = klass->image->assembly; str += ", " + vm::AssemblyName::AssemblyNameToString(ta->aname); } break; } } } std::string Type::GetName(const Il2CppType *type, Il2CppTypeNameFormat format) { std::string str; Type::GetNameInternal(str, type, format, false); return str; } enum { //max digits on uint16 is 5(used to convert the number of generic args) + max 3 other slots taken; kNameChunkBufferSize = 8 }; static inline char* flushChunkBuffer(char* buffer, void(*chunkReportFunc)(void*data, void* userData), void* userData) { chunkReportFunc(buffer, userData); memset(buffer, 0x00, kNameChunkBufferSize); return buffer; } void Type::GetNameChunkedRecurseInternal(const Il2CppType *type, Il2CppTypeNameFormat format, bool is_nested, void(*chunkReportFunc)(void*data, void* userData), void* userData) { char buffer[kNameChunkBufferSize + 1]; //null terminate the buffer memset(buffer, 0x00, kNameChunkBufferSize + 1); char* bufferPtr = buffer; char* bufferIter = bufferPtr; switch (type->type) { case IL2CPP_TYPE_ARRAY: { Il2CppClass* arrayClass = Class::FromIl2CppType(type); Il2CppClass* elementClass = Class::GetElementClass(arrayClass); Type::GetNameChunkedRecurseInternal( &elementClass->byval_arg, format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? IL2CPP_TYPE_NAME_FORMAT_FULL_NAME : format, false, chunkReportFunc, userData); *bufferIter++ = '['; if (arrayClass->rank == 1) *bufferIter++ = '*'; for (int32_t i = 1; i < arrayClass->rank; i++) { *bufferIter++ = ','; if (kNameChunkBufferSize - (bufferIter - bufferPtr) < 2) { bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); } } *bufferIter++ = ']'; if (type->byref) *bufferIter++ = '&'; bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); if (format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { const Il2CppAssembly *ta = elementClass->image->assembly; *bufferIter++ = ','; chunkReportFunc(bufferPtr, userData); //change this to call the callback vm::AssemblyName::AssemblyNameReportChunked(ta->aname, chunkReportFunc, userData); } break; } case IL2CPP_TYPE_SZARRAY: { Il2CppClass* elementClass = Class::FromIl2CppType(type->data.type); Type::GetNameChunkedRecurseInternal( &elementClass->byval_arg, format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? IL2CPP_TYPE_NAME_FORMAT_FULL_NAME : format, false, chunkReportFunc, userData); *bufferIter++ = '['; *bufferIter++ = ']'; if (type->byref) *bufferIter++ = '&'; bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); if (format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { const Il2CppAssembly *ta = elementClass->image->assembly; *bufferIter++ = ','; chunkReportFunc(bufferPtr, userData); //change this to call the callback vm::AssemblyName::AssemblyNameReportChunked(ta->aname, chunkReportFunc, userData); } break; } case IL2CPP_TYPE_PTR: { Type::GetNameChunkedRecurseInternal( type->data.type, format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? IL2CPP_TYPE_NAME_FORMAT_FULL_NAME : format, false, chunkReportFunc, userData); *bufferIter++ = '*'; if (type->byref) *bufferIter++ = '&'; bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); if (format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { const Il2CppAssembly *ta = Class::FromIl2CppType(type->data.type)->image->assembly; *bufferIter++ = ','; chunkReportFunc(bufferPtr, userData); //change this to call the callback vm::AssemblyName::AssemblyNameReportChunked(ta->aname, chunkReportFunc, userData); } break; } case IL2CPP_TYPE_VAR: case IL2CPP_TYPE_MVAR: chunkReportFunc(const_cast(MetadataCache::GetStringFromIndex(Type::GetGenericParameter(type)->nameIndex)), userData); if (type->byref) { *bufferIter++ = '&'; chunkReportFunc(bufferPtr, userData); } break; default: { Il2CppClass *klass = Class::FromIl2CppType(type); Class::Init(klass); Il2CppClass* declaringType = Class::GetDeclaringType(klass); if (declaringType) { Type::GetNameChunkedRecurseInternal(&declaringType->byval_arg, format, true, chunkReportFunc, userData); *bufferIter++ = (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '.' : '+'); } else if (*klass->namespaze) { chunkReportFunc(const_cast(klass->namespaze), userData); *bufferIter++ = '.'; } if (format == IL2CPP_TYPE_NAME_FORMAT_IL) { const char *s = strchr(klass->name, '`'); size_t len = s ? s - klass->name : strlen(klass->name); for (size_t i = 0; i < len; ++i) { *bufferIter++ = *(klass->name + i); if (kNameChunkBufferSize - (bufferIter - bufferPtr) == 0) { bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); } } } else chunkReportFunc(const_cast(klass->name), userData); if (bufferPtr != bufferIter) { bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); } if (is_nested) break; if (klass->generic_class) { Il2CppGenericClass *gclass = klass->generic_class; const Il2CppGenericInst *inst = gclass->context.class_inst; Il2CppTypeNameFormat nested_format; nested_format = format == IL2CPP_TYPE_NAME_FORMAT_FULL_NAME ? IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED : format; *bufferIter++ = (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '<' : '['); for (uint32_t i = 0; i < inst->type_argc; i++) { const Il2CppType *t = inst->type_argv[i]; if (i) *bufferIter++ = ','; if ((nested_format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (t->type != IL2CPP_TYPE_VAR) && (type->type != IL2CPP_TYPE_MVAR)) *bufferIter++ = '['; bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); Type::GetNameChunkedRecurseInternal(inst->type_argv[i], nested_format, false, chunkReportFunc, userData); if ((nested_format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (t->type != IL2CPP_TYPE_VAR) && (type->type != IL2CPP_TYPE_MVAR)) *bufferIter++ = ']'; } *bufferIter++ = (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '>' : ']'); } else if (Class::IsGeneric(klass) && (format != IL2CPP_TYPE_NAME_FORMAT_FULL_NAME) && (format != IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)) { const Il2CppGenericContainer* container = Class::GetGenericContainer(klass); *bufferIter++ = (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '<' : '['); for (int32_t i = 0; i < container->type_argc; ++i) { if (i) *bufferIter++ = ','; const char* idxStr = MetadataCache::GetStringFromIndex(GenericContainer::GetGenericParameter(container, i)->nameIndex); size_t len = strlen(idxStr); for (size_t l = 0; l < len; ++l) { *bufferIter++ = *(idxStr + l); if (kNameChunkBufferSize - (bufferIter - bufferPtr) < 2) //make sure there's at least 2 slots empty to //accommodate the worst case scenario until we flush { bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); } } } *bufferIter++ = (format == IL2CPP_TYPE_NAME_FORMAT_IL ? '>' : ']'); } if (type->byref) *bufferIter++ = '&'; bufferIter = flushChunkBuffer(bufferPtr, chunkReportFunc, userData); if ((format == IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (type->type != IL2CPP_TYPE_VAR) && (type->type != IL2CPP_TYPE_MVAR)) { const Il2CppAssembly *ta = klass->image->assembly; *bufferIter++ = ','; chunkReportFunc(bufferPtr, userData); //change this to call the callback vm::AssemblyName::AssemblyNameReportChunked(ta->aname, chunkReportFunc, userData); } break; } } } void Type::GetNameChunkedRecurse(const Il2CppType *type, Il2CppTypeNameFormat format, void(*reportFunc)(void*data, void* userData), void* userData) { GetNameChunkedRecurseInternal(type, format, false, reportFunc, userData); } Il2CppClass* Type::GetClassOrElementClass(const Il2CppType *type) { // This is a weird function to mimic old mono behaviour. // We incorrectly used the analogous mono function in Unity. // Expectations: Return class if type is not an array. Return element type if it is an array. if (type->type == IL2CPP_TYPE_ARRAY) return Class::FromIl2CppType(type->data.array->etype); if (type->type == IL2CPP_TYPE_SZARRAY) return Class::FromIl2CppType(type->data.type); // IL2CPP_TYPE_SZARRAY stores element class in klass return il2cpp::vm::MetadataCache::GetIl2CppClassFromTypeDefinition(type->data.typeHandle); } const Il2CppType* Type::GetUnderlyingType(const Il2CppType *type) { if (type->type == IL2CPP_TYPE_VALUETYPE && MetadataCache::GetIl2CppClassFromTypeDefinition(type->data.typeHandle)->enumtype && !type->byref) return Class::GetEnumBaseType(MetadataCache::GetIl2CppClassFromTypeDefinition(type->data.typeHandle)); if (IsGenericInstance(type)) { Il2CppClass* definition = GenericClass::GetTypeDefinition(type->data.generic_class); if (definition != NULL && definition->enumtype && !type->byref) return Class::GetEnumBaseType(definition); } return type; } bool Type::IsGenericInstance(const Il2CppType* type) { return type->type == IL2CPP_TYPE_GENERICINST; } Il2CppReflectionType* Type::GetDeclaringType(const Il2CppType* type) { Il2CppClass *typeInfo = NULL; if (type->byref) return NULL; if (type->type == IL2CPP_TYPE_VAR || type->type == IL2CPP_TYPE_MVAR) { const Il2CppGenericParameter* genericParameter = GetGenericParameter(type); const Il2CppGenericContainer* container = MetadataCache::GetGenericContainerFromIndex(genericParameter->ownerIndex); typeInfo = GenericContainer::GetDeclaringType(container); } else { typeInfo = Class::GetDeclaringType(Class::FromIl2CppType(type)); } return typeInfo ? Reflection::GetTypeObject(&typeInfo->byval_arg) : NULL; } Il2CppArray* Type::GetGenericArgumentsInternal(Il2CppReflectionType* type, bool runtimeArray) { Il2CppArray *res; Il2CppClass *klass, *pklass; klass = Class::FromIl2CppType(type->type); Il2CppClass *arrType = runtimeArray ? il2cpp_defaults.runtimetype_class : il2cpp_defaults.systemtype_class; if (Class::IsGeneric(klass)) { const Il2CppGenericContainer *container = MetadataCache::GetGenericContainerFromIndex(klass->genericContainerIndex); res = Array::New(arrType, container->type_argc); for (int32_t i = 0; i < container->type_argc; ++i) { pklass = Class::FromGenericParameter(GenericContainer::GetGenericParameter(container, i)); il2cpp_array_setref(res, i, Reflection::GetTypeObject(&pklass->byval_arg)); } } else if (klass->generic_class) { const Il2CppGenericInst *inst = klass->generic_class->context.class_inst; res = Array::New(arrType, inst->type_argc); for (uint32_t i = 0; i < inst->type_argc; ++i) il2cpp_array_setref(res, i, Reflection::GetTypeObject(inst->type_argv[i])); } else { res = Array::New(arrType, 0); } return res; } bool Type::IsEqualToType(const Il2CppType *type, const Il2CppType *otherType) { return ::il2cpp::metadata::Il2CppTypeEqualityComparer::AreEqual(type, otherType); } Il2CppReflectionType* Type::GetTypeFromHandle(intptr_t handle) { const Il2CppType* type = (const Il2CppType*)handle; Il2CppClass *klass = vm::Class::FromIl2CppType(type); return il2cpp::vm::Reflection::GetTypeObject(&klass->byval_arg); } uint32_t Type::GetToken(const Il2CppType *type) { if (IsGenericInstance(type)) return GenericClass::GetTypeDefinition(type->data.generic_class)->token; return GetClass(type)->token; } bool Type::IsReference(const Il2CppType* type) { if (!type) return false; if (type->type == IL2CPP_TYPE_STRING || type->type == IL2CPP_TYPE_SZARRAY || type->type == IL2CPP_TYPE_CLASS || type->type == IL2CPP_TYPE_OBJECT || type->type == IL2CPP_TYPE_ARRAY) return true; if (IsGenericInstance(type) && !GenericClass::IsValueType(type->data.generic_class)) return true; return false; } bool Type::IsStruct(const Il2CppType* type) { if (type->byref) return false; if (type->type == IL2CPP_TYPE_VALUETYPE && !MetadataCache::GetIl2CppClassFromTypeDefinition(type->data.typeHandle)->enumtype) return true; if (type->type == IL2CPP_TYPE_TYPEDBYREF) return true; if (IsGenericInstance(type) && GenericClass::IsValueType(type->data.generic_class) && !GenericClass::IsEnum(type->data.generic_class)) return true; return false; } bool Type::GenericInstIsValuetype(const Il2CppType* type) { IL2CPP_ASSERT(IsGenericInstance(type)); return GenericClass::IsValueType(type->data.generic_class); } bool Type::IsEnum(const Il2CppType *type) { if (type->type != IL2CPP_TYPE_VALUETYPE) return false; Il2CppClass* klass = GetClass(type); return klass->enumtype; } bool Type::IsValueType(const Il2CppType *type) { Il2CppClass* klass = GetClass(type); return klass->valuetype; } bool Type::IsEmptyType(const Il2CppType *type) { return IsGenericInstance(type) && type->data.generic_class->type == nullptr; } bool Type::IsSystemDBNull(const Il2CppType *type) { Il2CppClass* klass = GetClass(type); return (klass->image == il2cpp_defaults.corlib && strcmp(klass->namespaze, "System") == 0 && strcmp(klass->name, "DBNull") == 0); } bool Type::IsSystemDateTime(const Il2CppType *type) { Il2CppClass* klass = GetClass(type); return (klass->image == il2cpp_defaults.corlib && strcmp(klass->namespaze, "System") == 0 && strcmp(klass->name, "DateTime") == 0); } bool Type::IsSystemDecimal(const Il2CppType *type) { Il2CppClass* klass = GetClass(type); return (klass->image == il2cpp_defaults.corlib && strcmp(klass->namespaze, "System") == 0 && strcmp(klass->name, "Decimal") == 0); } Il2CppClass* Type::GetClass(const Il2CppType *type) { IL2CPP_ASSERT(type->type == IL2CPP_TYPE_CLASS || type->type == IL2CPP_TYPE_VALUETYPE); return MetadataCache::GetIl2CppClassFromTypeDefinition(type->data.typeHandle); } const Il2CppGenericParameter* Type::GetGenericParameter(const Il2CppType *type) { IL2CPP_ASSERT(type->type == IL2CPP_TYPE_VAR || type->type == IL2CPP_TYPE_MVAR); return type->data.genericParameterHandle; } const Il2CppType* Type::GetGenericTypeDefintion(const Il2CppType* type) { IL2CPP_ASSERT(IsGenericInstance(type)); return Class::GetType(GenericClass::GetTypeDefinition(type->data.generic_class)); } /** * Type::ConstructDelegate: * @delegate: pointer to an uninitialized delegate object * @target: target object * @addr: pointer to native code * @method: method * * Initialize a delegate and set a specific method, not the one * associated with addr. This is useful when sharing generic code. * In that case addr will most probably not be associated with the * correct instantiation of the method. */ void Type::ConstructDelegate(Il2CppDelegate* delegate, Il2CppObject* target, Il2CppMethodPointer addr, const MethodInfo* method) { #if IL2CPP_TINY IL2CPP_ASSERT(0 && "Type::ConstructDelegatee should not be called with the Tiny profile."); #else IL2CPP_ASSERT(delegate); if (method) delegate->method = method; delegate->method_ptr = addr; if (target != NULL) IL2CPP_OBJECT_SETREF(delegate, target, target); delegate->invoke_impl = method->invoker_method; //TODO:figure out if this is needed at all #endif } Il2CppString* Type::AppendAssemblyNameIfNecessary(Il2CppString* typeName, const char* assemblyName) { if (typeName != NULL) { std::string name = utils::StringUtils::Utf16ToUtf8(utils::StringUtils::GetChars(typeName)); il2cpp::vm::TypeNameParseInfo info; il2cpp::vm::TypeNameParser parser(name, info, false); if (parser.Parse()) { if (info.assembly_name().name.empty()) { std::string assemblyQualifiedName; assemblyQualifiedName = name + ", " + assemblyName; return vm::String::New(assemblyQualifiedName.c_str()); } } } return typeName; } } /* namespace vm */ } /* namespace il2cpp */