Model and Output: Dramatically improve handling of byref types

This commit is contained in:
Katy Coe
2019-12-11 09:30:00 +01:00
parent d8befc0a15
commit 5a42afe7f2
4 changed files with 52 additions and 15 deletions

View File

@@ -52,11 +52,14 @@ namespace Il2CppInspector.Reflection
public Assembly GetAssembly(string name) => Assemblies.FirstOrDefault(a => a.ShortName == name); public Assembly GetAssembly(string name) => Assemblies.FirstOrDefault(a => a.ShortName == name);
private TypeInfo getNewTypeUsage(Il2CppType usage, MemberTypes memberType) { private TypeInfo getNewTypeUsage(Il2CppType usage, MemberTypes memberType) {
TypeInfo underlyingType;
switch (usage.type) { switch (usage.type) {
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
// Classes defined in the metadata // Classes defined in the metadata
return TypesByDefinitionIndex[usage.datapoint]; // klassIndex underlyingType = TypesByDefinitionIndex[usage.datapoint]; // klassIndex
break;
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
@@ -65,12 +68,17 @@ namespace Il2CppInspector.Reflection
case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
// Everything that requires special handling // Everything that requires special handling
return new TypeInfo(this, usage, memberType); underlyingType = new TypeInfo(this, usage, memberType);
break;
default: default:
// Primitive types // Primitive types
return GetTypeFromTypeEnum(usage.type); underlyingType = GetTypeFromTypeEnum(usage.type);
break;
} }
// Create a reference type if necessary
return usage.byref? underlyingType.MakeByRefType() : underlyingType;
} }
// Get or generate a type from its IL2CPP binary type usage reference // Get or generate a type from its IL2CPP binary type usage reference

View File

@@ -31,7 +31,8 @@ namespace Il2CppInspector.Reflection
} }
// TODO: Generic arguments (and on ConstructorInfo) // TODO: Generic arguments (and on ConstructorInfo)
public override string ToString() => ReturnType.Name + " " + Name + "(" + string.Join(", ", DeclaredParameters.Select(x => x.ParameterType.Name)) + ")"; public override string ToString() => ReturnType.Name + " " + Name + "(" + string.Join(", ",
DeclaredParameters.Select(x => x.ParameterType.IsByRef? x.ParameterType.Name.TrimEnd('&') + " ByRef" : x.ParameterType.Name)) + ")";
public override string GetSignatureString() => ReturnParameter.GetSignatureString() + " " + Name + GetFullTypeParametersString() public override string GetSignatureString() => ReturnParameter.GetSignatureString() + " " + Name + GetFullTypeParametersString()
+ "(" + string.Join(",", DeclaredParameters.Select(x => x.GetSignatureString())) + ")"; + "(" + string.Join(",", DeclaredParameters.Select(x => x.GetSignatureString())) + ")";

View File

@@ -30,8 +30,6 @@ namespace Il2CppInspector.Reflection
// Default value for the parameter // Default value for the parameter
public object DefaultValue { get; } public object DefaultValue { get; }
public bool IsByRef => !ParameterType.ContainsGenericParameters && paramTypeUsage == ParameterType.Definition.byrefTypeIndex;
public bool IsIn => (Attributes & ParameterAttributes.In) != 0; public bool IsIn => (Attributes & ParameterAttributes.In) != 0;
public bool IsOptional => (Attributes & ParameterAttributes.Optional) != 0; public bool IsOptional => (Attributes & ParameterAttributes.Optional) != 0;
public bool IsOut => (Attributes & ParameterAttributes.Out) != 0; public bool IsOut => (Attributes & ParameterAttributes.Out) != 0;
@@ -96,12 +94,13 @@ namespace Il2CppInspector.Reflection
} }
} }
// ref will be handled as part of the type name
public string GetModifierString() => public string GetModifierString() =>
(IsIn ? "in " : "") (IsIn ? "in " : "")
+ (IsByRef? "ref " : "") + (IsOut ? "out " : "")
+ (IsOut? "out " : ""); + (!IsIn && !IsOut && ParameterType.IsByRef ? "ref " : "");
private string getCSharpSignatureString(Scope scope) => $"{GetModifierString()}{ParameterType.GetScopedCSharpName(scope)}"; private string getCSharpSignatureString(Scope scope) => $"{GetModifierString()}{ParameterType.GetScopedCSharpName(scope, omitRef: true)}";
public string GetSignatureString() => $"{GetModifierString()}{ParameterType.FullName}"; public string GetSignatureString() => $"{GetModifierString()}{ParameterType.FullName}";
public string GetParameterString(Scope usingScope, bool emitPointer = false, bool compileAttributes = false) => IsRetval? null : public string GetParameterString(Scope usingScope, bool emitPointer = false, bool compileAttributes = false) => IsRetval? null :
@@ -112,5 +111,7 @@ namespace Il2CppInspector.Reflection
+ (emitPointer && !(DefaultValue is null)? $" /* Metadata: 0x{(uint) DefaultValueMetadataAddress:X8} */" : "") : ""); + (emitPointer && !(DefaultValue is null)? $" /* Metadata: 0x{(uint) DefaultValueMetadataAddress:X8} */" : "") : "");
public string GetReturnParameterString(Scope scope) => !IsRetval? null : getCSharpSignatureString(scope); public string GetReturnParameterString(Scope scope) => !IsRetval? null : getCSharpSignatureString(scope);
public override string ToString() => ParameterType.Name + " " + Name;
} }
} }

View File

@@ -59,13 +59,16 @@ namespace Il2CppInspector.Reflection {
n = "out " + n; n = "out " + n;
if ((GenericParameterAttributes & GenericParameterAttributes.Contravariant) == GenericParameterAttributes.Contravariant) if ((GenericParameterAttributes & GenericParameterAttributes.Contravariant) == GenericParameterAttributes.Contravariant)
n = "in " + n; n = "in " + n;
if (IsByRef)
n = "ref " + n;
return n + (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsPointer ? "*" : ""); return n + (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsPointer ? "*" : "");
} }
} }
// C# name as it would be written in a type declaration // C# name as it would be written in a type declaration
public string CSharpTypeDeclarationName => public string CSharpTypeDeclarationName =>
(HasElementType? (IsByRef? "ref " : "")
+ (HasElementType?
ElementType.CSharpTypeDeclarationName : ElementType.CSharpTypeDeclarationName :
((GenericParameterAttributes & GenericParameterAttributes.Contravariant) == GenericParameterAttributes.Contravariant? "in " : "") ((GenericParameterAttributes & GenericParameterAttributes.Contravariant) == GenericParameterAttributes.Contravariant? "in " : "")
+ ((GenericParameterAttributes & GenericParameterAttributes.Covariant) == GenericParameterAttributes.Covariant? "out ":"") + ((GenericParameterAttributes & GenericParameterAttributes.Covariant) == GenericParameterAttributes.Covariant? "out ":"")
@@ -144,14 +147,16 @@ namespace Il2CppInspector.Reflection {
// Type name including namespace // Type name including namespace
public string FullName => public string FullName =>
IsGenericParameter? null : IsGenericParameter? null :
HasElementType && ElementType.IsGenericParameter? null :
(HasElementType? ElementType.FullName : (HasElementType? ElementType.FullName :
(DeclaringType != null? DeclaringType.FullName + "+" : Namespace + (Namespace.Length > 0? "." : "")) (DeclaringType != null? DeclaringType.FullName + "+" : Namespace + (Namespace.Length > 0? "." : ""))
+ base.Name) + base.Name)
+ (IsArray? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsArray? "[" + new string(',', GetArrayRank() - 1) + "]" : "")
+ (IsByRef? "&" : "")
+ (IsPointer? "*" : ""); + (IsPointer? "*" : "");
// Returns the minimally qualified type name required to refer to this type within the specified scope // Returns the minimally qualified type name required to refer to this type within the specified scope
public string GetScopedFullName(Scope scope) { private string getScopedFullName(Scope scope) {
// This is the type to be used (generic type parameters have a null FullName) // This is the type to be used (generic type parameters have a null FullName)
var usedType = FullName?.Replace('+', '.') ?? Name; var usedType = FullName?.Replace('+', '.') ?? Name;
@@ -299,7 +304,7 @@ namespace Il2CppInspector.Reflection {
} }
// C#-friendly type name as it should be used in the scope of a given type // C#-friendly type name as it should be used in the scope of a given type
public string GetScopedCSharpName(Scope usingScope = null) { public string GetScopedCSharpName(Scope usingScope = null, bool omitRef = false) {
// Unscoped name if no using scope specified // Unscoped name if no using scope specified
if (usingScope == null) if (usingScope == null)
return CSharpName; return CSharpName;
@@ -308,7 +313,7 @@ namespace Il2CppInspector.Reflection {
// Built-in keyword type names do not require a scope // Built-in keyword type names do not require a scope
var i = Il2CppConstants.FullNameTypeString.IndexOf(s); var i = Il2CppConstants.FullNameTypeString.IndexOf(s);
var n = i != -1 ? Il2CppConstants.CSharpTypeString[i] : GetScopedFullName(usingScope); var n = i != -1 ? Il2CppConstants.CSharpTypeString[i] : getScopedFullName(usingScope);
// Unmangle generic type names // Unmangle generic type names
if (n?.IndexOf("`", StringComparison.Ordinal) != -1) if (n?.IndexOf("`", StringComparison.Ordinal) != -1)
@@ -327,7 +332,7 @@ namespace Il2CppInspector.Reflection {
if (HasElementType) if (HasElementType)
n = ElementType.GetScopedCSharpName(usingScope); n = ElementType.GetScopedCSharpName(usingScope);
return n + (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsPointer ? "*" : ""); return (IsByRef && !omitRef? "ref " : "") + n + (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsPointer ? "*" : "");
} }
// Get the generic type parameters for a specific usage of this type based on its scope, // Get the generic type parameters for a specific usage of this type based on its scope,
@@ -375,7 +380,7 @@ namespace Il2CppInspector.Reflection {
public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract; public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract;
public bool IsArray { get; } public bool IsArray { get; }
public bool IsByRef => throw new NotImplementedException(); public bool IsByRef { get; }
public bool IsClass => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class; public bool IsClass => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class;
public bool IsEnum => enumUnderlyingTypeUsage != -1; public bool IsEnum => enumUnderlyingTypeUsage != -1;
public bool IsGenericParameter { get; } public bool IsGenericParameter { get; }
@@ -504,6 +509,10 @@ namespace Il2CppInspector.Reflection {
if (((Definition.bitfield >> 1) & 1) == 1) if (((Definition.bitfield >> 1) & 1) == 1)
enumUnderlyingTypeUsage = Definition.elementTypeIndex; enumUnderlyingTypeUsage = Definition.elementTypeIndex;
// Pass-by-reference type
// NOTE: This should actually always evaluate to false in the current implementation
IsByRef = Index == Definition.byrefTypeIndex;
// Add all implemented interfaces // Add all implemented interfaces
implementedInterfaceUsages = new int[Definition.interfaces_count]; implementedInterfaceUsages = new int[Definition.interfaces_count];
for (var i = 0; i < Definition.interfaces_count; i++) for (var i = 0; i < Definition.interfaces_count; i++)
@@ -692,6 +701,23 @@ 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
private TypeInfo(TypeInfo underlyingType) {
ElementType = underlyingType;
IsByRef = 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;
}
public TypeInfo MakeByRefType() => new TypeInfo(this);
// 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() {
var refs = new HashSet<TypeInfo>(); var refs = new HashSet<TypeInfo>();
@@ -797,6 +823,7 @@ namespace Il2CppInspector.Reflection {
+ (GenericTypeParameters != null ? "[" + string.Join(",", GenericTypeParameters.Select(x => x.Namespace != Namespace? x.FullName ?? x.Name : x.Name)) + "]" : "") + (GenericTypeParameters != null ? "[" + string.Join(",", GenericTypeParameters.Select(x => x.Namespace != Namespace? x.FullName ?? x.Name : x.Name)) + "]" : "")
+ (GenericTypeArguments != null ? "[" + string.Join(",", GenericTypeArguments.Select(x => x.Namespace != Namespace? x.FullName ?? x.Name : x.Name)) + "]" : "")) + (GenericTypeArguments != null ? "[" + string.Join(",", GenericTypeArguments.Select(x => x.Namespace != Namespace? x.FullName ?? x.Name : x.Name)) + "]" : ""))
+ (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "")
+ (IsByRef ? "&" : "")
+ (IsPointer ? "*" : ""); + (IsPointer ? "*" : "");
public string GetAccessModifierString() => this switch { public string GetAccessModifierString() => this switch {