From 518441335a1f5dbeb546079b8840ac567b8cf0fb Mon Sep 17 00:00:00 2001 From: Medeni Baykal <433724+Haplois@users.noreply.github.com> Date: Mon, 15 Feb 2021 10:01:56 +0100 Subject: [PATCH] Refactored name escaping. --- TestPlatform.sln | 14 +- .../Helpers/ReflectionHelpers.cs | 2 +- .../ManagedNameHelper.Reflection.cs | 83 +++-- .../ManagedNameUtilities/ManagedNameParser.cs | 37 ++- .../ManagedNameRoundTripTests.cs | 6 +- .../ManagedNameUtilities/SpecialNameTests.cs | 2 +- test/TestAssets/CILProject/OrphanMethod.il | 304 +++++++++++++----- 7 files changed, 325 insertions(+), 123 deletions(-) diff --git a/TestPlatform.sln b/TestPlatform.sln index 321f401295..ce073a3a41 100644 --- a/TestPlatform.sln +++ b/TestPlatform.sln @@ -82,17 +82,17 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{EE49F5DC-5835-4AE3-B3BA-8BDE0AD56330}" ProjectSection(SolutionItems) = preProject scripts\build.ps1 = scripts\build.ps1 + scripts\build.sh = scripts\build.sh + scripts\PortableToFullPdb.ps1 = scripts\PortableToFullPdb.ps1 scripts\stylecop.json = scripts\stylecop.json scripts\stylecop.ruleset = scripts\stylecop.ruleset scripts\stylecop.test.ruleset = scripts\stylecop.test.ruleset scripts\test.ps1 = scripts\test.ps1 - scripts\verify-sign.ps1 = scripts\verify-sign.ps1 + scripts\test.sh = scripts\test.sh scripts\verify-nupkgs.ps1 = scripts\verify-nupkgs.ps1 - scripts\PortableToFullPdb.ps1 = scripts\PortableToFullPdb.ps1 + scripts\verify-sign.ps1 = scripts\verify-sign.ps1 scripts\vsts-prebuild.ps1 = scripts\vsts-prebuild.ps1 scripts\write-release-notes.ps1 = scripts\write-release-notes.ps1 - scripts\build.sh = scripts\build.sh - scripts\test.sh = scripts\test.sh EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E344E0A2-7715-4C7F-BAF7-D64EA94CB19B}" @@ -818,6 +818,8 @@ Global {9EFCEFB5-253E-4DE2-8A70-821D7B8189DF} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} {3A8080FB-9C93-45B9-8EB5-828DDC31FDF0} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} {BFF7714C-E5A3-4EEB-B04B-5FA47F29AD03} = {020E15EA-731F-4667-95AF-226671E0C3AE} + {0D4DF78D-7E5F-4516-B19F-E6AA71A1DBF4} = {EE49F5DC-5835-4AE3-B3BA-8BDE0AD56330} + {DED1590A-ED25-413B-8590-006A4DD5B2FD} = {EE49F5DC-5835-4AE3-B3BA-8BDE0AD56330} {E344E0A2-7715-4C7F-BAF7-D64EA94CB19B} = {EE49F5DC-5835-4AE3-B3BA-8BDE0AD56330} {DD9382B5-5EC4-4B3D-BEB7-95423731AE29} = {46250E12-4CF1-4051-B4A7-80C8C06E0068} {156F8811-28BB-4EC7-87D9-434F10FB7DBE} = {46250E12-4CF1-4051-B4A7-80C8C06E0068} @@ -829,7 +831,6 @@ Global {CAE652AF-6801-425E-AAF3-AB20DE7DF88E} = {7D4082EA-7AC9-4DFB-98E8-C5E08BDC0EC3} {FF80D706-8309-4E02-BAC0-D28B4CBCF600} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} {7D4082EA-7AC9-4DFB-98E8-C5E08BDC0EC3} = {ED0C35EB-7F31-4841-A24F-8EB708FFA959} - {DED1590A-ED25-413B-8590-006A4DD5B2FD} = {EE49F5DC-5835-4AE3-B3BA-8BDE0AD56330} {E141A226-CC0A-4F26-BD17-4AE427D81C3B} = {D27E1CB4-C641-4C6C-A140-EF5F6215AE29} {376C19DE-31E2-4FF6-88FC-0D0D6233C999} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} {3E698655-0701-482E-9AA7-F956F6337FC7} = {376C19DE-31E2-4FF6-88FC-0D0D6233C999} @@ -842,7 +843,6 @@ Global {65A25D6E-C9CC-4F45-8925-04087AC82634} = {B705537C-B82C-4A30-AFA5-6244D9A7DAEB} {D9A30E32-D466-4EC5-B4F2-62E17562279B} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} {21DB138B-85B7-479E-91FE-01E0F972EC56} = {D9A30E32-D466-4EC5-B4F2-62E17562279B} - {0D4DF78D-7E5F-4516-B19F-E6AA71A1DBF4} = {EE49F5DC-5835-4AE3-B3BA-8BDE0AD56330} {8C068694-23A2-47A2-A0DD-DB82D0AF0142} = {376C19DE-31E2-4FF6-88FC-0D0D6233C999} {24C7683D-2607-4901-B8EB-83A57E49E93D} = {376C19DE-31E2-4FF6-88FC-0D0D6233C999} {69F5FF81-5615-4F06-B83C-FCF979BB84CA} = {ED0C35EB-7F31-4841-A24F-8EB708FFA959} @@ -850,7 +850,7 @@ Global {236A71E3-01DA-4679-9DFF-16A8E079ACFF} = {5E7F18A8-F843-4C8A-AB02-4C7D9205C6CF} {41248B96-6E15-4E5E-A78F-859897676814} = {020E15EA-731F-4667-95AF-226671E0C3AE} {074F5BD6-DC05-460B-B78F-044D125FD787} = {D9A30E32-D466-4EC5-B4F2-62E17562279B} - {DCD0C39E-C78C-4A44-B0BD-7325254A2E97} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} + {DCD0C39E-C78C-4A44-B0BD-7325254A2E97} = {376C19DE-31E2-4FF6-88FC-0D0D6233C999} {2DE99835-A3A3-4922-82AD-6D10D284816D} = {7D4082EA-7AC9-4DFB-98E8-C5E08BDC0EC3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.cs b/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.cs index d1a698932b..81fb8888ee 100644 --- a/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.cs +++ b/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.cs @@ -62,7 +62,7 @@ private static int ParseEscapedStringSegment(string escapedStringSegment, int po else { stringBuilder.Append(escapedStringSegment[i + 1]); - i += 2; + i += 1; } } else if (c == '\'') diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs index e13a382036..95aa100139 100644 --- a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs +++ b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs @@ -166,8 +166,8 @@ public static MethodBase GetMethod(Assembly assembly, string managedTypeName, st MethodInfo method = null; ManagedNameParser.ParseManagedMethodName(managedMethodName, out var methodName, out var methodArity, out var parameterTypes); -#if NET20 || NET35 +#if NET20 || NET35 if (!IsNullOrWhiteSpace(methodName)) #else if (!string.IsNullOrWhiteSpace(methodName)) @@ -256,7 +256,7 @@ private static void AppendTypeString(StringBuilder b, Type type, bool closedType } else { - b.Append(type.Namespace); + AppendNamespace(b, type.Namespace); b.Append('.'); AppendNestedTypeName(b, type); @@ -268,6 +268,39 @@ private static void AppendTypeString(StringBuilder b, Type type, bool closedType } } + private static void AppendNamespace(StringBuilder b, string ns) + { + int start = 0; + bool shouldEscape = false; + + for (int i = 0; i <= ns.Length; i++) + { + if (i == ns.Length || ns[i] == '.') + { + if (start != 0) + { + b.Append('.'); + } + + var part = ns.Substring(start, i - start); + if (shouldEscape) + { + NormalizeString(b, part); + shouldEscape = false; + } + else + { + b.Append(part); + } + + start = i + 1; + continue; + } + + shouldEscape = shouldEscape || NeedsEscaping(ns[i], i - start); + } + } + private static void AppendMethodString(StringBuilder methodBuilder, string name, int methodArity) { var arityStart = name.LastIndexOf('`'); @@ -288,34 +321,42 @@ private static void AppendMethodString(StringBuilder methodBuilder, string name, if (IsNormalized(name)) { methodBuilder.Append(name); - if (arity > 0 && methodArity == arity) - { - methodBuilder.Append($"`{arity}"); - } - return; } + else + { + NormalizeString(methodBuilder, name); + } + + if (arity > 0 && methodArity == arity) + { + methodBuilder.Append($"`{arity}"); + } + } - methodBuilder.Append("'"); + private static void NormalizeString(StringBuilder b, string name) + { + b.Append("'"); for (int i = 0; i < name.Length; i++) { char c = name[i]; if (NeedsEscaping(c, i)) { - var encoded = Convert.ToString(((uint)c), 16); - methodBuilder.Append("\\u"); - methodBuilder.Append('0', 4 - encoded.Length); - methodBuilder.Append(encoded); - } - else - { - methodBuilder.Append(c); + if (c == '\\' || c == '\'') + { + // var encoded = Convert.ToString(((uint)c), 16); + // b.Append("\\u"); + // b.Append('0', 4 - encoded.Length); + // b.Append(encoded); + + b.Append("\\"); + b.Append(c); + continue; + } } + + b.Append(c); } - methodBuilder.Append("'"); - if (arity > 0 && methodArity == arity) - { - methodBuilder.Append($"`{arity}"); - } + b.Append("'"); } private static bool IsNormalized(string s) diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameParser.cs b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameParser.cs index b2705f76bb..983fb8b04e 100644 --- a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameParser.cs +++ b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameParser.cs @@ -82,10 +82,18 @@ private static string Capture(string managedMethodName, int start, int end) private static int ParseMethodName(string managedMethodName, int start, out string methodName, out int arity) { - int i = start; + var i = start; + var quoted = false; for (; i < managedMethodName.Length; i++) { - switch (managedMethodName[i]) + var c = managedMethodName[i]; + if (c == '\'' || quoted) + { + quoted = c == '\'' ? !quoted : quoted; + continue; + } + + switch (c) { case var w when char.IsWhiteSpace(w): string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i); @@ -161,10 +169,17 @@ private static int ParseParameterTypeList(string managedMethodName, int start, o private static int ParseParameterType(string managedMethodName, int start, out string parameterType) { parameterType = string.Empty; + var quoted = false; int i = start; - for (; i < managedMethodName.Length; i++) + for (i = start; i < managedMethodName.Length; i++) { + if (managedMethodName[i] == '\'' || quoted) + { + quoted = managedMethodName[i] == '\'' ? !quoted : quoted; + continue; + } + switch (managedMethodName[i]) { case '<': @@ -187,8 +202,16 @@ private static int ParseParameterType(string managedMethodName, int start, out s private static int ParseArrayBrackets(string managedMethodName, int start) { + var quoted = false; + for (int i = start; i < managedMethodName.Length; i++) { + if (managedMethodName[i] == '\'' || quoted) + { + quoted = managedMethodName[i] == '\'' ? !quoted : quoted; + continue; + } + switch (managedMethodName[i]) { case ']': @@ -205,8 +228,16 @@ private static int ParseArrayBrackets(string managedMethodName, int start) private static int ParseGenericBrackets(string managedMethodName, int start) { + var quoted = false; + for (int i = start; i < managedMethodName.Length; i++) { + if (managedMethodName[i] == '\'' || quoted) + { + quoted = managedMethodName[i] == '\'' ? !quoted : quoted; + continue; + } + switch (managedMethodName[i]) { case '<': diff --git a/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/ManagedNameRoundTripTests.cs b/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/ManagedNameRoundTripTests.cs index 44543dab83..7358db0564 100644 --- a/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/ManagedNameRoundTripTests.cs +++ b/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/ManagedNameRoundTripTests.cs @@ -451,7 +451,7 @@ public void GenericExplicitInterfaceImplementation1() containingTypeSymbol: implT, methodSymbol: implT.FindMethod("TestClasses.IImplementation.ImplMethod0"), managedTypeName: "TestClasses.Impl`1", - managedMethodName: "'TestClasses\\u002eIImplementation\\u003cT\\u003e\\u002eImplMethod0'"); // "TestClasses.IImplementation.ImplMethod0" + managedMethodName: "'TestClasses.IImplementation.ImplMethod0'"); // "TestClasses.IImplementation.ImplMethod0" } [TestMethod] @@ -464,7 +464,7 @@ public void GenericExplicitInterfaceImplementation2() containingTypeSymbol: implT, methodSymbol: implT.FindMethod("TestClasses.IImplementation.ImplMethod1"), managedTypeName: "TestClasses.Impl`1", - managedMethodName: "'TestClasses\\u002eIImplementation\\u003cT\\u003e\\u002eImplMethod1'(!0)"); // "TestClasses.IImplementation.ImplMethod1(!0)" + managedMethodName: "'TestClasses.IImplementation.ImplMethod1'(!0)"); // "TestClasses.IImplementation.ImplMethod1(!0)" } [TestMethod] @@ -477,7 +477,7 @@ public void GenericExplicitInterfaceImplementation3() containingTypeSymbol: implT, methodSymbol: implT.FindMethod("TestClasses.IImplementation.ImplMethod2"), managedTypeName: "TestClasses.Impl`1", - managedMethodName: "'TestClasses\\u002eIImplementation\\u003cT\\u003e\\u002eImplMethod2'`1(!0,!!0,System.String)"); // "TestClasses.IImplementation.ImplMethod2`1(!0,!!0,System.String)" + managedMethodName: "'TestClasses.IImplementation.ImplMethod2'`1(!0,!!0,System.String)"); // "TestClasses.IImplementation.ImplMethod2`1(!0,!!0,System.String)" } [TestMethod] diff --git a/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/SpecialNameTests.cs b/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/SpecialNameTests.cs index 5fad3a8955..3528bfaaad 100644 --- a/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/SpecialNameTests.cs +++ b/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/SpecialNameTests.cs @@ -20,7 +20,7 @@ public void VerifyThatInvalidIdentifierNamesAreParsed() var asset = environment.GetTestAsset("CILProject.dll", "net451"); var assembly = Assembly.LoadFrom(asset); var types = assembly.GetTypes(); - + foreach (var type in types) { var methods = type.GetMethods(); diff --git a/test/TestAssets/CILProject/OrphanMethod.il b/test/TestAssets/CILProject/OrphanMethod.il index 39fa0a917e..d13d1457d1 100644 --- a/test/TestAssets/CILProject/OrphanMethod.il +++ b/test/TestAssets/CILProject/OrphanMethod.il @@ -21,96 +21,97 @@ ret } -.namespace CleanNamespaceName -{ - .namespace SecondLevel +.class public CleanNamespaceName.SecondLevel.'𝐌𝐲 𝗮𝘄𝗲𝘀𝗼𝗺𝗲 𝘤𝘭𝘢𝘴𝘴 𝘢𝘯 𝒊𝒏𝒂𝒄𝒄𝒆𝒔𝒔𝒊𝒃𝒍𝒆 𝙣𝙖𝙢𝙚 😭' +{ + .method static public int32 '𝑓'(int32 '𝓧', int32 '𝓨') cil managed { - .class public '𝐌𝐲 𝗮𝘄𝗲𝘀𝗼𝗺𝗲 𝘤𝘭𝘢𝘴𝘴 𝘢𝘯 𝒊𝒏𝒂𝒄𝒄𝒆𝒔𝒔𝒊𝒃𝒍𝒆 𝙣𝙖𝙢𝙚 😭' - { - .method static public int32 '𝑓'(int32 '𝓧', int32 '𝓨') cil managed - { - .maxstack 2 - - ldarg.0 - ldarg.1 - add - ret - } - - .method static public int32 Sum(int32 '𝓧', int32 '𝓨') cil managed - { - .maxstack 2 - - ldarg.0 - ldarg.1 - add - ret - } - - .method static public int32 'Method with . in it'(int32 '𝓧', int32 '𝓨') cil managed - { - .maxstack 2 - - ldarg.0 - ldarg.1 - add - ret - } - } + .maxstack 2 + + ldarg.0 + ldarg.1 + add + ret + } + + .method static public int32 Sum(int32 '𝓧', int32 '𝓨') cil managed + { + .maxstack 2 + + ldarg.0 + ldarg.1 + add + ret + } + + .method static public int32 'Method with . in it'(int32 '𝓧', int32 '𝓨') cil managed + { + .maxstack 2 + + ldarg.0 + ldarg.1 + add + ret + } +} + +.class public CleanNamespaceName.SecondLevel.'Deeply wrong .namespace name'.'My generic class'<'Generic Type'> +{ + .field private !0 'Generic Item' + + .method public instance void .ctor() + { + .maxstack 1 + ldarg.0 + call instance void System.Object::.ctor() + + ret + } + + .method static public int32 'Method with . in it'<'Generic Type'>(!'Generic Type' '𝓧', int32 '𝓨') cil managed + { + .maxstack 2 + + ldarg.0 + ldarg.1 + add + ret + } +} + +.class public CleanNamespaceName.SecondLevel.'Deeply wrong .namespace name'.'NamespaceA.NamespaceB.ClassName`1+InnerClass`2' +{ + .method public instance int32 'MethodName`1(System.Int32,System.Int32,System.Int32)'() + { + .maxstack 1 + + ldc.i4.s 0 + ret + } + + .method public instance int32 'MethodName`2(System.Int32,System.Int32,System.Int32)'(int32 '1st parameter', int32 '2nd parameter') + { + .maxstack 1 + + ldc.i4.s 0 + ret + } + + .method public instance int32 'MethodName`3'(int32 '1st parameter', int32 '2nd parameter') + { + .maxstack 1 + + ldc.i4.s 1 + ret + } - .namespace 'Deeply wrong .namespace name' + .class nested public 'Inner Class' + { + .method public instance int32 'MethodName`3'(int32 '1st parameter', int32 '2nd parameter') { - .class public 'My generic class'<'Generic Type'> - { - .field private !0 'Generic Item' - - .method public instance void .ctor() - { - .maxstack 1 - ldarg.0 - call instance void System.Object::.ctor() - - ret - } - } - - .class public 'NamespaceA.NamespaceB.ClassName`1+InnerClass`2' - { - .method public instance int32 'MethodName`1(System.Int32,System.Int32,System.Int32)'() - { - .maxstack 1 - - ldc.i4.s 0 - ret - } - - .method public instance int32 'MethodName`2(System.Int32,System.Int32,System.Int32)'(int32 '1st parameter', int32 '2nd parameter') - { - .maxstack 1 - - ldc.i4.s 0 - ret - } - - .method public instance int32 'MethodName`3'(int32 '1st parameter', int32 '2nd parameter') - { - .maxstack 1 - - ldc.i4.s 1 - ret - } - - .class nested public 'Inner Class' - { - .method public instance int32 'MethodName`3'(int32 '1st parameter', int32 '2nd parameter') - { - .maxstack 1 - - ldc.i4.s 1 - ret - } - } - } + .maxstack 1 + + ldc.i4.s 1 + ret } } } @@ -146,4 +147,133 @@ ldc.i4.s 1 ret } + + .method public hidebysig instance void Overload0() cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(int32 i) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(int32 i, class 'NamespaceA.NamespaceB.ClassName`1+InnerClass`2' c) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(int32* p) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(object d) cil managed + { + .param [1] + .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor() = ( 01 00 00 00 ) + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(!!U u) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0() cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0() cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(!!U[] u) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(!!U[][] u) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(!!U[0...,0...] u) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(!!U[0...,0...,0...] u) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(class [mscorlib]System.Collections.Generic.List`1 l) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(class [mscorlib]System.Collections.Generic.List`1 l) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(class [mscorlib]System.Tuple`2 t0, + class [mscorlib]System.Tuple`2 t1) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(class [mscorlib]System.Tuple`1> t0) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(class [mscorlib]System.Tuple`2,class [mscorlib]System.Tuple`1> t) cil managed + { + .maxstack 1 + nop + ret + } + + .method public hidebysig instance void Overload0(class [mscorlib]System.Tuple`1> t) cil managed + { + .maxstack 1 + nop + ret + } } \ No newline at end of file