From c5a5ec0e944da3339a0e117569f9fcbecf128275 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 14 Sep 2024 11:52:15 -0700 Subject: [PATCH 1/4] Cleanup name validity Remove event generation Add missing uses of MakeValidInSource --- .../Contexts/FieldRewriteContext.cs | 8 +--- .../Contexts/MethodRewriteContext.cs | 28 +++++-------- .../Contexts/RewriteGlobalContext.cs | 5 +-- .../Extensions/StringEx.cs | 41 ++++++++++++------- .../Passes/Pass10CreateTypedefs.cs | 5 +-- .../Passes/Pass12FillTypedefs.cs | 6 +-- .../Passes/Pass80UnstripMethods.cs | 2 +- 7 files changed, 44 insertions(+), 51 deletions(-) diff --git a/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs b/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs index 73817a15..a2420e68 100644 --- a/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs +++ b/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs @@ -44,9 +44,7 @@ private string UnmangleFieldNameBase(FieldDefinition field, GeneratorOptions opt if (!field.Name.IsObfuscated(options)) { - if (!field.Name.IsInvalidInSource()) - return field.Name!; - return field.Name.FilterInvalidInSourceChars(); + return field.Name.MakeValidInSource(); } Debug.Assert(field.Signature is not null); @@ -64,9 +62,7 @@ private string UnmangleFieldName(FieldDefinition field, GeneratorOptions options if (!field.Name.IsObfuscated(options)) { - if (!field.Name.IsInvalidInSource()) - return field.Name!; - return field.Name.FilterInvalidInSourceChars(); + return field.Name.MakeValidInSource(); } if (renamedFieldCounts == null) throw new ArgumentNullException(nameof(renamedFieldCounts)); diff --git a/Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs b/Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs index 33034ada..178bfae1 100644 --- a/Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs +++ b/Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; +using AsmResolver; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; @@ -73,12 +74,9 @@ public MethodRewriteContext(TypeRewriteContext declaringType, MethodDefinition o foreach (var oldParameter in genericParams) { - var genericParameter = new GenericParameter(oldParameter.Name); - genericParameter.Attributes = oldParameter.Attributes.StripValueTypeConstraint(); - newMethod.GenericParameters.Add(genericParameter); - - if (genericParameter.Name.IsInvalidInSource()) - genericParameter.Name = genericParameter.Name.FilterInvalidInSourceChars(); + newMethod.GenericParameters.Add(new GenericParameter( + oldParameter.Name.MakeValidInSource(), + oldParameter.Attributes.StripValueTypeConstraint())); } } @@ -94,7 +92,7 @@ public MethodRewriteContext(TypeRewriteContext declaringType, MethodDefinition o declaringType.AssemblyContext.GlobalContext.MethodStartAddresses.Add(FileOffset); } - public string? UnmangledName { get; private set; } + public Utf8String? UnmangledName { get; private set; } public string? UnmangledNameWithSignature { get; private set; } public TypeDefinition? GenericInstantiationsStore { get; private set; } @@ -137,9 +135,7 @@ public void CtorPhase2() for (var index = 0; index < genericParams.Count; index++) { var oldParameter = genericParams[index]; - var genericParameter = new GenericParameter(oldParameter.Name); - if (genericParameter.Name.IsInvalidInSource()) - genericParameter.Name = genericParameter.Name.FilterInvalidInSourceChars(); + var genericParameter = new GenericParameter(oldParameter.Name.MakeValidInSource()); genericMethodInfoStoreType.GenericParameters.Add(genericParameter); selfSubstRef.TypeArguments.Add(genericParameter.ToTypeSignature()); var newParameter = NewMethod.GenericParameters[index]; @@ -204,22 +200,18 @@ private string UnmangleMethodName() if (method.Name.IsObfuscated(DeclaringType.AssemblyContext.GlobalContext.Options)) return UnmangleMethodNameWithSignature(); - if (method.Name.IsInvalidInSource()) - return method.Name.FilterInvalidInSourceChars(); - - return method.Name!; + return method.Name.MakeValidInSource(); } private string ProduceMethodSignatureBase() { var method = OriginalMethod; - var name = method.Name; + string name; if (method.Name.IsObfuscated(DeclaringType.AssemblyContext.GlobalContext.Options)) name = "Method"; - - if (name.IsInvalidInSource()) - name = name.FilterInvalidInSourceChars(); + else + name = method.Name.MakeValidInSource(); if (method.Name == "GetType" && method.Parameters.Count == 0) name = "GetIl2CppType"; diff --git a/Il2CppInterop.Generator/Contexts/RewriteGlobalContext.cs b/Il2CppInterop.Generator/Contexts/RewriteGlobalContext.cs index b38fc93c..a9f390f1 100644 --- a/Il2CppInterop.Generator/Contexts/RewriteGlobalContext.cs +++ b/Il2CppInterop.Generator/Contexts/RewriteGlobalContext.cs @@ -147,10 +147,7 @@ public TypeRewriteContext GetContextForNewType(TypeDefinition type) var paramsMethod = new MethodDefinition(newMethod.Name, newMethod.Attributes, MethodSignatureCreator.CreateMethodSignature(newMethod.Attributes, newMethod.Signature!.ReturnType, newMethod.Signature.GenericParameterCount)); foreach (var genericParameter in originalMethod.GenericParameters) { - var newGenericParameter = new GenericParameter(genericParameter.Name, genericParameter.Attributes); - - if (newGenericParameter.Name.IsInvalidInSource()) - newGenericParameter.Name = newGenericParameter.Name.FilterInvalidInSourceChars(); + var newGenericParameter = new GenericParameter(genericParameter.Name.MakeValidInSource(), genericParameter.Attributes); foreach (var constraint in genericParameter.Constraints) { diff --git a/Il2CppInterop.Generator/Extensions/StringEx.cs b/Il2CppInterop.Generator/Extensions/StringEx.cs index 9d9287e5..f0e29304 100644 --- a/Il2CppInterop.Generator/Extensions/StringEx.cs +++ b/Il2CppInterop.Generator/Extensions/StringEx.cs @@ -43,26 +43,39 @@ public static string UnSystemify(this Utf8String? str, GeneratorOptions options) return UnSystemify(str?.Value ?? "", options); } - public static string FilterInvalidInSourceChars(this string str) + public static string MakeValidInSource(this string str) { - var chars = str.ToCharArray(); - for (var i = 0; i < chars.Length; i++) + if (string.IsNullOrEmpty(str)) + return ""; + + char[]? chars = null; + for (var i = 0; i < str.Length; i++) { - var it = chars[i]; - if (!char.IsDigit(it) && !((it >= 'a' && it <= 'z') || (it >= 'A' && it <= 'Z')) && it != '_' && - it != '`') chars[i] = '_'; + var it = str[i]; + if (char.IsDigit(it) || (it is >= 'a' and <= 'z') || (it is >= 'A' and <= 'Z') || it == '_' || it == '`') + continue; + + chars ??= str.ToCharArray(); + chars[i] = '_'; } - return new string(chars); + var result = chars is null ? str : new string(chars); + return char.IsDigit(result[0]) ? "_" + result : result; } - public static string FilterInvalidInSourceChars(this Utf8String? str) + public static Utf8String MakeValidInSource(this Utf8String? str) { - return str?.Value.FilterInvalidInSourceChars() ?? ""; + if (Utf8String.IsNullOrEmpty(str)) + return Utf8String.Empty; + var result = str.Value.MakeValidInSource(); + return result == str ? str : result; } - public static bool IsInvalidInSource(this string str) + public static bool IsInvalidInSource([NotNullWhen(true)] this string? str) { + if (str is null) + return false; + for (var i = 0; i < str.Length; i++) { var it = str[i]; @@ -73,9 +86,9 @@ public static bool IsInvalidInSource(this string str) return false; } - public static bool IsInvalidInSource(this Utf8String? str) + public static bool IsInvalidInSource([NotNullWhen(true)] this Utf8String? str) { - return IsInvalidInSource(str?.Value ?? ""); + return IsInvalidInSource(str?.Value); } public static bool IsObfuscated([NotNullWhen(true)] this string? str, GeneratorOptions options) @@ -149,9 +162,9 @@ public static string GetUnmangledName(this TypeSignature typeRef, TypeDefinition else if (typeRef is GenericParameterSignature genericParameter) { if (genericParameter.ParameterType == GenericParameterType.Type) - builder.Append(declaringType!.GenericParameters[genericParameter.Index].Name); + builder.Append(declaringType!.GenericParameters[genericParameter.Index].Name.MakeValidInSource()); else - builder.Append(declaringMethod!.GenericParameters[genericParameter.Index].Name); + builder.Append(declaringMethod!.GenericParameters[genericParameter.Index].Name.MakeValidInSource()); } else { diff --git a/Il2CppInterop.Generator/Passes/Pass10CreateTypedefs.cs b/Il2CppInterop.Generator/Passes/Pass10CreateTypedefs.cs index 63cc04b1..575e111b 100644 --- a/Il2CppInterop.Generator/Passes/Pass10CreateTypedefs.cs +++ b/Il2CppInterop.Generator/Passes/Pass10CreateTypedefs.cs @@ -101,10 +101,7 @@ internal static (string? Namespace, string Name) GetConvertedTypeName( return (null, convertedTypeName); } - if (type.Name.IsInvalidInSource()) - return (null, type.Name.FilterInvalidInSourceChars()); - - return (null, type.Name!); + return (null, type.Name.MakeValidInSource()); } private static TypeAttributes AdjustAttributes(TypeAttributes typeAttributes) diff --git a/Il2CppInterop.Generator/Passes/Pass12FillTypedefs.cs b/Il2CppInterop.Generator/Passes/Pass12FillTypedefs.cs index 8c644a23..39b9f5b1 100644 --- a/Il2CppInterop.Generator/Passes/Pass12FillTypedefs.cs +++ b/Il2CppInterop.Generator/Passes/Pass12FillTypedefs.cs @@ -14,11 +14,9 @@ public static void DoPass(RewriteGlobalContext context) { foreach (var originalParameter in typeContext.OriginalType.GenericParameters) { - var newParameter = new GenericParameter(originalParameter.Name); - if (newParameter.Name.IsInvalidInSource()) - newParameter.Name = newParameter.Name.FilterInvalidInSourceChars(); + var newParameter = new GenericParameter(originalParameter.Name.MakeValidInSource(), + originalParameter.Attributes.StripValueTypeConstraint()); typeContext.NewType.GenericParameters.Add(newParameter); - newParameter.Attributes = originalParameter.Attributes.StripValueTypeConstraint(); } if (typeContext.OriginalType.IsEnum) diff --git a/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs b/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs index d42dd1dd..11165cc2 100644 --- a/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs +++ b/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs @@ -73,7 +73,7 @@ public static void DoPass(RewriteGlobalContext context) foreach (var unityMethodGenericParameter in unityMethod.GenericParameters) { - var newParameter = new GenericParameter(unityMethodGenericParameter.Name); + var newParameter = new GenericParameter(unityMethodGenericParameter.Name.MakeValidInSource()); newParameter.Attributes = unityMethodGenericParameter.Attributes; foreach (var genericParameterConstraint in unityMethodGenericParameter.Constraints) { From b44f421dae305b508771b5d9425f3c2ddca30382 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 5 Oct 2024 19:02:07 -0700 Subject: [PATCH 2/4] More efficient Utf8String filtering --- .../Extensions/StringEx.cs | 48 +++++++++++++++++-- .../Il2CppInterop.Generator.csproj | 3 ++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/Il2CppInterop.Generator/Extensions/StringEx.cs b/Il2CppInterop.Generator/Extensions/StringEx.cs index f0e29304..d30e4674 100644 --- a/Il2CppInterop.Generator/Extensions/StringEx.cs +++ b/Il2CppInterop.Generator/Extensions/StringEx.cs @@ -1,3 +1,4 @@ +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Text; using AsmResolver; @@ -52,7 +53,7 @@ public static string MakeValidInSource(this string str) for (var i = 0; i < str.Length; i++) { var it = str[i]; - if (char.IsDigit(it) || (it is >= 'a' and <= 'z') || (it is >= 'A' and <= 'Z') || it == '_' || it == '`') + if (IsValidInSource(it)) continue; chars ??= str.ToCharArray(); @@ -63,12 +64,53 @@ public static string MakeValidInSource(this string str) return char.IsDigit(result[0]) ? "_" + result : result; } + private static bool IsValidInSource(char c) => char.IsDigit(c) || (c is >= 'a' and <= 'z') || (c is >= 'A' and <= 'Z') || c == '_' || c == '`'; + public static Utf8String MakeValidInSource(this Utf8String? str) { if (Utf8String.IsNullOrEmpty(str)) return Utf8String.Empty; - var result = str.Value.MakeValidInSource(); - return result == str ? str : result; + + byte[]? rentedArray = null; + var data = str.GetBytesUnsafe(); + for (var i = 0; i < data.Length; i++) + { + var it = data[i]; + if (IsValidInSource((char)it)) + continue; + + if (rentedArray is null) + { + rentedArray ??= ArrayPool.Shared.Rent(data.Length + 1); + Array.Copy(data, 0, rentedArray, 1, data.Length); + rentedArray[0] = (byte)'_'; + } + rentedArray[i + 1] = (byte)'_'; + } + + if (char.IsDigit((char)data[0])) + { + if (rentedArray is null) + { + rentedArray = ArrayPool.Shared.Rent(data.Length + 1); + Array.Copy(data, 0, rentedArray, 1, data.Length); + rentedArray[0] = (byte)'_'; + } + + var result = new Utf8String(rentedArray, 0, data.Length + 1); + ArrayPool.Shared.Return(rentedArray); + return result; + } + else if (rentedArray is not null) + { + var result = new Utf8String(rentedArray, 1, data.Length); + ArrayPool.Shared.Return(rentedArray); + return result; + } + else + { + return str; + } } public static bool IsInvalidInSource([NotNullWhen(true)] this string? str) diff --git a/Il2CppInterop.Generator/Il2CppInterop.Generator.csproj b/Il2CppInterop.Generator/Il2CppInterop.Generator.csproj index 9eaf64a0..5e0ac19b 100644 --- a/Il2CppInterop.Generator/Il2CppInterop.Generator.csproj +++ b/Il2CppInterop.Generator/Il2CppInterop.Generator.csproj @@ -14,6 +14,9 @@ + + MonoModBackports + all runtime; build; native; contentfiles; analyzers; buildtransitive From 4d036de84e24ad10a51bdb4cfda4e991176f6586 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 5 Oct 2024 19:06:51 -0700 Subject: [PATCH 3/4] ReadOnlySpan --- Il2CppInterop.Generator/Extensions/StringEx.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Il2CppInterop.Generator/Extensions/StringEx.cs b/Il2CppInterop.Generator/Extensions/StringEx.cs index d30e4674..c6620a7c 100644 --- a/Il2CppInterop.Generator/Extensions/StringEx.cs +++ b/Il2CppInterop.Generator/Extensions/StringEx.cs @@ -72,7 +72,7 @@ public static Utf8String MakeValidInSource(this Utf8String? str) return Utf8String.Empty; byte[]? rentedArray = null; - var data = str.GetBytesUnsafe(); + ReadOnlySpan data = str.GetBytesUnsafe(); for (var i = 0; i < data.Length; i++) { var it = data[i]; @@ -82,7 +82,7 @@ public static Utf8String MakeValidInSource(this Utf8String? str) if (rentedArray is null) { rentedArray ??= ArrayPool.Shared.Rent(data.Length + 1); - Array.Copy(data, 0, rentedArray, 1, data.Length); + data.CopyTo(rentedArray.AsSpan(1)); rentedArray[0] = (byte)'_'; } rentedArray[i + 1] = (byte)'_'; @@ -93,7 +93,7 @@ public static Utf8String MakeValidInSource(this Utf8String? str) if (rentedArray is null) { rentedArray = ArrayPool.Shared.Rent(data.Length + 1); - Array.Copy(data, 0, rentedArray, 1, data.Length); + data.CopyTo(rentedArray.AsSpan(1)); rentedArray[0] = (byte)'_'; } From b58848e244c82cbe7b385faf1949738a48ae3983 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 5 Oct 2024 19:41:15 -0700 Subject: [PATCH 4/4] cleaner implementation --- .../Extensions/StringEx.cs | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/Il2CppInterop.Generator/Extensions/StringEx.cs b/Il2CppInterop.Generator/Extensions/StringEx.cs index c6620a7c..39769153 100644 --- a/Il2CppInterop.Generator/Extensions/StringEx.cs +++ b/Il2CppInterop.Generator/Extensions/StringEx.cs @@ -71,39 +71,38 @@ public static Utf8String MakeValidInSource(this Utf8String? str) if (Utf8String.IsNullOrEmpty(str)) return Utf8String.Empty; - byte[]? rentedArray = null; ReadOnlySpan data = str.GetBytesUnsafe(); + + var length = data.Length; + byte[]? rentedArray = null; + Span rentedArraySpan = default; + + if (char.IsDigit((char)data[0])) + { + length++; + rentedArray = ArrayPool.Shared.Rent(length); + rentedArray[0] = (byte)'_'; + rentedArraySpan = rentedArray.AsSpan(1); + data.CopyTo(rentedArraySpan); + } + for (var i = 0; i < data.Length; i++) { - var it = data[i]; - if (IsValidInSource((char)it)) + if (IsValidInSource((char)data[i])) continue; if (rentedArray is null) { - rentedArray ??= ArrayPool.Shared.Rent(data.Length + 1); - data.CopyTo(rentedArray.AsSpan(1)); - rentedArray[0] = (byte)'_'; + rentedArray = ArrayPool.Shared.Rent(length); + rentedArraySpan = rentedArray.AsSpan(); + data.CopyTo(rentedArraySpan); } - rentedArray[i + 1] = (byte)'_'; + rentedArraySpan[i] = (byte)'_'; } - if (char.IsDigit((char)data[0])) - { - if (rentedArray is null) - { - rentedArray = ArrayPool.Shared.Rent(data.Length + 1); - data.CopyTo(rentedArray.AsSpan(1)); - rentedArray[0] = (byte)'_'; - } - - var result = new Utf8String(rentedArray, 0, data.Length + 1); - ArrayPool.Shared.Return(rentedArray); - return result; - } - else if (rentedArray is not null) + if (rentedArray is not null) { - var result = new Utf8String(rentedArray, 1, data.Length); + var result = new Utf8String(rentedArray, 0, length); ArrayPool.Shared.Return(rentedArray); return result; }