Refactor TypeInfo constructors

This patch replaces Il2CppModel.resolveTypeReference by a static
TypeInfo constructor, and simultaneously refactors the TypeInfo
constructors to eliminate duplication between resolveTypeReference and
the original constructors. This will make future refactoring much
easier.
This commit is contained in:
Robert Xiao
2020-04-10 02:16:25 -07:00
committed by Katy
parent dfabe9235d
commit 7797ac2506
2 changed files with 101 additions and 159 deletions

View File

@@ -74,7 +74,7 @@ namespace Il2CppInspector.Reflection
// Note that you can't resolve any TypeRefs until all the TypeDefs have been processed
for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Count; typeRefIndex++) {
var typeRef = Package.TypeReferences[typeRefIndex];
var referencedType = resolveTypeReference(typeRef);
var referencedType = TypeInfo.FromTypeReference(this, typeRef);
TypesByReferenceIndex[typeRefIndex] = referencedType;
}
@@ -86,8 +86,11 @@ namespace Il2CppInspector.Reflection
// Concrete instance of a generic class
// If the class index is not specified, we will later create a generic method in a non-generic class
if (spec.classIndexIndex != -1) {
if (!TypesByMethodSpecClassIndex.ContainsKey(spec.classIndexIndex))
TypesByMethodSpecClassIndex.Add(spec.classIndexIndex, new TypeInfo(this, spec));
if (!TypesByMethodSpecClassIndex.ContainsKey(spec.classIndexIndex)) {
var genericTypeDefinition = MethodsByDefinitionIndex[spec.methodDefinitionIndex].DeclaringType;
var genericInstance = Package.GenericInstances[spec.classIndexIndex];
TypesByMethodSpecClassIndex.Add(spec.classIndexIndex, new TypeInfo(genericTypeDefinition, genericInstance));
}
declaringType = TypesByMethodSpecClassIndex[spec.classIndexIndex];
}
@@ -147,48 +150,6 @@ namespace Il2CppInspector.Reflection
return genericTypeArguments.Select(a => GetTypeFromVirtualAddress((ulong) a)).ToList();
}
private TypeInfo resolveTypeReference(Il2CppType typeRef) {
TypeInfo underlyingType;
switch (typeRef.type) {
// Classes defined in the metadata (reference to a TypeDef)
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
underlyingType = TypesByDefinitionIndex[typeRef.datapoint]; // klassIndex
break;
// Constructed types
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
// Generic type and generic method parameters
case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
underlyingType = new TypeInfo(this, typeRef);
break;
// Primitive types
default:
underlyingType = getTypeDefinitionFromTypeEnum(typeRef.type);
break;
}
// Create a reference type if necessary
return typeRef.byref ? underlyingType.MakeByRefType() : underlyingType;
}
// Basic primitive types are specified via a flag value
private TypeInfo getTypeDefinitionFromTypeEnum(Il2CppTypeEnum t) {
if ((int) t >= Il2CppConstants.FullNameTypeString.Count)
return null;
var fqn = Il2CppConstants.FullNameTypeString[(int) t];
return TypesByFullName[fqn];
}
// Get a TypeRef by its virtual address
// These are always nested types from references within another TypeRef
public TypeInfo GetTypeFromVirtualAddress(ulong ptr) {
@@ -198,7 +159,7 @@ namespace Il2CppInspector.Reflection
return TypesByReferenceIndex[typeRefIndex];
var type = Package.TypeReferences[typeRefIndex];
var referencedType = resolveTypeReference(type);
var referencedType = TypeInfo.FromTypeReference(this, type);
TypesByReferenceIndex[typeRefIndex] = referencedType;
return referencedType;

View File

@@ -597,121 +597,85 @@ namespace Il2CppInspector.Reflection {
// Initialize type from type reference (TypeRef)
// Much of the following is adapted from il2cpp::vm::Class::FromIl2CppType
public TypeInfo(Il2CppModel model, Il2CppType pType) {
internal static TypeInfo FromTypeReference(Il2CppModel model, Il2CppType typeRef) {
var image = model.Package.BinaryImage;
TypeInfo underlyingType;
// Open and closed generic types
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) {
switch (typeRef.type) {
// Classes defined in the metadata (reference to a TypeDef)
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
underlyingType = model.TypesByDefinitionIndex[typeRef.datapoint]; // klassIndex
break;
// Constructed types
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
// TODO: Replace with array load from Il2CppMetadataRegistration.genericClasses
var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint); // Il2CppGenericClass *
var generic = image.ReadMappedObject<Il2CppGenericClass>(typeRef.datapoint); // Il2CppGenericClass *
// We have seen one test case where the TypeRef can point to no generic instance
// This is going to leave the TypeInfo in an undefined state
if (generic.typeDefinitionIndex == 0x0000_0000_ffff_ffff)
return;
return null;
var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex];
Assembly = genericTypeDef.Assembly;
Namespace = genericTypeDef.Namespace;
Name = genericTypeDef.BaseName;
Attributes |= TypeAttributes.Class;
// Derived type?
if (genericTypeDef.Definition.parentIndex >= 0)
baseTypeReference = genericTypeDef.Definition.parentIndex;
// Nested type?
if (genericTypeDef.Definition.declaringTypeIndex >= 0) {
declaringTypeDefinitionIndex = (int)model.Package.TypeReferences[genericTypeDef.Definition.declaringTypeIndex].datapoint;
MemberType |= MemberTypes.NestedType;
}
IsGenericType = true;
IsGenericParameter = false;
// Get the instantiation
// TODO: Replace with array load from Il2CppMetadataRegistration.genericInsts
var genericInstance = image.ReadMappedObject<Il2CppGenericInst>(generic.context.class_inst);
if (generic.context.method_inst != 0)
throw new InvalidOperationException("Generic method instance cannot be non-null when processing a generic class instance");
underlyingType = new TypeInfo(genericTypeDef, genericInstance);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
var descriptor = image.ReadMappedObject<Il2CppArrayType>(typeRef.datapoint);
var elementType = model.GetTypeFromVirtualAddress(descriptor.etype);
underlyingType = new TypeInfo(elementType, descriptor.rank);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
elementType = model.GetTypeFromVirtualAddress(typeRef.datapoint);
underlyingType = new TypeInfo(elementType, 1);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
elementType = model.GetTypeFromVirtualAddress(typeRef.datapoint);
underlyingType = new TypeInfo(elementType, isPointer: true);
break;
// Find all the type parameters (both unresolved and concrete)
// This will cause new types to be generated with the VAR and MVAR types below
genericArguments = model.ResolveGenericArguments(genericInstance);
}
// TODO: Set DeclaringType for the two below
// Array with known dimensions and bounds
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) {
var descriptor = image.ReadMappedObject<Il2CppArrayType>(pType.datapoint);
ElementType = model.GetTypeFromVirtualAddress(descriptor.etype);
Assembly = ElementType.Assembly;
Namespace = ElementType.Namespace;
Name = ElementType.Name;
IsArray = true;
arrayRank = descriptor.rank;
}
// Dynamically allocated array or pointer type
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY || pType.type == Il2CppTypeEnum.IL2CPP_TYPE_PTR) {
ElementType = model.GetTypeFromVirtualAddress(pType.datapoint);
Assembly = ElementType.Assembly;
Namespace = ElementType.Namespace;
Name = ElementType.Name;
IsPointer = (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_PTR);
IsArray = !IsPointer;
// Heap arrays always have one dimension
arrayRank = 1;
}
// Generic type parameter
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_VAR || pType.type == Il2CppTypeEnum.IL2CPP_TYPE_MVAR) {
var paramType = model.Package.GenericParameters[pType.datapoint]; // genericParameterIndex
// Generic type and generic method parameters
case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
var paramType = model.Package.GenericParameters[typeRef.datapoint]; // genericParameterIndex
var container = model.Package.GenericContainers[paramType.ownerIndex];
var ownerType = model.TypesByDefinitionIndex[
container.is_method == 1
? model.Package.Methods[container.ownerIndex].declaringType
: container.ownerIndex];
Assembly = ownerType.Assembly;
Namespace = "";
Name = model.Package.Strings[paramType.nameIndex];
Attributes |= TypeAttributes.Class;
// Derived type?
if (ownerType.Definition.parentIndex >= 0)
baseTypeReference = ownerType.Definition.parentIndex;
// Nested type always - sets DeclaringType used below
declaringTypeDefinitionIndex = ownerType.Index;
MemberType |= MemberTypes.NestedType;
// All generic method type parameters have a declared method
if (container.is_method == 1)
DeclaringMethod = model.MethodsByDefinitionIndex[container.ownerIndex];
// Set position in argument list
GenericParameterPosition = paramType.num;
IsGenericParameter = true;
IsGenericType = false;
if(container.is_method == 1) {
var owner = model.MethodsByDefinitionIndex[container.ownerIndex];
underlyingType = new TypeInfo(owner, paramType);
} else {
var owner = model.TypesByDefinitionIndex[container.ownerIndex];
underlyingType = new TypeInfo(owner, paramType);
}
break;
// Primitive types
default:
underlyingType = FromTypeEnum(model, typeRef.type);
break;
}
// Initialize a type from a concrete generic instance (TypeSpec)
public TypeInfo(Il2CppModel model, Il2CppMethodSpec spec) {
var genericTypeDefinition = model.MethodsByDefinitionIndex[spec.methodDefinitionIndex].DeclaringType;
// Create a reference type if necessary
return typeRef.byref ? underlyingType.MakeByRefType() : underlyingType;
}
// Basic primitive types are specified via a flag value
internal static TypeInfo FromTypeEnum(Il2CppModel model, Il2CppTypeEnum t) {
if ((int)t >= Il2CppConstants.FullNameTypeString.Count)
return null;
var fqn = Il2CppConstants.FullNameTypeString[(int)t];
return model.TypesByFullName[fqn];
}
// Initialize a type from a concrete generic instance
public TypeInfo(TypeInfo genericTypeDefinition, Il2CppGenericInst instance) : base(genericTypeDefinition.Assembly) {
// Same visibility attributes as generic type definition
Attributes = genericTypeDefinition.Attributes;
@@ -719,15 +683,24 @@ namespace Il2CppInspector.Reflection {
Index = genericTypeDefinition.Index;
// Same name as generic type definition
Assembly = genericTypeDefinition.Assembly;
Namespace = genericTypeDefinition.Namespace;
Name = genericTypeDefinition.BaseName; // use BaseName to exclude the type parameters so we can supply our own
// Derived type?
if (genericTypeDefinition.Definition.parentIndex >= 0)
baseTypeReference = genericTypeDefinition.Definition.parentIndex;
// Nested type?
if (genericTypeDefinition.Definition.declaringTypeIndex >= 0) {
declaringTypeDefinitionIndex = (int)Assembly.Model.Package.TypeReferences[genericTypeDefinition.Definition.declaringTypeIndex].datapoint;
MemberType |= MemberTypes.NestedType;
}
IsGenericParameter = false;
IsGenericType = true;
// Resolve type arguments
genericArguments = model.ResolveGenericArguments(spec.classIndexIndex);
genericArguments = Assembly.Model.ResolveGenericArguments(instance);
/* TODO: This is a bare definition at the moment. We need to iterate over all the members of genericTypeDefinition
* and replace the matching generic type parameters with our concrete type parameters,
@@ -769,22 +742,30 @@ namespace Il2CppInspector.Reflection {
public TypeInfo(MethodBase declaringMethod, Il2CppGenericParameter param) : this(declaringMethod.DeclaringType, param)
=> DeclaringMethod = declaringMethod;
// Initialize a type that is a reference to the specified type
private TypeInfo(TypeInfo underlyingType) {
ElementType = underlyingType;
IsByRef = true;
// Initialize a type that is an array of the specified type
private TypeInfo(TypeInfo elementType, int rank) : base(elementType.Assembly) {
ElementType = elementType;
IsArray = true;
// No base type or declaring type for reference types
Assembly = ElementType.Assembly;
Definition = ElementType.Definition;
Index = ElementType.Index;
Namespace = ElementType.Namespace;
Name = ElementType.Name;
Attributes = ElementType.Attributes;
arrayRank = rank;
}
public TypeInfo MakeByRefType() => new TypeInfo(this);
// Initialize a type that is a reference or pointer to the specified type
private TypeInfo(TypeInfo underlyingType, bool isPointer) : base(underlyingType.Assembly) {
ElementType = underlyingType;
if(isPointer) {
IsPointer = true;
} else {
IsByRef = true;
}
Namespace = ElementType.Namespace;
Name = ElementType.Name;
}
public TypeInfo MakeByRefType() => new TypeInfo(this, isPointer: false);
// Get all the other types directly referenced by this type (single level depth; no recursion)
public List<TypeInfo> GetAllTypeReferences() {