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 // Note that you can't resolve any TypeRefs until all the TypeDefs have been processed
for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Count; typeRefIndex++) { for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Count; typeRefIndex++) {
var typeRef = Package.TypeReferences[typeRefIndex]; var typeRef = Package.TypeReferences[typeRefIndex];
var referencedType = resolveTypeReference(typeRef); var referencedType = TypeInfo.FromTypeReference(this, typeRef);
TypesByReferenceIndex[typeRefIndex] = referencedType; TypesByReferenceIndex[typeRefIndex] = referencedType;
} }
@@ -86,8 +86,11 @@ namespace Il2CppInspector.Reflection
// Concrete instance of a generic class // 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 the class index is not specified, we will later create a generic method in a non-generic class
if (spec.classIndexIndex != -1) { if (spec.classIndexIndex != -1) {
if (!TypesByMethodSpecClassIndex.ContainsKey(spec.classIndexIndex)) if (!TypesByMethodSpecClassIndex.ContainsKey(spec.classIndexIndex)) {
TypesByMethodSpecClassIndex.Add(spec.classIndexIndex, new TypeInfo(this, spec)); var genericTypeDefinition = MethodsByDefinitionIndex[spec.methodDefinitionIndex].DeclaringType;
var genericInstance = Package.GenericInstances[spec.classIndexIndex];
TypesByMethodSpecClassIndex.Add(spec.classIndexIndex, new TypeInfo(genericTypeDefinition, genericInstance));
}
declaringType = TypesByMethodSpecClassIndex[spec.classIndexIndex]; declaringType = TypesByMethodSpecClassIndex[spec.classIndexIndex];
} }
@@ -147,48 +150,6 @@ namespace Il2CppInspector.Reflection
return genericTypeArguments.Select(a => GetTypeFromVirtualAddress((ulong) a)).ToList(); 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 // Get a TypeRef by its virtual address
// These are always nested types from references within another TypeRef // These are always nested types from references within another TypeRef
public TypeInfo GetTypeFromVirtualAddress(ulong ptr) { public TypeInfo GetTypeFromVirtualAddress(ulong ptr) {
@@ -198,7 +159,7 @@ namespace Il2CppInspector.Reflection
return TypesByReferenceIndex[typeRefIndex]; return TypesByReferenceIndex[typeRefIndex];
var type = Package.TypeReferences[typeRefIndex]; var type = Package.TypeReferences[typeRefIndex];
var referencedType = resolveTypeReference(type); var referencedType = TypeInfo.FromTypeReference(this, type);
TypesByReferenceIndex[typeRefIndex] = referencedType; TypesByReferenceIndex[typeRefIndex] = referencedType;
return referencedType; return referencedType;

View File

@@ -597,121 +597,85 @@ namespace Il2CppInspector.Reflection {
// Initialize type from type reference (TypeRef) // Initialize type from type reference (TypeRef)
// Much of the following is adapted from il2cpp::vm::Class::FromIl2CppType // 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; var image = model.Package.BinaryImage;
TypeInfo underlyingType;
// Open and closed generic types switch (typeRef.type) {
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { // 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;
// TODO: Replace with array load from Il2CppMetadataRegistration.genericClasses // Constructed types
var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint); // Il2CppGenericClass * case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
// TODO: Replace with array load from Il2CppMetadataRegistration.genericClasses
var generic = image.ReadMappedObject<Il2CppGenericClass>(typeRef.datapoint); // Il2CppGenericClass *
// We have seen one test case where the TypeRef can point to no generic instance // 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 // This is going to leave the TypeInfo in an undefined state
if (generic.typeDefinitionIndex == 0x0000_0000_ffff_ffff) if (generic.typeDefinitionIndex == 0x0000_0000_ffff_ffff)
return; return null;
var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex]; var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex];
Assembly = genericTypeDef.Assembly; // Get the instantiation
Namespace = genericTypeDef.Namespace; // TODO: Replace with array load from Il2CppMetadataRegistration.genericInsts
Name = genericTypeDef.BaseName; var genericInstance = image.ReadMappedObject<Il2CppGenericInst>(generic.context.class_inst);
Attributes |= TypeAttributes.Class;
// Derived type? underlyingType = new TypeInfo(genericTypeDef, genericInstance);
if (genericTypeDef.Definition.parentIndex >= 0) break;
baseTypeReference = genericTypeDef.Definition.parentIndex; 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;
// Nested type? // Generic type and generic method parameters
if (genericTypeDef.Definition.declaringTypeIndex >= 0) { case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
declaringTypeDefinitionIndex = (int)model.Package.TypeReferences[genericTypeDef.Definition.declaringTypeIndex].datapoint; case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
MemberType |= MemberTypes.NestedType; var paramType = model.Package.GenericParameters[typeRef.datapoint]; // genericParameterIndex
} var container = model.Package.GenericContainers[paramType.ownerIndex];
IsGenericType = true; if(container.is_method == 1) {
IsGenericParameter = false; var owner = model.MethodsByDefinitionIndex[container.ownerIndex];
underlyingType = new TypeInfo(owner, paramType);
} else {
var owner = model.TypesByDefinitionIndex[container.ownerIndex];
underlyingType = new TypeInfo(owner, paramType);
}
break;
// Get the instantiation // Primitive types
// TODO: Replace with array load from Il2CppMetadataRegistration.genericInsts default:
var genericInstance = image.ReadMappedObject<Il2CppGenericInst>(generic.context.class_inst); underlyingType = FromTypeEnum(model, typeRef.type);
break;
if (generic.context.method_inst != 0)
throw new InvalidOperationException("Generic method instance cannot be non-null when processing a generic class instance");
// 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 // Create a reference type if necessary
return typeRef.byref ? underlyingType.MakeByRefType() : underlyingType;
// 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
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;
}
} }
// Initialize a type from a concrete generic instance (TypeSpec) // Basic primitive types are specified via a flag value
public TypeInfo(Il2CppModel model, Il2CppMethodSpec spec) { internal static TypeInfo FromTypeEnum(Il2CppModel model, Il2CppTypeEnum t) {
var genericTypeDefinition = model.MethodsByDefinitionIndex[spec.methodDefinitionIndex].DeclaringType; 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 // Same visibility attributes as generic type definition
Attributes = genericTypeDefinition.Attributes; Attributes = genericTypeDefinition.Attributes;
@@ -719,15 +683,24 @@ namespace Il2CppInspector.Reflection {
Index = genericTypeDefinition.Index; Index = genericTypeDefinition.Index;
// Same name as generic type definition // Same name as generic type definition
Assembly = genericTypeDefinition.Assembly;
Namespace = genericTypeDefinition.Namespace; Namespace = genericTypeDefinition.Namespace;
Name = genericTypeDefinition.BaseName; // use BaseName to exclude the type parameters so we can supply our own 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; IsGenericParameter = false;
IsGenericType = true; IsGenericType = true;
// Resolve type arguments // 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 /* 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, * 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) public TypeInfo(MethodBase declaringMethod, Il2CppGenericParameter param) : this(declaringMethod.DeclaringType, param)
=> DeclaringMethod = declaringMethod; => DeclaringMethod = declaringMethod;
// Initialize a type that is a reference to the specified type // Initialize a type that is an array of the specified type
private TypeInfo(TypeInfo underlyingType) { private TypeInfo(TypeInfo elementType, int rank) : base(elementType.Assembly) {
ElementType = underlyingType; ElementType = elementType;
IsByRef = true; IsArray = true;
// No base type or declaring type for reference types
Assembly = ElementType.Assembly;
Definition = ElementType.Definition;
Index = ElementType.Index;
Namespace = ElementType.Namespace; Namespace = ElementType.Namespace;
Name = ElementType.Name; Name = ElementType.Name;
arrayRank = rank;
Attributes = ElementType.Attributes;
} }
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) // Get all the other types directly referenced by this type (single level depth; no recursion)
public List<TypeInfo> GetAllTypeReferences() { public List<TypeInfo> GetAllTypeReferences() {