Output: Handle scoped type name resolution conflicts (CS0104)

This commit is contained in:
Katy Coe
2019-11-17 22:27:44 +01:00
parent e2f423b7f2
commit 32608874cc
9 changed files with 217 additions and 72 deletions

View File

@@ -74,6 +74,21 @@ namespace Il2CppInspector
var nsContext = ""; var nsContext = "";
var usedTypes = new List<TypeInfo>(); var usedTypes = new List<TypeInfo>();
// Determine all namespace references (note: this may include some that aren't actually used due to output suppression in generateType()
// We have to do this first so that we can generate properly scoped type references in the code
foreach (var type in types) {
var refs = type.GetAllTypeReferences();
var ns = refs.Where(r => !string.IsNullOrEmpty(r.Namespace) && r.Namespace != type.Namespace).Select(r => r.Namespace);
nsRefs.UnionWith(ns);
}
// Determine assemblies used in this file
var assemblies = types.Select(t => t.Assembly).Distinct();
// Add assembly attribute namespaces to reference list
nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace));
// Generate each type
foreach (var type in types) { foreach (var type in types) {
// Skip namespace and any children if requested // Skip namespace and any children if requested
@@ -85,7 +100,7 @@ namespace Il2CppInspector
continue; continue;
// Get code // Get code
var text = generateType(type); var text = generateType(type, nsRefs);
if (string.IsNullOrEmpty(text)) if (string.IsNullOrEmpty(text))
continue; continue;
@@ -111,11 +126,6 @@ namespace Il2CppInspector
if (!useNamespaceSyntax) if (!useNamespaceSyntax)
code.Append($"// Namespace: {(!string.IsNullOrEmpty(type.Namespace) ? type.Namespace : "<global namespace>")}\n"); code.Append($"// Namespace: {(!string.IsNullOrEmpty(type.Namespace) ? type.Namespace : "<global namespace>")}\n");
// Determine namespace references (note: this may include some that aren't actually used due to output suppression in generateType()
var refs = type.GetAllTypeReferences();
var ns = refs.Where(r => !string.IsNullOrEmpty(r.Namespace) && r.Namespace != type.Namespace).Select(r => r.Namespace);
nsRefs.UnionWith(ns);
// Append type definition // Append type definition
code.Append(text + "\n"); code.Append(text + "\n");
@@ -131,13 +141,15 @@ namespace Il2CppInspector
if (useNamespaceSyntax && !string.IsNullOrEmpty(nsContext)) if (useNamespaceSyntax && !string.IsNullOrEmpty(nsContext))
code.Remove(code.Length - 1, 1).Append("}\n"); code.Remove(code.Length - 1, 1).Append("}\n");
// Determine assemblies used in this file // Determine using directives (put System namespaces first)
var assemblies = types.Select(t => t.Assembly).Distinct(); nsRefs.Clear();
foreach (var type in usedTypes) {
// Add assembly attribute namespaces to reference list var refs = type.GetAllTypeReferences();
var ns = refs.Where(r => !string.IsNullOrEmpty(r.Namespace) && r.Namespace != type.Namespace).Select(r => r.Namespace);
nsRefs.UnionWith(ns);
}
nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace)); nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace));
// Determine using directives (put System namespaces first)
var usings = nsRefs.OrderBy(n => (n.StartsWith("System.") || n == "System") ? "0" + n : "1" + n); var usings = nsRefs.OrderBy(n => (n.StartsWith("System.") || n == "System") ? "0" + n : "1" + n);
// Ensure output directory exists // Ensure output directory exists
@@ -160,13 +172,13 @@ namespace Il2CppInspector
writer.Write("\n"); writer.Write("\n");
// Output assembly information and attributes // Output assembly information and attributes
writer.Write(generateAssemblyInfo(assemblies, excludedAssemblyAttributes) + "\n\n"); writer.Write(generateAssemblyInfo(assemblies, nsRefs, excludedAssemblyAttributes) + "\n\n");
// Output type definitions // Output type definitions
writer.Write(code); writer.Write(code);
} }
private string generateAssemblyInfo(IEnumerable<Reflection.Assembly> assemblies, IEnumerable<CustomAttributeData> excludedAttributes = null) { private string generateAssemblyInfo(IEnumerable<Reflection.Assembly> assemblies, IEnumerable<string> namespaces, IEnumerable<CustomAttributeData> excludedAttributes = null) {
var text = new StringBuilder(); var text = new StringBuilder();
foreach (var asm in assemblies) { foreach (var asm in assemblies) {
@@ -176,14 +188,14 @@ namespace Il2CppInspector
text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute) text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute)
.Except(excludedAttributes ?? new List<CustomAttributeData>()) .Except(excludedAttributes ?? new List<CustomAttributeData>())
.OrderBy(a => a.AttributeType.Name) .OrderBy(a => a.AttributeType.Name)
.ToString(attributePrefix: "assembly: ", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)); .ToString(new Scope { Current = null, Namespaces = namespaces }, attributePrefix: "assembly: ", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes));
if (asm.CustomAttributes.Any()) if (asm.CustomAttributes.Any())
text.Append("\n"); text.Append("\n");
} }
return text.ToString().TrimEnd(); return text.ToString().TrimEnd();
} }
private string generateType(TypeInfo type, string prefix = "") { private string generateType(TypeInfo type, IEnumerable<string> namespaces, string prefix = "") {
// Don't output compiler-generated types if desired // Don't output compiler-generated types if desired
if (SuppressGenerated && type.GetCustomAttributes(CGAttribute).Any()) if (SuppressGenerated && type.GetCustomAttributes(CGAttribute).Any())
return string.Empty; return string.Empty;
@@ -192,6 +204,11 @@ namespace Il2CppInspector
var usedMethods = new List<MethodInfo>(); var usedMethods = new List<MethodInfo>();
var sb = new StringBuilder(); var sb = new StringBuilder();
var scope = new Scope {
Current = type,
Namespaces = namespaces
};
// Fields // Fields
sb.Clear(); sb.Clear();
if (!type.IsEnum) { if (!type.IsEnum) {
@@ -203,7 +220,8 @@ namespace Il2CppInspector
sb.Append(prefix + "\t[NonSerialized]\n"); sb.Append(prefix + "\t[NonSerialized]\n");
// Attributes // Attributes
sb.Append(field.CustomAttributes.Where(a => a.AttributeType.FullName != FBAttribute).OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)); sb.Append(field.CustomAttributes.Where(a => a.AttributeType.FullName != FBAttribute).OrderBy(a => a.AttributeType.Name)
.ToString(scope, prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes));
sb.Append(prefix + "\t"); sb.Append(prefix + "\t");
sb.Append(field.GetModifierString()); sb.Append(field.GetModifierString());
@@ -211,11 +229,11 @@ namespace Il2CppInspector
if (field.GetCustomAttributes(FBAttribute).Any()) { if (field.GetCustomAttributes(FBAttribute).Any()) {
if (!SuppressMetadata) if (!SuppressMetadata)
sb.Append($"/* {field.GetCustomAttributes(FBAttribute)[0].VirtualAddress.ToAddressString()} */ "); sb.Append($"/* {field.GetCustomAttributes(FBAttribute)[0].VirtualAddress.ToAddressString()} */ ");
sb.Append($"{field.FieldType.DeclaredFields[0].FieldType.CSharpName} {field.Name}[0]"); // FixedElementField sb.Append($"{field.FieldType.DeclaredFields[0].FieldType.GetScopedCSharpName(scope)} {field.Name}[0]"); // FixedElementField
} }
// Regular fields // Regular fields
else else
sb.Append($"{field.FieldType.CSharpName} {field.Name}"); sb.Append($"{field.FieldType.GetScopedCSharpName(scope)} {field.Name}");
if (field.HasDefaultValue) if (field.HasDefaultValue)
sb.Append($" = {field.DefaultValueString}"); sb.Append($" = {field.DefaultValueString}");
sb.Append(";"); sb.Append(";");
@@ -234,14 +252,15 @@ namespace Il2CppInspector
sb.Clear(); sb.Clear();
foreach (var prop in type.DeclaredProperties) { foreach (var prop in type.DeclaredProperties) {
// Attributes // Attributes
sb.Append(prop.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)); sb.Append(prop.CustomAttributes.OrderBy(a => a.AttributeType.Name)
.ToString(scope, prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes));
// The access mask enum values go from 1 (private) to 6 (public) in order from most to least restrictive // The access mask enum values go from 1 (private) to 6 (public) in order from most to least restrictive
var getAccess = (prop.GetMethod?.Attributes ?? 0) & MethodAttributes.MemberAccessMask; var getAccess = (prop.GetMethod?.Attributes ?? 0) & MethodAttributes.MemberAccessMask;
var setAccess = (prop.SetMethod?.Attributes ?? 0) & MethodAttributes.MemberAccessMask; var setAccess = (prop.SetMethod?.Attributes ?? 0) & MethodAttributes.MemberAccessMask;
var primary = getAccess >= setAccess ? prop.GetMethod : prop.SetMethod; var primary = getAccess >= setAccess ? prop.GetMethod : prop.SetMethod;
sb.Append($"{prefix}\t{primary.GetModifierString()}{prop.PropertyType.CSharpName} "); sb.Append($"{prefix}\t{primary.GetModifierString(scope)}{prop.PropertyType.GetScopedCSharpName(scope)} ");
// Non-indexer // Non-indexer
if ((!prop.CanRead || !prop.GetMethod.DeclaredParameters.Any()) && (!prop.CanWrite || prop.SetMethod.DeclaredParameters.Count == 1)) if ((!prop.CanRead || !prop.GetMethod.DeclaredParameters.Any()) && (!prop.CanWrite || prop.SetMethod.DeclaredParameters.Count == 1))
@@ -249,13 +268,13 @@ namespace Il2CppInspector
// Indexer // Indexer
else else
sb.Append("this[" + string.Join(", ", primary.DeclaredParameters.SkipLast(getAccess >= setAccess? 0 : 1) sb.Append("this[" + string.Join(", ", primary.DeclaredParameters.SkipLast(getAccess >= setAccess? 0 : 1)
.Select(p => p.GetParameterString(!SuppressMetadata, CommentAttributes))) + "] { "); .Select(p => p.GetParameterString(scope, !SuppressMetadata, CommentAttributes))) + "] { ");
sb.Append((prop.CanRead? prop.GetMethod.CustomAttributes.Where(a => !SuppressGenerated || a.AttributeType.FullName != CGAttribute) sb.Append((prop.CanRead? prop.GetMethod.CustomAttributes.Where(a => !SuppressGenerated || a.AttributeType.FullName != CGAttribute)
.ToString(inline: true, emitPointer: !SuppressMetadata, mustCompile: CommentAttributes) .ToString(scope, inline: true, emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)
+ (getAccess < setAccess? prop.GetMethod.GetAccessModifierString() : "") + "get; " : "") + (getAccess < setAccess? prop.GetMethod.GetAccessModifierString() : "") + "get; " : "")
+ (prop.CanWrite? prop.SetMethod.CustomAttributes.Where(a => !SuppressGenerated || a.AttributeType.FullName != CGAttribute) + (prop.CanWrite? prop.SetMethod.CustomAttributes.Where(a => !SuppressGenerated || a.AttributeType.FullName != CGAttribute)
.ToString(inline: true, emitPointer: !SuppressMetadata, mustCompile: CommentAttributes) .ToString(scope, inline: true, emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)
+ (setAccess < getAccess? prop.SetMethod.GetAccessModifierString() : "") + "set; " : "") + "}"); + (setAccess < getAccess? prop.SetMethod.GetAccessModifierString() : "") + "set; " : "") + "}");
if (!SuppressMetadata) { if (!SuppressMetadata) {
if ((prop.CanRead && prop.GetMethod.VirtualAddress != null) || (prop.CanWrite && prop.SetMethod.VirtualAddress != null)) if ((prop.CanRead && prop.GetMethod.VirtualAddress != null) || (prop.CanWrite && prop.SetMethod.VirtualAddress != null))
@@ -274,10 +293,11 @@ namespace Il2CppInspector
sb.Clear(); sb.Clear();
foreach (var evt in type.DeclaredEvents) { foreach (var evt in type.DeclaredEvents) {
// Attributes // Attributes
sb.Append(evt.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)); sb.Append(evt.CustomAttributes.OrderBy(a => a.AttributeType.Name)
.ToString(scope, prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes));
string modifiers = evt.AddMethod?.GetModifierString(); string modifiers = evt.AddMethod?.GetModifierString(scope);
sb.Append($"{prefix}\t{modifiers}event {evt.EventHandlerType.CSharpName} {evt.Name} {{\n"); sb.Append($"{prefix}\t{modifiers}event {evt.EventHandlerType.GetScopedCSharpName(scope)} {evt.Name} {{\n");
var m = new Dictionary<string, (ulong, ulong)?>(); var m = new Dictionary<string, (ulong, ulong)?>();
if (evt.AddMethod != null) m.Add("add", evt.AddMethod.VirtualAddress); if (evt.AddMethod != null) m.Add("add", evt.AddMethod.VirtualAddress);
if (evt.RemoveMethod != null) m.Add("remove", evt.RemoveMethod.VirtualAddress); if (evt.RemoveMethod != null) m.Add("remove", evt.RemoveMethod.VirtualAddress);
@@ -290,16 +310,18 @@ namespace Il2CppInspector
codeBlocks.Add("Events", sb.ToString()); codeBlocks.Add("Events", sb.ToString());
// Nested types // Nested types
codeBlocks.Add("Nested types", string.Join("\n", type.DeclaredNestedTypes.Select(n => generateType(n, prefix + "\t")).Where(c => !string.IsNullOrEmpty(c)))); codeBlocks.Add("Nested types", string.Join("\n", type.DeclaredNestedTypes
.Select(n => generateType(n, namespaces, prefix + "\t")).Where(c => !string.IsNullOrEmpty(c))));
// Constructors // Constructors
sb.Clear(); sb.Clear();
foreach (var method in type.DeclaredConstructors) { foreach (var method in type.DeclaredConstructors) {
// Attributes // Attributes
sb.Append(method.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)); sb.Append(method.CustomAttributes.OrderBy(a => a.AttributeType.Name)
.ToString(scope, prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes));
sb.Append($"{prefix}\t{method.GetModifierString()}{method.DeclaringType.UnmangledBaseName}{method.GetTypeParametersString()}("); sb.Append($"{prefix}\t{method.GetModifierString(scope)}{method.DeclaringType.UnmangledBaseName}{method.GetTypeParametersString(scope)}(");
sb.Append(method.GetParametersString(!SuppressMetadata) + ")" + (method.IsAbstract? ";" : @" {}")); sb.Append(method.GetParametersString(scope, !SuppressMetadata) + ")" + (method.IsAbstract? ";" : @" {}"));
sb.Append((!SuppressMetadata && method.VirtualAddress != null ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n"); sb.Append((!SuppressMetadata && method.VirtualAddress != null ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n");
} }
codeBlocks.Add("Constructors", sb.ToString()); codeBlocks.Add("Constructors", sb.ToString());
@@ -307,11 +329,11 @@ namespace Il2CppInspector
// Methods // Methods
// Don't re-output methods for constructors, properties, events etc. // Don't re-output methods for constructors, properties, events etc.
var methods = type.DeclaredMethods.Except(usedMethods).Where(m => m.CustomAttributes.All(a => a.AttributeType.FullName != ExtAttribute)); var methods = type.DeclaredMethods.Except(usedMethods).Where(m => m.CustomAttributes.All(a => a.AttributeType.FullName != ExtAttribute));
codeBlocks.Add("Methods", string.Concat(methods.Select(m => generateMethod(m, prefix)))); codeBlocks.Add("Methods", string.Concat(methods.Select(m => generateMethod(m, scope, prefix))));
usedMethods.AddRange(methods); usedMethods.AddRange(methods);
// Extension methods // Extension methods
codeBlocks.Add("Extension methods", string.Concat(type.DeclaredMethods.Except(usedMethods).Select(m => generateMethod(m, prefix)))); codeBlocks.Add("Extension methods", string.Concat(type.DeclaredMethods.Except(usedMethods).Select(m => generateMethod(m, scope, prefix))));
// Type declaration // Type declaration
sb.Clear(); sb.Clear();
@@ -324,7 +346,7 @@ namespace Il2CppInspector
// TODO: DefaultMemberAttribute should be output if it is present and the type does not have an indexer, otherwise suppressed // TODO: DefaultMemberAttribute should be output if it is present and the type does not have an indexer, otherwise suppressed
// See https://docs.microsoft.com/en-us/dotnet/api/system.reflection.defaultmemberattribute?view=netframework-4.8 // See https://docs.microsoft.com/en-us/dotnet/api/system.reflection.defaultmemberattribute?view=netframework-4.8
sb.Append(type.CustomAttributes.Where(a => a.AttributeType.FullName != DMAttribute && a.AttributeType.FullName != ExtAttribute) sb.Append(type.CustomAttributes.Where(a => a.AttributeType.FullName != DMAttribute && a.AttributeType.FullName != ExtAttribute)
.OrderBy(a => a.AttributeType.Name).ToString(prefix, emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)); .OrderBy(a => a.AttributeType.Name).ToString(scope, prefix, emitPointer: !SuppressMetadata, mustCompile: CommentAttributes));
// Roll-up multicast delegates to use the 'delegate' syntactic sugar // Roll-up multicast delegates to use the 'delegate' syntactic sugar
if (type.IsClass && type.IsSealed && type.BaseType?.FullName == "System.MulticastDelegate") { if (type.IsClass && type.IsSealed && type.BaseType?.FullName == "System.MulticastDelegate") {
@@ -335,8 +357,8 @@ namespace Il2CppInspector
//sb.Append(del.ReturnType.CustomAttributes.ToString(prefix, "return: ", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)); //sb.Append(del.ReturnType.CustomAttributes.ToString(prefix, "return: ", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes));
if (del.RequiresUnsafeContext) if (del.RequiresUnsafeContext)
sb.Append("unsafe "); sb.Append("unsafe ");
sb.Append($"delegate {del.ReturnType.CSharpName} {type.CSharpTypeDeclarationName}("); sb.Append($"delegate {del.ReturnType.GetScopedCSharpName(scope)} {type.CSharpTypeDeclarationName}(");
sb.Append(del.GetParametersString(!SuppressMetadata) + ");"); sb.Append(del.GetParametersString(scope, !SuppressMetadata) + ");");
if (!SuppressMetadata) if (!SuppressMetadata)
sb.Append($" // TypeDefIndex: {type.Index}; {del.VirtualAddress.ToAddressString()}"); sb.Append($" // TypeDefIndex: {type.Index}; {del.VirtualAddress.ToAddressString()}");
sb.Append("\n"); sb.Append("\n");
@@ -345,11 +367,11 @@ namespace Il2CppInspector
sb.Append(prefix + type.GetModifierString()); sb.Append(prefix + type.GetModifierString());
var @base = type.ImplementedInterfaces.Select(x => x.CSharpName).ToList(); var @base = type.ImplementedInterfaces.Select(x => x.GetScopedCSharpName(scope)).ToList();
if (type.BaseType != null && type.BaseType.FullName != "System.Object" && type.BaseType.FullName != "System.ValueType" && !type.IsEnum) if (type.BaseType != null && type.BaseType.FullName != "System.Object" && type.BaseType.FullName != "System.ValueType" && !type.IsEnum)
@base.Insert(0, type.BaseType.CSharpName); @base.Insert(0, type.BaseType.GetScopedCSharpName(scope));
if (type.IsEnum && type.GetEnumUnderlyingType().FullName != "System.Int32") // enums derive from int by default if (type.IsEnum && type.GetEnumUnderlyingType().FullName != "System.Int32") // enums derive from int by default
@base.Insert(0, type.GetEnumUnderlyingType().CSharpName); @base.Insert(0, type.GetEnumUnderlyingType().GetScopedCSharpName(scope));
var baseText = @base.Count > 0 ? " : " + string.Join(", ", @base) : string.Empty; var baseText = @base.Count > 0 ? " : " + string.Join(", ", @base) : string.Empty;
sb.Append($"{type.CSharpTypeDeclarationName}{baseText}"); sb.Append($"{type.CSharpTypeDeclarationName}{baseText}");
@@ -359,7 +381,7 @@ namespace Il2CppInspector
if (type.GenericTypeParameters != null) if (type.GenericTypeParameters != null)
foreach (var gp in type.GenericTypeParameters) { foreach (var gp in type.GenericTypeParameters) {
var constraint = gp.GetTypeConstraintsString(); var constraint = gp.GetTypeConstraintsString(scope);
if (constraint != string.Empty) if (constraint != string.Empty)
sb.Append($"{prefix}\t{constraint}\n"); sb.Append($"{prefix}\t{constraint}\n");
} }
@@ -380,7 +402,7 @@ namespace Il2CppInspector
return sb.ToString(); return sb.ToString();
} }
private string generateMethod(MethodInfo method, string prefix) { private string generateMethod(MethodInfo method, Scope scope, string prefix) {
if (SuppressGenerated && method.GetCustomAttributes(CGAttribute).Any()) if (SuppressGenerated && method.GetCustomAttributes(CGAttribute).Any())
return string.Empty; return string.Empty;
@@ -388,20 +410,20 @@ namespace Il2CppInspector
// Attributes // Attributes
writer.Append(method.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute).OrderBy(a => a.AttributeType.Name) writer.Append(method.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute).OrderBy(a => a.AttributeType.Name)
.ToString(prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)); .ToString(scope, prefix + "\t", emitPointer: !SuppressMetadata, mustCompile: CommentAttributes));
// IL2CPP doesn't seem to retain return type attributes // IL2CPP doesn't seem to retain return type attributes
//writer.Append(method.ReturnType.CustomAttributes.ToString(prefix + "\t", "return: ", emitPointer: !SuppressMetadata)); //writer.Append(method.ReturnType.CustomAttributes.ToString(prefix + "\t", "return: ", emitPointer: !SuppressMetadata));
writer.Append($"{prefix}\t{method.GetModifierString()}"); writer.Append($"{prefix}\t{method.GetModifierString(scope)}");
if (method.Name != "op_Implicit" && method.Name != "op_Explicit") if (method.Name != "op_Implicit" && method.Name != "op_Explicit")
writer.Append($"{method.ReturnParameter.GetReturnParameterString()} {method.CSharpName}{method.GetTypeParametersString()}"); writer.Append($"{method.ReturnParameter.GetReturnParameterString(scope)} {method.CSharpName}{method.GetTypeParametersString(scope)}");
else else
writer.Append($"{method.CSharpName}{method.ReturnType.CSharpName}"); writer.Append($"{method.CSharpName}{method.ReturnType.GetScopedCSharpName(scope)}");
writer.Append("(" + method.GetParametersString(!SuppressMetadata) + ")"); writer.Append("(" + method.GetParametersString(scope, !SuppressMetadata) + ")");
if (method.GenericTypeParameters != null) if (method.GenericTypeParameters != null)
foreach (var gp in method.GenericTypeParameters) { foreach (var gp in method.GenericTypeParameters) {
var constraint = gp.GetTypeConstraintsString(); var constraint = gp.GetTypeConstraintsString(scope);
if (constraint != string.Empty) if (constraint != string.Empty)
writer.Append($"\n{prefix}\t\t{constraint}"); writer.Append($"\n{prefix}\t\t{constraint}");
} }

View File

@@ -22,7 +22,7 @@ namespace Il2CppInspector.Reflection
public override string ToString() => DeclaringType.Name + "(" + string.Join(", ", DeclaredParameters.Select(x => x.ParameterType.Name)) + ")"; public override string ToString() => DeclaringType.Name + "(" + string.Join(", ", DeclaredParameters.Select(x => x.ParameterType.Name)) + ")";
public override string GetSignatureString() => Name + GetTypeParametersString() public override string GetSignatureString(Scope usingScope) => Name + GetTypeParametersString(usingScope)
+ "(" + string.Join(",", DeclaredParameters.Select(x => x.GetSignatureString())) + ")"; + "(" + string.Join(",", DeclaredParameters.Select(x => x.GetSignatureString())) + ")";
} }
} }

View File

@@ -61,7 +61,7 @@ namespace Il2CppInspector.Reflection
public static IList<CustomAttributeData> GetCustomAttributes(EventInfo evt) => getCustomAttributes(evt.Assembly, evt.Definition.token, evt.Definition.customAttributeIndex); public static IList<CustomAttributeData> GetCustomAttributes(EventInfo evt) => getCustomAttributes(evt.Assembly, evt.Definition.token, evt.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(FieldInfo field) => getCustomAttributes(field.Assembly, field.Definition.token, field.Definition.customAttributeIndex); public static IList<CustomAttributeData> GetCustomAttributes(FieldInfo field) => getCustomAttributes(field.Assembly, field.Definition.token, field.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(MethodBase method) => getCustomAttributes(method.Assembly, method.Definition.token, method.Definition.customAttributeIndex); public static IList<CustomAttributeData> GetCustomAttributes(MethodBase method) => getCustomAttributes(method.Assembly, method.Definition.token, method.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo param) => getCustomAttributes(param.Member.Assembly, param.Definition.token, param.Definition.customAttributeIndex); public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo param) => getCustomAttributes(param.DeclaringMethod.Assembly, param.Definition.token, param.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(PropertyInfo prop) => getCustomAttributes(prop.Assembly, prop.Definition.token, prop.Definition.customAttributeIndex); public static IList<CustomAttributeData> GetCustomAttributes(PropertyInfo prop) => getCustomAttributes(prop.Assembly, prop.Definition.token, prop.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(TypeInfo type) => getCustomAttributes(type.Assembly, type.Definition.token, type.Definition.customAttributeIndex); public static IList<CustomAttributeData> GetCustomAttributes(TypeInfo type) => getCustomAttributes(type.Assembly, type.Definition.token, type.Definition.customAttributeIndex);
} }

View File

@@ -1,4 +1,10 @@
using System; /*
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -8,8 +14,8 @@ namespace Il2CppInspector.Reflection
public static class Extensions public static class Extensions
{ {
// Convert a list of CustomAttributeData objects into C#-friendly attribute usages // Convert a list of CustomAttributeData objects into C#-friendly attribute usages
public static string ToString(this IEnumerable<CustomAttributeData> attributes, string linePrefix = "", string attributePrefix = "", public static string ToString(this IEnumerable<CustomAttributeData> attributes, Scope scope = null,
bool inline = false, bool emitPointer = false, bool mustCompile = false) { string linePrefix = "", string attributePrefix = "", bool inline = false, bool emitPointer = false, bool mustCompile = false) {
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var cad in attributes) { foreach (var cad in attributes) {
@@ -20,7 +26,7 @@ namespace Il2CppInspector.Reflection
var commentStart = mustCompile && !parameterlessConstructor? inline? "/* " : "// " : ""; var commentStart = mustCompile && !parameterlessConstructor? inline? "/* " : "// " : "";
var commentEnd = commentStart.Length > 0 && inline? " */" : ""; var commentEnd = commentStart.Length > 0 && inline? " */" : "";
var name = cad.AttributeType.CSharpName; var name = cad.AttributeType.GetScopedCSharpName(scope);
var suffix = name.LastIndexOf("Attribute", StringComparison.Ordinal); var suffix = name.LastIndexOf("Attribute", StringComparison.Ordinal);
if (suffix != -1) if (suffix != -1)
name = name[..suffix]; name = name[..suffix];

View File

@@ -128,7 +128,7 @@ namespace Il2CppInspector.Reflection
_ => "" _ => ""
}; };
public string GetModifierString() { public string GetModifierString(Scope usingScope) {
// Interface methods and properties have no visible modifiers (they are always declared 'public abstract') // Interface methods and properties have no visible modifiers (they are always declared 'public abstract')
if (DeclaringType.IsInterface) if (DeclaringType.IsInterface)
return string.Empty; return string.Empty;
@@ -151,7 +151,7 @@ namespace Il2CppInspector.Reflection
modifiers.Append("extern "); modifiers.Append("extern ");
// Method hiding // Method hiding
if ((DeclaringType.BaseType?.GetAllMethods().Any(m => m.GetSignatureString() == GetSignatureString() && m.IsHideBySig) ?? false) if ((DeclaringType.BaseType?.GetAllMethods().Any(m => m.GetSignatureString(usingScope) == GetSignatureString(usingScope) && m.IsHideBySig) ?? false)
&& (((Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.ReuseSlot && !IsVirtual) && (((Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.ReuseSlot && !IsVirtual)
|| (Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.NewSlot)) || (Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.NewSlot))
modifiers.Append($"new "); modifiers.Append($"new ");
@@ -166,13 +166,13 @@ namespace Il2CppInspector.Reflection
} }
// Get C# syntax-friendly list of parameters // Get C# syntax-friendly list of parameters
public string GetParametersString(bool emitPointer = false, bool commentAttributes = false) public string GetParametersString(Scope usingScope, bool emitPointer = false, bool commentAttributes = false)
=> string.Join(", ", DeclaredParameters.Select(p => p.GetParameterString(emitPointer, commentAttributes))); => string.Join(", ", DeclaredParameters.Select(p => p.GetParameterString(usingScope, emitPointer, commentAttributes)));
public string GetTypeParametersString() => GenericTypeParameters == null? "" : public string GetTypeParametersString(Scope usingScope) => GenericTypeParameters == null? "" :
"<" + string.Join(", ", GenericTypeParameters.Select(p => p.CSharpName)) + ">"; "<" + string.Join(", ", GenericTypeParameters.Select(p => p.GetScopedCSharpName(usingScope))) + ">";
public abstract string GetSignatureString(); public abstract string GetSignatureString(Scope usingScope);
// List of operator overload metadata names // List of operator overload metadata names
// https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/operator-overloads // https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/operator-overloads

View File

@@ -33,7 +33,7 @@ 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.Name)) + ")";
public override string GetSignatureString() => ReturnParameter.GetSignatureString() + " " + Name + GetTypeParametersString() public override string GetSignatureString(Scope usingScope) => ReturnParameter.GetSignatureString() + " " + Name + GetTypeParametersString(usingScope)
+ "(" + string.Join(",", DeclaredParameters.Select(x => x.GetSignatureString())) + ")"; + "(" + string.Join(",", DeclaredParameters.Select(x => x.GetSignatureString())) + ")";
} }
} }

View File

@@ -37,15 +37,15 @@ namespace Il2CppInspector.Reflection
public bool IsOut => (Attributes & ParameterAttributes.Out) != 0; public bool IsOut => (Attributes & ParameterAttributes.Out) != 0;
public bool IsRetval => (Attributes & ParameterAttributes.Retval) != 0; public bool IsRetval => (Attributes & ParameterAttributes.Retval) != 0;
// The member in which the parameter is defined // The method in which the parameter is defined
public MemberInfo Member { get; } public MethodBase DeclaringMethod { get; }
// Name of parameter // Name of parameter
public string Name { get; } public string Name { get; }
// Type of this parameter // Type of this parameter
private readonly int paramTypeUsage; private readonly int paramTypeUsage;
public TypeInfo ParameterType => Member.Assembly.Model.GetTypeFromUsage(paramTypeUsage, MemberTypes.TypeInfo); public TypeInfo ParameterType => DeclaringMethod.Assembly.Model.GetTypeFromUsage(paramTypeUsage, MemberTypes.TypeInfo);
// Zero-indexed position of the parameter in parameter list // Zero-indexed position of the parameter in parameter list
public int Position { get; } public int Position { get; }
@@ -53,7 +53,7 @@ namespace Il2CppInspector.Reflection
// Create a parameter. Specify paramIndex == -1 for a return type parameter // Create a parameter. Specify paramIndex == -1 for a return type parameter
public ParameterInfo(Il2CppInspector pkg, int paramIndex, MethodBase declaringMethod) { public ParameterInfo(Il2CppInspector pkg, int paramIndex, MethodBase declaringMethod) {
Index = paramIndex; Index = paramIndex;
Member = declaringMethod; DeclaringMethod = declaringMethod;
if (paramIndex == -1) { if (paramIndex == -1) {
Position = -1; Position = -1;
@@ -96,15 +96,15 @@ namespace Il2CppInspector.Reflection
+ (IsByRef? "ref " : "") + (IsByRef? "ref " : "")
+ (IsOut? "out " : ""); + (IsOut? "out " : "");
private string getCSharpSignatureString() => $"{GetModifierString()}{ParameterType.CSharpName}"; private string getCSharpSignatureString(Scope scope) => $"{GetModifierString()}{ParameterType.GetScopedCSharpName(scope)}";
public string GetSignatureString() => $"{GetModifierString()}{ParameterType.FullName}"; public string GetSignatureString() => $"{GetModifierString()}{ParameterType.FullName}";
public string GetParameterString(bool emitPointer = false, bool compileAttributes = false) => IsRetval? null : public string GetParameterString(Scope usingScope, bool emitPointer = false, bool compileAttributes = false) => IsRetval? null :
(Position == 0 && Member.GetCustomAttributes("System.Runtime.CompilerServices.ExtensionAttribute").Any()? "this ":"") (Position == 0 && DeclaringMethod.GetCustomAttributes("System.Runtime.CompilerServices.ExtensionAttribute").Any()? "this ":"")
+ $"{CustomAttributes.ToString(inline: true, emitPointer: emitPointer, mustCompile: compileAttributes).Replace("[ParamArray]", "params")}" + $"{CustomAttributes.ToString(usingScope, inline: true, emitPointer: emitPointer, mustCompile: compileAttributes).Replace("[ParamArray]", "params")}"
+ $"{getCSharpSignatureString()} {Name}" + $"{getCSharpSignatureString(usingScope)} {Name}"
+ (HasDefaultValue ? " = " + DefaultValue.ToCSharpValue() + (emitPointer && !(DefaultValue is null)? $" /* Metadata: 0x{(uint) DefaultValueMetadataAddress:X8} */" : "") : ""); + (HasDefaultValue ? " = " + DefaultValue.ToCSharpValue() + (emitPointer && !(DefaultValue is null)? $" /* Metadata: 0x{(uint) DefaultValueMetadataAddress:X8} */" : "") : "");
public string GetReturnParameterString() => !IsRetval? null : getCSharpSignatureString(); public string GetReturnParameterString(Scope scope) => !IsRetval? null : getCSharpSignatureString(scope);
} }
} }

View File

@@ -0,0 +1,20 @@
/*
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using System.Collections.Generic;
namespace Il2CppInspector.Reflection
{
// A code scope with which to evaluate how to output type references
public class Scope
{
// The scope we are currently in
public TypeInfo Current;
// The list of namespace using directives in the file
public IEnumerable<string> Namespaces;
}
}

View File

@@ -143,6 +143,103 @@ namespace Il2CppInspector.Reflection {
+ (IsArray? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsArray? "[" + new string(',', GetArrayRank() - 1) + "]" : "")
+ (IsPointer? "*" : ""); + (IsPointer? "*" : "");
// Returns the minimally qualified type name required to refer to this type within the specified scope
public string GetScopedFullName(Scope scope) {
// This is the scope in which this type is currently being used
// If Scope.Current is null, our scope is at the assembly level
var usingScope = scope.Current?.FullName ?? "";
// This is the scope in which this type's definition is located
var declaringScope = DeclaringType?.FullName ?? Namespace;
// If the scope of usage is inside the scope in which the type is declared, no additional scope is needed
if ((usingScope + ".").StartsWith(declaringScope + ".") || (usingScope + "+").StartsWith(declaringScope + "+"))
return base.Name;
// Global (unnamed) namespace?
string scopedName;
if (string.IsNullOrEmpty(declaringScope))
scopedName = base.Name;
// Find first difference in the declaring scope from the using scope, moving one namespace/type name at a time
else {
var diff = 0;
usingScope += ".";
while (usingScope.IndexOf(".", diff) == declaringScope.IndexOf(".", diff)
&& usingScope.IndexOf(".", diff) != -1
&& usingScope.Substring(0, usingScope.IndexOf(".", diff))
== declaringScope.Substring(0, declaringScope.IndexOf(".", diff)))
diff = usingScope.IndexOf(".", diff) + 1;
usingScope = usingScope.Substring(0, usingScope.Length -1) + "+";
while (usingScope.IndexOf("+", diff) == declaringScope.IndexOf("+", diff)
&& usingScope.IndexOf("+", diff) != -1
&& usingScope.Substring(0, usingScope.IndexOf("+", diff))
== declaringScope.Substring(0, declaringScope.IndexOf("+", diff)))
diff = usingScope.IndexOf("+", diff) + 1;
scopedName = declaringScope.Substring(diff) + (DeclaringType != null? "+" : ".") + base.Name;
}
// At this point, scopedName contains the minimum required scope, discounting any using directives
// or whether there are conflicts with any ancestor scope
// Check to see if there is a namespace in our using directives which brings this type into scope
var usingRef = scope.Namespaces.OrderByDescending(n => n.Length).FirstOrDefault(n => scopedName.StartsWith(n + "."));
var minimallyScopedName = usingRef == null ? scopedName : scopedName.Substring(usingRef.Length + 1);
// minimallyScopedName now contains the minimum required scope, taking using directives into account
// Are there any ancestors in the using scope with the same type name as the first part of the minimally scoped name?
// If so, the ancestor type name will hide the type we are trying to reference,
// so we need to provide the scope ignoring any using directives
var firstPart = minimallyScopedName.Split('.')[0].Split('+')[0];
for (var d = scope.Current; d != null; d = d.DeclaringType)
if (d.BaseName == firstPart)
return scopedName.Replace('+', '.');
// If there are multiple using directives that would allow the same minimally scoped name to be used,
// then the minimally scoped name is ambiguous and we can't use it
// NOTE: We should check all the parts, not just the first part, but this works in the vast majority of cases
if (scope.Namespaces.Count(n => Assembly.Model.TypesByFullName.ContainsKey(n + "." + firstPart)) > 1)
return scopedName.Replace('+', '.');
return minimallyScopedName.Replace('+', '.');
}
// C#-friendly type name as it should be used in the scope of a given type
public string GetScopedCSharpName(Scope usingScope = null) {
// Unscoped name if no using scope specified
if (usingScope == null)
return CSharpName;
var s = Namespace + "." + base.Name;
// Built-in keyword type names do not require a scope
var i = Il2CppConstants.FullNameTypeString.IndexOf(s);
var n = i != -1 ? Il2CppConstants.CSharpTypeString[i] : GetScopedFullName(usingScope);
// Unmangle generic type names
if (n?.IndexOf("`", StringComparison.Ordinal) != -1)
n = n?.Remove(n.IndexOf("`", StringComparison.Ordinal));
// Generic type parameters and type arguments
var g = (GenericTypeParameters != null ? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.GetScopedCSharpName(usingScope))) + ">" : "");
g = (GenericTypeArguments != null ? "<" + string.Join(", ", GenericTypeArguments.Select(x => x.GetScopedCSharpName(usingScope))) + ">" : g);
n += g;
// Nullable types
if (s == "System.Nullable`1" && GenericTypeArguments.Any())
n = GenericTypeArguments[0].GetScopedCSharpName(usingScope) + "?";
// Arrays, pointers, references
if (HasElementType)
n = ElementType.GetScopedCSharpName(usingScope);
return n + (IsArray ? "[" + new string(',', GetArrayRank() - 1) + "]" : "") + (IsPointer ? "*" : "");
}
public GenericParameterAttributes GenericParameterAttributes { get; } public GenericParameterAttributes GenericParameterAttributes { get; }
public int GenericParameterPosition { get; } public int GenericParameterPosition { get; }
@@ -607,7 +704,7 @@ namespace Il2CppInspector.Reflection {
return modifiers.ToString(); return modifiers.ToString();
} }
public string GetTypeConstraintsString() { public string GetTypeConstraintsString(Scope scope) {
if (!IsGenericParameter) if (!IsGenericParameter)
return string.Empty; return string.Empty;
@@ -620,7 +717,7 @@ namespace Il2CppInspector.Reflection {
if (DeclaringMethod == null && DeclaringType.IsNested && (DeclaringType.DeclaringType.GenericTypeParameters?.Any(p => p.Name == Name) ?? false)) if (DeclaringMethod == null && DeclaringType.IsNested && (DeclaringType.DeclaringType.GenericTypeParameters?.Any(p => p.Name == Name) ?? false))
return string.Empty; return string.Empty;
var constraintList = typeConstraints.Where(c => c.FullName != "System.ValueType").Select(c => c.CSharpTypeDeclarationName).ToList(); var constraintList = typeConstraints.Where(c => c.FullName != "System.ValueType").Select(c => c.GetScopedCSharpName(scope)).ToList();
if ((GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) == GenericParameterAttributes.NotNullableValueTypeConstraint) if ((GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) == GenericParameterAttributes.NotNullableValueTypeConstraint)
constraintList.Add("struct"); constraintList.Add("struct");