Output: Handle scoped type name resolution conflicts (CS0104)
This commit is contained in:
@@ -74,6 +74,21 @@ namespace Il2CppInspector
|
||||
var nsContext = "";
|
||||
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) {
|
||||
|
||||
// Skip namespace and any children if requested
|
||||
@@ -85,7 +100,7 @@ namespace Il2CppInspector
|
||||
continue;
|
||||
|
||||
// Get code
|
||||
var text = generateType(type);
|
||||
var text = generateType(type, nsRefs);
|
||||
if (string.IsNullOrEmpty(text))
|
||||
continue;
|
||||
|
||||
@@ -111,11 +126,6 @@ namespace Il2CppInspector
|
||||
if (!useNamespaceSyntax)
|
||||
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
|
||||
code.Append(text + "\n");
|
||||
|
||||
@@ -131,13 +141,15 @@ namespace Il2CppInspector
|
||||
if (useNamespaceSyntax && !string.IsNullOrEmpty(nsContext))
|
||||
code.Remove(code.Length - 1, 1).Append("}\n");
|
||||
|
||||
// Determine assemblies used in this file
|
||||
var assemblies = types.Select(t => t.Assembly).Distinct();
|
||||
|
||||
// Add assembly attribute namespaces to reference list
|
||||
// Determine using directives (put System namespaces first)
|
||||
nsRefs.Clear();
|
||||
foreach (var type in usedTypes) {
|
||||
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));
|
||||
|
||||
// Determine using directives (put System namespaces first)
|
||||
var usings = nsRefs.OrderBy(n => (n.StartsWith("System.") || n == "System") ? "0" + n : "1" + n);
|
||||
|
||||
// Ensure output directory exists
|
||||
@@ -160,13 +172,13 @@ namespace Il2CppInspector
|
||||
writer.Write("\n");
|
||||
|
||||
// Output assembly information and attributes
|
||||
writer.Write(generateAssemblyInfo(assemblies, excludedAssemblyAttributes) + "\n\n");
|
||||
writer.Write(generateAssemblyInfo(assemblies, nsRefs, excludedAssemblyAttributes) + "\n\n");
|
||||
|
||||
// Output type definitions
|
||||
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();
|
||||
|
||||
foreach (var asm in assemblies) {
|
||||
@@ -176,14 +188,14 @@ namespace Il2CppInspector
|
||||
text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute)
|
||||
.Except(excludedAttributes ?? new List<CustomAttributeData>())
|
||||
.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())
|
||||
text.Append("\n");
|
||||
}
|
||||
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
|
||||
if (SuppressGenerated && type.GetCustomAttributes(CGAttribute).Any())
|
||||
return string.Empty;
|
||||
@@ -192,6 +204,11 @@ namespace Il2CppInspector
|
||||
var usedMethods = new List<MethodInfo>();
|
||||
var sb = new StringBuilder();
|
||||
|
||||
var scope = new Scope {
|
||||
Current = type,
|
||||
Namespaces = namespaces
|
||||
};
|
||||
|
||||
// Fields
|
||||
sb.Clear();
|
||||
if (!type.IsEnum) {
|
||||
@@ -203,7 +220,8 @@ namespace Il2CppInspector
|
||||
sb.Append(prefix + "\t[NonSerialized]\n");
|
||||
|
||||
// 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(field.GetModifierString());
|
||||
|
||||
@@ -211,11 +229,11 @@ namespace Il2CppInspector
|
||||
if (field.GetCustomAttributes(FBAttribute).Any()) {
|
||||
if (!SuppressMetadata)
|
||||
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
|
||||
else
|
||||
sb.Append($"{field.FieldType.CSharpName} {field.Name}");
|
||||
sb.Append($"{field.FieldType.GetScopedCSharpName(scope)} {field.Name}");
|
||||
if (field.HasDefaultValue)
|
||||
sb.Append($" = {field.DefaultValueString}");
|
||||
sb.Append(";");
|
||||
@@ -234,14 +252,15 @@ namespace Il2CppInspector
|
||||
sb.Clear();
|
||||
foreach (var prop in type.DeclaredProperties) {
|
||||
// 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
|
||||
var getAccess = (prop.GetMethod?.Attributes ?? 0) & MethodAttributes.MemberAccessMask;
|
||||
var setAccess = (prop.SetMethod?.Attributes ?? 0) & MethodAttributes.MemberAccessMask;
|
||||
|
||||
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
|
||||
if ((!prop.CanRead || !prop.GetMethod.DeclaredParameters.Any()) && (!prop.CanWrite || prop.SetMethod.DeclaredParameters.Count == 1))
|
||||
@@ -249,13 +268,13 @@ namespace Il2CppInspector
|
||||
// Indexer
|
||||
else
|
||||
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)
|
||||
.ToString(inline: true, emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)
|
||||
.ToString(scope, inline: true, emitPointer: !SuppressMetadata, mustCompile: CommentAttributes)
|
||||
+ (getAccess < setAccess? prop.GetMethod.GetAccessModifierString() : "") + "get; " : "")
|
||||
+ (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; " : "") + "}");
|
||||
if (!SuppressMetadata) {
|
||||
if ((prop.CanRead && prop.GetMethod.VirtualAddress != null) || (prop.CanWrite && prop.SetMethod.VirtualAddress != null))
|
||||
@@ -274,10 +293,11 @@ namespace Il2CppInspector
|
||||
sb.Clear();
|
||||
foreach (var evt in type.DeclaredEvents) {
|
||||
// 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();
|
||||
sb.Append($"{prefix}\t{modifiers}event {evt.EventHandlerType.CSharpName} {evt.Name} {{\n");
|
||||
string modifiers = evt.AddMethod?.GetModifierString(scope);
|
||||
sb.Append($"{prefix}\t{modifiers}event {evt.EventHandlerType.GetScopedCSharpName(scope)} {evt.Name} {{\n");
|
||||
var m = new Dictionary<string, (ulong, ulong)?>();
|
||||
if (evt.AddMethod != null) m.Add("add", evt.AddMethod.VirtualAddress);
|
||||
if (evt.RemoveMethod != null) m.Add("remove", evt.RemoveMethod.VirtualAddress);
|
||||
@@ -290,16 +310,18 @@ namespace Il2CppInspector
|
||||
codeBlocks.Add("Events", sb.ToString());
|
||||
|
||||
// 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
|
||||
sb.Clear();
|
||||
foreach (var method in type.DeclaredConstructors) {
|
||||
// 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(method.GetParametersString(!SuppressMetadata) + ")" + (method.IsAbstract? ";" : @" {}"));
|
||||
sb.Append($"{prefix}\t{method.GetModifierString(scope)}{method.DeclaringType.UnmangledBaseName}{method.GetTypeParametersString(scope)}(");
|
||||
sb.Append(method.GetParametersString(scope, !SuppressMetadata) + ")" + (method.IsAbstract? ";" : @" {}"));
|
||||
sb.Append((!SuppressMetadata && method.VirtualAddress != null ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n");
|
||||
}
|
||||
codeBlocks.Add("Constructors", sb.ToString());
|
||||
@@ -307,11 +329,11 @@ namespace Il2CppInspector
|
||||
// Methods
|
||||
// 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));
|
||||
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);
|
||||
|
||||
// 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
|
||||
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
|
||||
// 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)
|
||||
.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
|
||||
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));
|
||||
if (del.RequiresUnsafeContext)
|
||||
sb.Append("unsafe ");
|
||||
sb.Append($"delegate {del.ReturnType.CSharpName} {type.CSharpTypeDeclarationName}(");
|
||||
sb.Append(del.GetParametersString(!SuppressMetadata) + ");");
|
||||
sb.Append($"delegate {del.ReturnType.GetScopedCSharpName(scope)} {type.CSharpTypeDeclarationName}(");
|
||||
sb.Append(del.GetParametersString(scope, !SuppressMetadata) + ");");
|
||||
if (!SuppressMetadata)
|
||||
sb.Append($" // TypeDefIndex: {type.Index}; {del.VirtualAddress.ToAddressString()}");
|
||||
sb.Append("\n");
|
||||
@@ -345,11 +367,11 @@ namespace Il2CppInspector
|
||||
|
||||
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)
|
||||
@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
|
||||
@base.Insert(0, type.GetEnumUnderlyingType().CSharpName);
|
||||
@base.Insert(0, type.GetEnumUnderlyingType().GetScopedCSharpName(scope));
|
||||
var baseText = @base.Count > 0 ? " : " + string.Join(", ", @base) : string.Empty;
|
||||
|
||||
sb.Append($"{type.CSharpTypeDeclarationName}{baseText}");
|
||||
@@ -359,7 +381,7 @@ namespace Il2CppInspector
|
||||
|
||||
if (type.GenericTypeParameters != null)
|
||||
foreach (var gp in type.GenericTypeParameters) {
|
||||
var constraint = gp.GetTypeConstraintsString();
|
||||
var constraint = gp.GetTypeConstraintsString(scope);
|
||||
if (constraint != string.Empty)
|
||||
sb.Append($"{prefix}\t{constraint}\n");
|
||||
}
|
||||
@@ -380,7 +402,7 @@ namespace Il2CppInspector
|
||||
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())
|
||||
return string.Empty;
|
||||
|
||||
@@ -388,20 +410,20 @@ namespace Il2CppInspector
|
||||
|
||||
// Attributes
|
||||
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
|
||||
//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")
|
||||
writer.Append($"{method.ReturnParameter.GetReturnParameterString()} {method.CSharpName}{method.GetTypeParametersString()}");
|
||||
writer.Append($"{method.ReturnParameter.GetReturnParameterString(scope)} {method.CSharpName}{method.GetTypeParametersString(scope)}");
|
||||
else
|
||||
writer.Append($"{method.CSharpName}{method.ReturnType.CSharpName}");
|
||||
writer.Append("(" + method.GetParametersString(!SuppressMetadata) + ")");
|
||||
writer.Append($"{method.CSharpName}{method.ReturnType.GetScopedCSharpName(scope)}");
|
||||
writer.Append("(" + method.GetParametersString(scope, !SuppressMetadata) + ")");
|
||||
|
||||
if (method.GenericTypeParameters != null)
|
||||
foreach (var gp in method.GenericTypeParameters) {
|
||||
var constraint = gp.GetTypeConstraintsString();
|
||||
var constraint = gp.GetTypeConstraintsString(scope);
|
||||
if (constraint != string.Empty)
|
||||
writer.Append($"\n{prefix}\t\t{constraint}");
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Il2CppInspector.Reflection
|
||||
|
||||
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())) + ")";
|
||||
}
|
||||
}
|
||||
@@ -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(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(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(TypeInfo type) => getCustomAttributes(type.Assembly, type.Definition.token, type.Definition.customAttributeIndex);
|
||||
}
|
||||
|
||||
@@ -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.Linq;
|
||||
using System.Text;
|
||||
@@ -8,8 +14,8 @@ namespace Il2CppInspector.Reflection
|
||||
public static class Extensions
|
||||
{
|
||||
// Convert a list of CustomAttributeData objects into C#-friendly attribute usages
|
||||
public static string ToString(this IEnumerable<CustomAttributeData> attributes, string linePrefix = "", string attributePrefix = "",
|
||||
bool inline = false, bool emitPointer = false, bool mustCompile = false) {
|
||||
public static string ToString(this IEnumerable<CustomAttributeData> attributes, Scope scope = null,
|
||||
string linePrefix = "", string attributePrefix = "", bool inline = false, bool emitPointer = false, bool mustCompile = false) {
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var cad in attributes) {
|
||||
@@ -20,7 +26,7 @@ namespace Il2CppInspector.Reflection
|
||||
var commentStart = mustCompile && !parameterlessConstructor? 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);
|
||||
if (suffix != -1)
|
||||
name = name[..suffix];
|
||||
|
||||
@@ -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')
|
||||
if (DeclaringType.IsInterface)
|
||||
return string.Empty;
|
||||
@@ -151,7 +151,7 @@ namespace Il2CppInspector.Reflection
|
||||
modifiers.Append("extern ");
|
||||
|
||||
// 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.NewSlot))
|
||||
modifiers.Append($"new ");
|
||||
@@ -166,13 +166,13 @@ namespace Il2CppInspector.Reflection
|
||||
}
|
||||
|
||||
// Get C# syntax-friendly list of parameters
|
||||
public string GetParametersString(bool emitPointer = false, bool commentAttributes = false)
|
||||
=> string.Join(", ", DeclaredParameters.Select(p => p.GetParameterString(emitPointer, commentAttributes)));
|
||||
public string GetParametersString(Scope usingScope, bool emitPointer = false, bool commentAttributes = false)
|
||||
=> string.Join(", ", DeclaredParameters.Select(p => p.GetParameterString(usingScope, emitPointer, commentAttributes)));
|
||||
|
||||
public string GetTypeParametersString() => GenericTypeParameters == null? "" :
|
||||
"<" + string.Join(", ", GenericTypeParameters.Select(p => p.CSharpName)) + ">";
|
||||
public string GetTypeParametersString(Scope usingScope) => GenericTypeParameters == null? "" :
|
||||
"<" + string.Join(", ", GenericTypeParameters.Select(p => p.GetScopedCSharpName(usingScope))) + ">";
|
||||
|
||||
public abstract string GetSignatureString();
|
||||
public abstract string GetSignatureString(Scope usingScope);
|
||||
|
||||
// List of operator overload metadata names
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/operator-overloads
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Il2CppInspector.Reflection
|
||||
// TODO: Generic arguments (and on ConstructorInfo)
|
||||
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())) + ")";
|
||||
}
|
||||
}
|
||||
@@ -37,15 +37,15 @@ namespace Il2CppInspector.Reflection
|
||||
public bool IsOut => (Attributes & ParameterAttributes.Out) != 0;
|
||||
public bool IsRetval => (Attributes & ParameterAttributes.Retval) != 0;
|
||||
|
||||
// The member in which the parameter is defined
|
||||
public MemberInfo Member { get; }
|
||||
// The method in which the parameter is defined
|
||||
public MethodBase DeclaringMethod { get; }
|
||||
|
||||
// Name of parameter
|
||||
public string Name { get; }
|
||||
|
||||
// Type of this parameter
|
||||
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
|
||||
public int Position { get; }
|
||||
@@ -53,7 +53,7 @@ namespace Il2CppInspector.Reflection
|
||||
// Create a parameter. Specify paramIndex == -1 for a return type parameter
|
||||
public ParameterInfo(Il2CppInspector pkg, int paramIndex, MethodBase declaringMethod) {
|
||||
Index = paramIndex;
|
||||
Member = declaringMethod;
|
||||
DeclaringMethod = declaringMethod;
|
||||
|
||||
if (paramIndex == -1) {
|
||||
Position = -1;
|
||||
@@ -96,15 +96,15 @@ namespace Il2CppInspector.Reflection
|
||||
+ (IsByRef? "ref " : "")
|
||||
+ (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 GetParameterString(bool emitPointer = false, bool compileAttributes = false) => IsRetval? null :
|
||||
(Position == 0 && Member.GetCustomAttributes("System.Runtime.CompilerServices.ExtensionAttribute").Any()? "this ":"")
|
||||
+ $"{CustomAttributes.ToString(inline: true, emitPointer: emitPointer, mustCompile: compileAttributes).Replace("[ParamArray]", "params")}"
|
||||
+ $"{getCSharpSignatureString()} {Name}"
|
||||
public string GetParameterString(Scope usingScope, bool emitPointer = false, bool compileAttributes = false) => IsRetval? null :
|
||||
(Position == 0 && DeclaringMethod.GetCustomAttributes("System.Runtime.CompilerServices.ExtensionAttribute").Any()? "this ":"")
|
||||
+ $"{CustomAttributes.ToString(usingScope, inline: true, emitPointer: emitPointer, mustCompile: compileAttributes).Replace("[ParamArray]", "params")}"
|
||||
+ $"{getCSharpSignatureString(usingScope)} {Name}"
|
||||
+ (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);
|
||||
}
|
||||
}
|
||||
20
Il2CppInspector/Reflection/Scope.cs
Normal file
20
Il2CppInspector/Reflection/Scope.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -143,6 +143,103 @@ namespace Il2CppInspector.Reflection {
|
||||
+ (IsArray? "[" + new string(',', GetArrayRank() - 1) + "]" : "")
|
||||
+ (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 int GenericParameterPosition { get; }
|
||||
@@ -607,7 +704,7 @@ namespace Il2CppInspector.Reflection {
|
||||
return modifiers.ToString();
|
||||
}
|
||||
|
||||
public string GetTypeConstraintsString() {
|
||||
public string GetTypeConstraintsString(Scope scope) {
|
||||
if (!IsGenericParameter)
|
||||
return string.Empty;
|
||||
|
||||
@@ -620,7 +717,7 @@ namespace Il2CppInspector.Reflection {
|
||||
if (DeclaringMethod == null && DeclaringType.IsNested && (DeclaringType.DeclaringType.GenericTypeParameters?.Any(p => p.Name == Name) ?? false))
|
||||
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)
|
||||
constraintList.Add("struct");
|
||||
|
||||
Reference in New Issue
Block a user