From 33621e0f828c90e7242eb6ea166ebc19cfc77ca1 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 3 Feb 2022 09:04:42 -0600 Subject: [PATCH] [Xamarin.Android.Tools.Bytecode] Improve Kotlin metadata matching. --- .../Kotlin/KotlinClassMetadata.cs | 4 +- .../Kotlin/KotlinFixups.cs | 71 ++++++++++++++++-- .../Kotlin/KotlinUtilities.cs | 40 +++++++--- .../ClassFileFixture.cs | 40 ++++++++++ .../KotlinFixupsTests.cs | 48 ++++++++++++ .../KotlinMetadataTests.cs | 25 ------ ...amarin.Android.Tools.Bytecode-Tests.csproj | 2 +- .../kotlin-ThirdParty/DeepRecursiveKt.class | Bin 0 -> 2380 bytes .../DeepRecursiveScope.class | Bin 0 -> 2779 bytes .../kotlin-ThirdParty/UByteArray.class | Bin 0 -> 7167 bytes tools/generator/generator.slnf | 1 + 11 files changed, 185 insertions(+), 46 deletions(-) create mode 100644 tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin-ThirdParty/DeepRecursiveKt.class create mode 100644 tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin-ThirdParty/DeepRecursiveScope.class create mode 100644 tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin-ThirdParty/UByteArray.class diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs index d798192f7..d983df5c4 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs @@ -358,9 +358,9 @@ public class KotlinType }; } - public string GetSignature () + public string GetSignature (bool convertUnsignedToPrimitive = true) { - return KotlinUtilities.ConvertKotlinTypeSignature (this); + return KotlinUtilities.ConvertKotlinTypeSignature (this, null, convertUnsignedToPrimitive); } } diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs index a763718f4..b4b8940de 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs @@ -175,10 +175,12 @@ static void FixupFunction (MethodInfo? method, KotlinFunction metadata, KotlinCl return; } - var java_parameters = method.GetFilteredParameters (); + (var start, var end) = CreateParameterMap (method, metadata, kotlinClass); - for (var i = 0; i < java_parameters.Length; i++) { - var java_p = java_parameters [i]; + var java_parameters = method.GetParameters (); + + for (var i = 0; i < end - start; i++) { + var java_p = java_parameters [start + i]; var kotlin_p = metadata.ValueParameters == null ? null : metadata.ValueParameters [i]; if (kotlin_p == null || kotlin_p.Type == null || kotlin_p.Name == null) continue; @@ -197,6 +199,46 @@ static void FixupFunction (MethodInfo? method, KotlinFunction metadata, KotlinCl method.KotlinReturnType = GetKotlinType (method.ReturnType.TypeSignature, metadata.ReturnType?.ClassName); } + public static (int start, int end) CreateParameterMap (MethodInfo method, KotlinFunction function, KotlinClass? kotlinClass) + { + var parameters = method.GetParameters (); + var start = 0; + var end = parameters.Length; + + // If the parameter counts are the same, that's good enough (because we know signatures matched) + if (IsValidParameterMap (method, function, start, end)) + return (start, end); + + // Remove the "hidden" receiver type parameter from the start of the parameter list + if (function.ReceiverType != null) + start++; + + if (IsValidParameterMap (method, function, start, end)) + return (start, end); + + var last_p = parameters.Last (); + + // Remove the "hidden" coroutine continuation type parameter from the end of the parameter list + // We try to restrict it to compiler generated paramteres because a user might have actually used it as a parameter + if (last_p.Type.BinaryName == "Lkotlin/coroutines/Continuation;" && (last_p.IsUnnamedParameter () || last_p.IsCompilerNamed ())) + end--; + + if (IsValidParameterMap (method, function, start, end)) + return (start, end); + + // Remove the "hidden" "this" type parameter for a static method from the start of the parameter list + // Note we do this last because sometimes it isn't there. + if (method.AccessFlags.HasFlag (MethodAccessFlags.Static)) + start++; + + if (IsValidParameterMap (method, function, start, end)) + return (start, end); + + return (0, 0); + } + + static bool IsValidParameterMap (MethodInfo method, KotlinFunction function, int start, int end) => function.ValueParameters?.Count == end - start; + static void FixupExtensionMethod (MethodInfo method) { // Kotlin "extension" methods give the first parameter an ugly name @@ -271,10 +313,23 @@ static void FixupField (FieldInfo? field, KotlinProperty metadata) static MethodInfo? FindJavaMethod (KotlinFile kotlinFile, KotlinFunction function, ClassFile klass) { - var possible_methods = klass.Methods.Where (method => method.Name == function.JvmName && - method.GetFilteredParameters ().Length == function.ValueParameters?.Count); + var possible_methods = klass.Methods.Where (method => method.Name == function.JvmName).ToArray (); + var signature = function.JvmSignature; + + // If the Descriptor/Signature match, that means all parameters and return type match + if (signature != null && possible_methods.SingleOrDefault (method => method.Descriptor == signature) is MethodInfo m) + return m; + + // Sometimes JvmSignature is null (or unhelpful), so we're going to construct one ourselves and see if they match + signature = function.ConstructJvmSignature (); - foreach (var method in possible_methods) { + if (possible_methods.SingleOrDefault (method => method.Descriptor == signature) is MethodInfo m2) + return m2; + + // If that didn't work, let's do it the hard way! + // I don't know if this catches anything additional, but it was the original code we shipped, so + // we'll keep it just in case something in the wild requires it. + foreach (var method in possible_methods.Where (method => method.GetFilteredParameters ().Length == function.ValueParameters?.Count)) { if (function.ReturnType == null) continue; if (!TypesMatch (method.ReturnType, function.ReturnType, kotlinFile)) @@ -286,6 +341,10 @@ static void FixupField (FieldInfo? field, KotlinProperty metadata) return method; } + // Theoretically this should never be hit, but who knows. At worst, it just means + // Kotlin niceties won't be applied to the method. + Log.Debug ($"Kotlin: Could not find Java method to match '{function.Name} ({function.ConstructJvmSignature ()})'"); + return null; } diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs index 8b27073fa..f177db989 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs @@ -8,7 +8,7 @@ namespace Xamarin.Android.Tools.Bytecode { public static class KotlinUtilities { - public static string ConvertKotlinTypeSignature (KotlinType? type, KotlinFile? metadata = null) + public static string ConvertKotlinTypeSignature (KotlinType? type, KotlinFile? metadata = null, bool convertUnsignedToPrimitive = true) { if (type is null) return string.Empty; @@ -27,15 +27,15 @@ public static string ConvertKotlinTypeSignature (KotlinType? type, KotlinFile? m return "Ljava/lang/Object;"; } - var result = ConvertKotlinClassToJava (class_name); + var result = ConvertKotlinClassToJava (class_name, convertUnsignedToPrimitive); if (result == "[") - result += ConvertKotlinTypeSignature (type.Arguments?.FirstOrDefault ()?.Type); + result += ConvertKotlinTypeSignature (type.Arguments?.FirstOrDefault ()?.Type, null, convertUnsignedToPrimitive); return result; } - public static string ConvertKotlinClassToJava (string? className) + public static string ConvertKotlinClassToJava (string? className, bool convertUnsignedToPrimitive = true) { if (className == null || string.IsNullOrWhiteSpace (className)) return string.Empty; @@ -45,6 +45,9 @@ public static string ConvertKotlinClassToJava (string? className) if (type_map.TryGetValue (className.TrimEnd (';'), out var result)) return result; + if (convertUnsignedToPrimitive && unsigned_type_map.TryGetValue (className.TrimEnd (';'), out var result2)) + return result2; + return "L" + className; } @@ -92,6 +95,14 @@ public static bool IsDefaultConstructorMarker (this MethodInfo method) parameters [parameters.Length - 1].Type.TypeSignature == "Lkotlin/jvm/internal/DefaultConstructorMarker;"; } + // Sometimes the Kotlin provided JvmSignature is null (or unhelpful), so we need to construct one ourselves + public static string ConstructJvmSignature (this KotlinFunction function) + { + // The receiver type (if specified) is a "hidden" parameter passed at the beginning + // of the Java parameter list, so we include it so the Signature/Descriptors match. + return $"({function.ReceiverType?.GetSignature (false)}{string.Concat (function.ValueParameters?.Select (p => p.Type?.GetSignature (false)) ?? Enumerable.Empty ())}){function.ReturnType?.GetSignature (false)}"; + } + internal static List? ToList (this IEnumerable? self, JvmNameResolver resolver, Func creator) where TResult: class { @@ -114,37 +125,42 @@ public static bool IsDefaultConstructorMarker (this MethodInfo method) public static bool IsUnnamedParameter (this ParameterInfo parameter) => parameter.Name.Length > 1 && parameter.Name.StartsWith ("p", StringComparison.Ordinal) && int.TryParse (parameter.Name.Substring (1), out var _); + public static bool IsCompilerNamed (this ParameterInfo parameter) => parameter.Name.Length > 0 && parameter.Name.StartsWith ("$", StringComparison.Ordinal); + public static bool IsUnnamedParameter (this KotlinValueParameter parameter) => parameter.Name?.Length > 1 && parameter.Name.StartsWith ("p", StringComparison.Ordinal) && int.TryParse (parameter.Name.Substring (1), out var _); + static Dictionary unsigned_type_map = new Dictionary { + { "kotlin/UInt", "I" }, + { "kotlin/ULong", "J" }, + { "kotlin/UShort", "S" }, + { "kotlin/UByte", "B" }, + { "kotlin/UIntArray", "[I" }, + { "kotlin/ULongArray", "[J" }, + { "kotlin/UShortArray", "[S" }, + { "kotlin/UByteArray", "[B" }, + }; + static Dictionary type_map = new Dictionary { { "kotlin/Int", "I" }, - { "kotlin/UInt", "I" }, { "kotlin/Double", "D" }, { "kotlin/Char", "C" }, { "kotlin/Long", "J" }, - { "kotlin/ULong", "J" }, { "kotlin/Float", "F" }, { "kotlin/Short", "S" }, - { "kotlin/UShort", "S" }, { "kotlin/Byte", "B" }, - { "kotlin/UByte", "B" }, { "kotlin/Boolean", "Z" }, { "kotlin/Unit", "V" }, { "kotlin/Array", "[" }, { "kotlin/IntArray", "[I" }, - { "kotlin/UIntArray", "[I" }, { "kotlin/DoubleArray", "[D" }, { "kotlin/CharArray", "[C" }, { "kotlin/LongArray", "[J" }, - { "kotlin/ULongArray", "[J" }, { "kotlin/FloatArray", "[F" }, { "kotlin/ShortArray", "[S" }, - { "kotlin/UShortArray", "[S" }, { "kotlin/ByteArray", "[B" }, - { "kotlin/UByteArray", "[B" }, { "kotlin/BooleanArray", "[Z" }, { "kotlin/Any", "Ljava/lang/Object;" }, diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/ClassFileFixture.cs b/tests/Xamarin.Android.Tools.Bytecode-Tests/ClassFileFixture.cs index e119fa58f..51795a60c 100644 --- a/tests/Xamarin.Android.Tools.Bytecode-Tests/ClassFileFixture.cs +++ b/tests/Xamarin.Android.Tools.Bytecode-Tests/ClassFileFixture.cs @@ -105,6 +105,46 @@ protected static void AssertXmlDeclaration (string[] classResources, string xmlR Assert.AreEqual (expected, actual.ToString ()); } + protected static KotlinMetadata GetMetadataForClass (ClassFile klass) + { + var attr = klass.Attributes.OfType ().FirstOrDefault (); + var kotlin = attr?.Annotations.FirstOrDefault (a => a.Type == "Lkotlin/Metadata;"); + + Assert.NotNull (kotlin); + + var meta = KotlinMetadata.FromAnnotation (kotlin); + + return meta; + } + + protected static KotlinClass GetClassMetadataForClass (ClassFile klass) + { + var meta = GetMetadataForClass (klass); + Assert.AreEqual (KotlinMetadataKind.Class, meta.Kind); + + return meta.AsClassMetadata (); + } + + protected static KotlinFile GetFileMetadataForClass (ClassFile klass) + { + var meta = GetMetadataForClass (klass); + Assert.AreEqual (KotlinMetadataKind.File, meta.Kind); + + return meta.AsFileMetadata (); + } + + protected static KotlinMetadata GetMetadataForClassFile (string file) + { + var c = LoadClassFile (file); + return GetMetadataForClass (c); + } + + protected static KotlinClass GetClassMetadata (string file) + { + var c = LoadClassFile (file); + return GetClassMetadataForClass (c); + } + static Stream GetResourceStream (string resource) { // Look for resources that end with our name, this allows us to diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs index 39841abe2..a4a272220 100644 --- a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs +++ b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs @@ -310,5 +310,53 @@ public void HandleKotlinNameShadowing () Assert.True (klass.Methods.Single (m => m.Name == "hitCount").AccessFlags.HasFlag (MethodAccessFlags.Public)); Assert.True (klass.Methods.Single (m => m.Name == "setType").AccessFlags.HasFlag (MethodAccessFlags.Public)); } + + [Test] + public void MatchParametersWithReceiver () + { + var klass = LoadClassFile ("DeepRecursiveKt.class"); + var meta = GetFileMetadataForClass (klass); + + var java_method = klass.Methods.Single (m => m.Name == "invoke"); + var kotlin_function = meta.Functions.Single (m => m.Name == "invoke"); + + (var start, var end) = KotlinFixups.CreateParameterMap (java_method, kotlin_function, null); + + // Start is 1 instead of 0 to skip the receiver + Assert.AreEqual (1, start); + Assert.AreEqual (2, end); + } + + [Test] + public void MatchParametersWithContinuation () + { + var klass = LoadClassFile ("DeepRecursiveScope.class"); + var meta = GetClassMetadataForClass (klass); + + var java_method = klass.Methods.Single (m => m.Name == "callRecursive" && m.GetParameters ().Count () == 2); + var kotlin_function = meta.Functions.Single (m => m.Name == "callRecursive" && m.JvmSignature.Count (c => c == ';') == 3); + + (var start, var end) = KotlinFixups.CreateParameterMap (java_method, kotlin_function, null); + + // End is 1 instead of 2 to skip the trailing continuation + Assert.AreEqual (0, start); + Assert.AreEqual (1, end); + } + + [Test] + public void MatchParametersWithStaticThis () + { + var klass = LoadClassFile ("UByteArray.class"); + var meta = GetClassMetadataForClass (klass); + + var java_method = klass.Methods.Single (m => m.Name == "contains-7apg3OU" && m.GetParameters ().Count () == 2); + var kotlin_function = meta.Functions.Single (m => m.Name == "contains"); + + (var start, var end) = KotlinFixups.CreateParameterMap (java_method, kotlin_function, null); + + // Start is 1 instead of 0 to skip the static 'this' + Assert.AreEqual (1, start); + Assert.AreEqual (2, end); + } } } diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinMetadataTests.cs b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinMetadataTests.cs index 0475dba2d..cf3e97ff1 100644 --- a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinMetadataTests.cs +++ b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinMetadataTests.cs @@ -201,31 +201,6 @@ public void KotlinExtensionMethods () Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); } - - private KotlinMetadata GetMetadataForClassFile (string file) - { - var c = LoadClassFile (file); - var attr = c.Attributes.OfType ().FirstOrDefault (); - var kotlin = attr?.Annotations.FirstOrDefault (a => a.Type == "Lkotlin/Metadata;"); - - Assert.NotNull (kotlin); - - var meta = KotlinMetadata.FromAnnotation (kotlin); - - return meta; - } - - private KotlinClass GetClassMetadata (string file) - { - var meta = GetMetadataForClassFile (file); - - Assert.AreEqual (KotlinMetadataKind.Class, meta.Kind); - - Assert.AreEqual ("1.0.3", meta.ByteCodeVersion.ToString ()); - Assert.AreEqual ("1.1.15", meta.MetadataVersion.ToString ()); - - return meta.AsClassMetadata (); - } } } diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj b/tests/Xamarin.Android.Tools.Bytecode-Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj index cb07bdc4b..3f39f0716 100644 --- a/tests/Xamarin.Android.Tools.Bytecode-Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj +++ b/tests/Xamarin.Android.Tools.Bytecode-Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj @@ -25,7 +25,7 @@ - + diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin-ThirdParty/DeepRecursiveKt.class b/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin-ThirdParty/DeepRecursiveKt.class new file mode 100644 index 0000000000000000000000000000000000000000..e115f1336fb943c5966b31566fdc66e8fc053683 GIT binary patch literal 2380 zcmbVM-BTM?6#v~#NH$9-+q9*H(k~iYLQ4~9tF=M2B|#!7)FfalDlW^FE^Kx)dpDW- zM)kq}!zUkWovDn@I1Ik|M>(Fm$wnxt5AMv~bMN`O=i_(oUw{Ah2Y_jO%`mj%`nKg{ z3!FD9+-!QnYVk!s4#uEw8!aPi8_rgCd1ITK#Eme-ET`q}aE8KoxtH;L(=mO^b@Jsm z&GVV#EQTjDwfo2Ul^(p=|GsdhR?F8a`Pn?t3~d-rhXGbC$K;Daro@i7IHl=243Vi@ zcNi{)YQ^Ub?pby2_=a8eH*IUpjcHKTflebUTr_Q($ti}!oLg@gPJrp*uvKk&x+|g{-Nu~+&HY`5Ovj1odT!IV z94;hIJMcV&RQVLV*M`R2a%Fj?M#WgKu2dfvsWOEm-j}t%hx-)1N#o#qP1AS1NsCZN zmFa=xo*kUQ2O7mikU1Gf|A_m>rr{gZC8geq(0j$?5|axU*pA#NBz$hkU5<8}Q;eP6 z-@U2r?`q09MN@RSQDg0c8fjB0IP~xDj!f&Z5oIAh7FG9mb(ILy;}a)R(Uh{F25UT* zn2zZhDX9wwKPU%3#uUAO>gG$Uk=?6-!0Wv78ccJNw*V$juGNTU34ef7(4%)RPC_DN)LQ_~55lN|tAu zVK6Lj8yr+zS<4FjHHMM516htp2QlIJ+$Q7mA@hi3@a~ZGawt6nvN_iwv^9grlT^AB zT72$vdKz6PbYJ_n!AYS5_Q~)m(vRG9YsaS`DoyfP=Ow3Q32VdV5B`0g8KQIUCQbNY znJ~W8tZ#6yW{{3d$Tban)$lBN?l2QoYs)eGrbi0tH~n_s=pz?s1>W$uY507TL94n= z&*bx#ykWTGVD(s7n4+nU20$K>LrzWjj7~A4Nuqu9nD)^riYIjXgs4JvXd-oD52t^} z(9?<67`^c;F26?lB}o)KrBzzTu!_&6ZrcLy;2NFe`Fe268toOtgWCpar;<%VKtn$X zzMwy$bYg(6A%h9hkueO=FF*EkK)#eKDcm51$XgY2EO1TWxN!I1=xi-T*m8pER1u4-pj z#gX`uQ~pE_sa$iiE2(l+PAWYs7D<-kkHgN4x?lIa?&+S<-~ayp2EZboF-%r% z*EFp366f^-*F8rV4PMmkI*)=eWV59^>-$}ZB1(wb%2uD_p1Z+NDu zl}%3kue=LpyAnyL%1pqd>tt3uOZOw_4IsdORLnnRxB8rAv5zeGZbI48R;E;>)X@AM zCX_=7?0W_g!)n-7&hX&f+huy=@AS8A8x=|-l}5ix3X+&jrE(wUXuR#G?r>dmxpW_` zaUrx_V*A5%i}$oD_H56rsC4pbnX9r?c}0EU*fq859Q_WPQ?~?Hr@QKTT6Jx;Vym=^ z>_yvB4NGvXY@U;S2tQ!|3q`N8YdvbbibZ%-z7!q zWH%!(kI(T%98;Jko3?(-5?)=>=atPmcl;h)KG1nxwoVij-u2=~oKp2~FBiNDZ1)1}KyxQ?g0w)0qX zs@$Pm#G%MLbbLy0E1%bVEyG+u=EJ;EHUoo>>!iSGQ$u$}(Gzt}r${$xN2L`FOXrXL z=Y+)32KVciZuy0|8&nQ~!{q}y5~Ic~S2K%l#Wc$I>joK&1%Sug)he2+kxZ#ILiAEG zIYi_D2395cfgyj*)2gz-@K5vba$K2E;!2FZMDvgqkq!a#*UiJ&t%>GgDjaJbCKydD zp)4x%p$E}e_~_?|k_fL&CI-$ax7lJO5uv$xxO()pwO)}sqeOE|-mh3(en84&coFmkEflu+-4rwoNT!ebUnd0%o z8&Qrq`w=H3gBv7~jAv<<}zRBZW9{2HpmUx)Q5|#-du!3)QkPsLWSQW?%tO-N~h6NS{Mg$%StP3m%j0ueU Y+Q))2D$i=1BEC)Y*R;_cfef<1f0_I1(*OVf literal 0 HcmV?d00001 diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin-ThirdParty/UByteArray.class b/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin-ThirdParty/UByteArray.class new file mode 100644 index 0000000000000000000000000000000000000000..15ba35755897aaa190f5690fd06a7a928436a328 GIT binary patch literal 7167 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 ZY<$PYe>+FidK&bP4D>(Q_-FhJ@M~ZBEBF8a literal 0 HcmV?d00001 diff --git a/tools/generator/generator.slnf b/tools/generator/generator.slnf index 577b7bf1a..e56de5ee3 100644 --- a/tools/generator/generator.slnf +++ b/tools/generator/generator.slnf @@ -15,6 +15,7 @@ "tests\\Xamarin.Android.Tools.Bytecode-Tests\\Xamarin.Android.Tools.Bytecode-Tests.csproj", "tests\\Xamarin.SourceWriter-Tests\\Xamarin.SourceWriter-Tests.csproj", "tests\\generator-Tests\\generator-Tests.csproj", + "tools\\class-parse\\class-parse.csproj", "tools\\generator\\generator.csproj" ] }