From 98d1f7ae89fca2202e9dfd6529d50e44cbade90e Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 17 Oct 2019 09:46:08 -0500 Subject: [PATCH 1/5] Add Kotlin support to our binding process. --- .../ClassPath.cs | 3 +- .../Kotlin/KotlinClassMetadata.cs | 203 +++++++++++++--- .../Kotlin/KotlinFixups.cs | 217 +++++++++++++++++- .../Kotlin/KotlinProtobufDefinition.cs | 32 +-- .../Kotlin/KotlinUtilities.cs | 172 ++++++++++++++ src/Xamarin.Android.Tools.Bytecode/Methods.cs | 4 +- .../XmlApiImporter.cs | 21 +- .../Tests/Unit-Tests/XmlApiImporterTests.cs | 18 ++ 8 files changed, 613 insertions(+), 57 deletions(-) create mode 100644 src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs diff --git a/src/Xamarin.Android.Tools.Bytecode/ClassPath.cs b/src/Xamarin.Android.Tools.Bytecode/ClassPath.cs index 0aee8a5d5..62adb9a5d 100644 --- a/src/Xamarin.Android.Tools.Bytecode/ClassPath.cs +++ b/src/Xamarin.Android.Tools.Bytecode/ClassPath.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; @@ -8,7 +8,6 @@ using System.Xml.Linq; using System.Xml.XPath; using System.Text; -using Xamarin.Android.Tools.Bytecode.Kotlin; namespace Xamarin.Android.Tools.Bytecode { diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs index 1f7944df3..cd9395a80 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using org.jetbrains.kotlin.metadata.jvm; using Type = org.jetbrains.kotlin.metadata.jvm.Type; @@ -48,23 +49,34 @@ internal static KotlinClass FromProtobuf (Class c, JvmNameResolver resolver) } } - public class KotlinConstructor + public class KotlinMethodBase { - public int Flags { get; set; } - public List ValueParameters { get; set; } public int [] VersionRequirements { get; set; } + public virtual string GetSignature () => string.Empty; + } + + public class KotlinConstructor : KotlinMethodBase + { + public KotlinConstructorFlags Flags { get; set; } + public List ValueParameters { get; set; } + internal static KotlinConstructor FromProtobuf (Constructor c, JvmNameResolver resolver) { if (c is null) return null; return new KotlinConstructor { - Flags = c.Flags, + Flags = (KotlinConstructorFlags)c.Flags, ValueParameters = c.ValueParameters?.Select (vp => KotlinValueParameter.FromProtobuf (vp, resolver)).ToList (), VersionRequirements = c.VersionRequirements }; } + + public override string GetSignature () + { + return $"({ValueParameters.GetSignature ()})V"; + } } public class KotlinAnnotation @@ -113,7 +125,7 @@ public class KotlinAnnotationArgumentValue public KotlinAnnotation Annotation { get; set; } public List ArrayElements { get; set; } public int ArrayDimensionCount { get; set; } - public int Flags { get; set; } + public KotlinAnnotationFlags Flags { get; set; } internal static KotlinAnnotationArgumentValue FromProtobuf (org.jetbrains.kotlin.metadata.jvm.Annotation.Argument.Value value, JvmNameResolver resolver) { @@ -131,7 +143,7 @@ internal static KotlinAnnotationArgumentValue FromProtobuf (org.jetbrains.kotlin Annotation = KotlinAnnotation.FromProtobuf (value.Annotation, resolver), ArrayDimensionCount = value.ArrayDimensionCount, ArrayElements = value.ArrayElements?.Select (vp => KotlinAnnotationArgumentValue.FromProtobuf (vp, resolver)).ToList (), - Flags = value.Flags + Flags = (KotlinAnnotationFlags)value.Flags }; } } @@ -159,7 +171,7 @@ internal static KotlinEffect FromProtobuf (Effect ef, JvmNameResolver resolver) public class KotlinExpression { - public int Flags { get; set; } + public KotlinExpressionFlags Flags { get; set; } public int ValueParameterReference { get; set; } public KotlinConstantValue ConstantValue { get; set; } public KotlinType IsInstanceType { get; set; } @@ -173,7 +185,7 @@ internal static KotlinExpression FromProtobuf (Expression exp, JvmNameResolver r return null; return new KotlinExpression { - Flags = exp.Flags, + Flags = (KotlinExpressionFlags)exp.Flags, ValueParameterReference = exp.ValueParameterReference, ConstantValue = (KotlinConstantValue) exp.constant_value, IsInstanceType = KotlinType.FromProtobuf (exp.IsInstanceType, resolver), @@ -184,19 +196,18 @@ internal static KotlinExpression FromProtobuf (Expression exp, JvmNameResolver r } } - public class KotlinFunction + public class KotlinFunction : KotlinMethodBase { - public int Flags { get; set; } public string Name { get; set; } + public KotlinFunctionFlags Flags { get; set; } public KotlinType ReturnType { get; set; } public int ReturnTypeId { get; set; } public List TypeParameters { get; set; } public KotlinType ReceiverType { get; set; } public int ReceiverTypeId { get; set; } - public List ValueParameters { get; set; } public KotlinTypeTable TypeTable { get; set; } - public int [] VersionRequirements { get; set; } public KotlinContract Contract { get; set; } + public List ValueParameters { get; set; } internal static KotlinFunction FromProtobuf (Function f, JvmNameResolver resolver) { @@ -204,7 +215,7 @@ internal static KotlinFunction FromProtobuf (Function f, JvmNameResolver resolve return null; return new KotlinFunction { - Flags = f.Flags, + Flags = (KotlinFunctionFlags)f.Flags, Name = resolver.GetString (f.Name), ReturnType = KotlinType.FromProtobuf (f.ReturnType, resolver), ReturnTypeId = f.ReturnTypeId, @@ -215,6 +226,8 @@ internal static KotlinFunction FromProtobuf (Function f, JvmNameResolver resolve VersionRequirements = f.VersionRequirements }; } + + public override string ToString () => Name; } public class KotlinContract @@ -229,10 +242,10 @@ internal static KotlinContract FromProtobuf (Contract c, JvmNameResolver resolve } } - public class KotlinProperty + public class KotlinProperty : KotlinMethodBase { - public int Flags { get; set; } public string Name { get; set; } + public KotlinPropertyFlags Flags { get; set; } public KotlinType ReturnType { get; set; } public int ReturnTypeId { get; set; } public List TypeParameters { get; set; } @@ -241,7 +254,6 @@ public class KotlinProperty public KotlinValueParameter SetterValueParameter { get; set; } public int GetterFlags { get; set; } public int SetterFlags { get; set; } - public int [] VersionRequirements { get; set; } internal static KotlinProperty FromProtobuf (Property p, JvmNameResolver resolver) { @@ -249,7 +261,7 @@ internal static KotlinProperty FromProtobuf (Property p, JvmNameResolver resolve return null; return new KotlinProperty { - Flags = p.Flags, + Flags = (KotlinPropertyFlags)p.Flags, Name = resolver.GetString (p.Name), ReturnTypeId = p.ReturnTypeId, ReturnType = KotlinType.FromProtobuf (p.ReturnType, resolver), @@ -268,18 +280,18 @@ public class KotlinType { public List Arguments { get; set; } public bool Nullable { get; set; } - public int FlexibleTypeCapabilitiesId { get; set; } + public int? FlexibleTypeCapabilitiesId { get; set; } public KotlinType FlexibleUpperBound { get; set; } public int FlexibleUpperBoundId { get; set; } public string ClassName { get; set; } - public int TypeParameter { get; set; } + public int? TypeParameter { get; set; } public string TypeParameterName { get; set; } public string TypeAliasName { get; set; } public KotlinType OuterType { get; set; } - public int OuterTypeId { get; set; } + public int? OuterTypeId { get; set; } public KotlinType AbbreviatedType { get; set; } - public int AbbreviatedTypeId { get; set; } - public int Flags { get; set; } + public int? AbbreviatedTypeId { get; set; } + public KotlinTypeFlags Flags { get; set; } internal static KotlinType FromProtobuf (Type t, JvmNameResolver resolver) { @@ -291,16 +303,21 @@ internal static KotlinType FromProtobuf (Type t, JvmNameResolver resolver) Nullable = t.Nullable, FlexibleTypeCapabilitiesId = t.FlexibleTypeCapabilitiesId, FlexibleUpperBound = FromProtobuf (t.FlexibleUpperBound, resolver), - ClassName = t.ClassName > 0 ? resolver.GetString (t.ClassName) : null, + ClassName = t.ClassName >= 0 ? resolver.GetString (t.ClassName.Value) : null, TypeParameter = t.TypeParameter, - TypeParameterName = t.TypeParameterName > 0 ? resolver.GetString (t.TypeParameterName) : null, + TypeParameterName = t.TypeParameterName >= 0 ? resolver.GetString (t.TypeParameterName.GetValueOrDefault ()) : null, OuterType = FromProtobuf (t.OuterType, resolver), OuterTypeId = t.OuterTypeId, AbbreviatedType = FromProtobuf (t.AbbreviatedType, resolver), AbbreviatedTypeId = t.AbbreviatedTypeId, - Flags = t.Flags + Flags = (KotlinTypeFlags)t.Flags }; } + + public string GetSignature () + { + return KotlinUtilities.ConvertKotlinTypeSignature (this); + } } public class KotlinTypeAlias @@ -436,7 +453,7 @@ internal static KotlinVersionRequirementTable FromProtobuf (VersionRequirementTa public class KotlinValueParameter { - public int Flags { get; set; } + public KotlinParameterFlags Flags { get; set; } public string Name { get; set; } public KotlinType Type { get; set; } public int TypeId { get; set; } @@ -449,7 +466,7 @@ internal static KotlinValueParameter FromProtobuf (ValueParameter vp, JvmNameRes return null; return new KotlinValueParameter { - Flags = vp.Flags, + Flags = (KotlinParameterFlags)vp.Flags, Name = resolver.GetString (vp.Name), Type = KotlinType.FromProtobuf (vp.Type, resolver), TypeId = vp.TypeId, @@ -457,6 +474,8 @@ internal static KotlinValueParameter FromProtobuf (ValueParameter vp, JvmNameRes VarArgElementTypeId = vp.VarargElementTypeId }; } + + public string GetSignature () => Type.GetSignature (); } public enum KotlinVariance @@ -557,4 +576,134 @@ public enum KotlinClassFlags IsExpectClass = 0b_01000_000_00_000_0, IsInlineClass = 0b_10000_000_00_000_0 } + + [Flags] + public enum KotlinConstructorFlags + { + HasAnnotations = 0b0_000_1, + + Internal = 0b0_000_0, + Private = 0b0_001_0, + Protected = 0b0_010_0, + Public = 0b0_011_0, + PrivateToThis = 0b0_100_0, + Local = 0b0_101_0, + + IsSecondary = 0b1_000_0 + } + + [Flags] + public enum KotlinFunctionFlags + { + HasAnnotations = 0b00_00_000_1, + + Internal = 0b00_00_000_0, + Private = 0b00_00_001_0, + Protected = 0b00_00_010_0, + Public = 0b00_00_011_0, + PrivateToThis = 0b00_00_100_0, + Local = 0b00_00_101_0, + + Final = 0b00_00_000_0, + Open = 0b00_01_000_0, + Abstract = 0b00_10_000_0, + Sealed = 0b00_11_000_0, + + Declaration = 0b00_00_000_0, + FakeOverride = 0b01_00_000_0, + Delegation = 0b10_00_000_0, + Synthesized = 0b11_00_000_0, + + IsOperator = 0b_0000001_000_00_000_0, + IsInfix = 0b_0000010_000_00_000_0, + IsInline = 0b_0000100_000_00_000_0, + IsTailrec = 0b_0001000_000_00_000_0, + IsExternalFunction = 0b_0010000_000_00_000_0, + IsSuspend = 0b_0100000_000_00_000_0, + IsExpectFunction = 0b_1000000_000_00_000_0 + } + + [Flags] + public enum KotlinPropertyFlags + { + HasAnnotations = 0b00_00_000_1, + + Internal = 0b00_00_000_0, + Private = 0b00_00_001_0, + Protected = 0b00_00_010_0, + Public = 0b00_00_011_0, + PrivateToThis = 0b00_00_100_0, + Local = 0b00_00_101_0, + + Final = 0b00_00_000_0, + Open = 0b00_01_000_0, + Abstract = 0b00_10_000_0, + Sealed = 0b00_11_000_0, + + Declaration = 0b00_00_000_0, + FakeOverride = 0b01_00_000_0, + Delegation = 0b10_00_000_0, + Synthesized = 0b11_00_000_0, + + IsVar = 0b_000000001_000_00_000_0, + HasGetter = 0b_000000010_000_00_000_0, + HasSetter = 0b_000000100_000_00_000_0, + IsConst = 0b_000001000_000_00_000_0, + IsLateInit = 0b_000010000_000_00_000_0, + HasConstant = 0b_000100000_000_00_000_0, + IsExternalProperty = 0b_001000000_000_00_000_0, + IsDelegated = 0b_010000000_000_00_000_0, + IsExpectProperty = 0b_100000000_000_00_000_0 + } + + [Flags] + public enum KotlinParameterFlags + { + HasAnnotations = 0b000_1, + + DeclaresDefaultValue = 0b001_0, + IsCrossInline = 0b010_0, + IsNoInline = 0b100_0 + } + + [Flags] + public enum KotlinAccessorFlags + { + HasAnnotations = 0b00_00_000_1, + + Internal = 0b00_00_000_0, + Private = 0b00_00_001_0, + Protected = 0b00_00_010_0, + Public = 0b00_00_011_0, + PrivateToThis = 0b00_00_100_0, + Local = 0b00_00_101_0, + + Final = 0b00_00_000_0, + Open = 0b00_01_000_0, + Abstract = 0b00_10_000_0, + Sealed = 0b00_11_000_0, + + IsNotDefault = 0b001_00_000_0, + IsExternalAccessor = 0b010_00_000_0, + IsInlineAccessor = 0b100_00_000_0 + } + + [Flags] + public enum KotlinExpressionFlags + { + IsNegated = 0b01, + IsNullCheckPredicate = 0b10 + } + + [Flags] + public enum KotlinAnnotationFlags + { + IsUnsigned = 0b01 + } + + [Flags] + public enum KotlinTypeFlags + { + SuspendType = 0b01 + } } diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs index 1165a6f5a..03d3826aa 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Xamarin.Android.Tools.Bytecode.Kotlin +namespace Xamarin.Android.Tools.Bytecode { static class KotlinFixups { @@ -25,10 +26,25 @@ public static void Fixup (IList classes) var class_metadata = km.AsClassMetadata (); FixupClassVisibility (c, class_metadata); + + if (class_metadata.Flags.HasFlag (KotlinClassFlags.EnumClass)) + continue; + + foreach (var con in class_metadata.Constructors) + FixupConstructor (FindJavaConstructor (class_metadata, con, c), con); + + foreach (var met in class_metadata.Functions) + FixupFunction (FindJavaMethod (class_metadata, met, c), met, class_metadata); + + foreach (var prop in class_metadata.Properties) { + var getter = FindJavaPropertyGetter (class_metadata, prop, c); + var setter = FindJavaPropertySetter (class_metadata, prop, c); + + FixupProperty (getter, setter, prop); + } } else { // We don't have explicit support for other types of Kotlin constructs yet, - // so they are unlikely to work. Mark them as private and consumers - // can override that if they want to fix them up. + // so they are unlikely to work. Mark them as private so they don't get bound. c.AccessFlags = ClassAccessFlags.Private; } } catch (Exception ex) { @@ -39,15 +55,198 @@ public static void Fixup (IList classes) static void FixupClassVisibility (ClassFile klass, KotlinClass metadata) { - // We don't have explicit support for these types of Kotlin constructs yet, - // so they are unlikely to work. Mark them as private and consumers - // can override that if they want to fix them up. - if (metadata.Flags.HasFlag (KotlinClassFlags.AnnotationClass) || metadata.Flags.HasFlag (KotlinClassFlags.CompanionObject) || metadata.Flags.HasFlag (KotlinClassFlags.Object)) + // Hide class if it isn't Public/Protected + if (klass.AccessFlags.IsPubliclyVisible () && !metadata.Flags.IsPubliclyVisible ()) { + Log.Debug ($"Kotlin: Hiding internal class {klass.ThisClass.Name.Value}"); klass.AccessFlags = ClassAccessFlags.Private; + return; + } - // Hide class if it isn't Public/Protected - if (!metadata.Flags.HasFlag (KotlinClassFlags.Public) && !metadata.Flags.HasFlag (KotlinClassFlags.Protected)) + // We don't have explicit support for these types of Kotlin constructs yet, + // so they are unlikely to work. Mark them as private so they don't get bound. + if (metadata.Flags.HasFlag (KotlinClassFlags.AnnotationClass) || metadata.Flags.HasFlag (KotlinClassFlags.CompanionObject) || metadata.Flags.HasFlag (KotlinClassFlags.Object)) { + Log.Debug ($"Kotlin: Hiding unsupported class {klass.ThisClass.Name.Value} ({metadata.Flags})"); klass.AccessFlags = ClassAccessFlags.Private; + } + + foreach (var method in klass.Methods) + if (method.Name.Contains ("-impl")) { + Log.Debug ($"Kotlin: Hiding implementation method {method.DeclaringType?.ThisClass.Name.Value} - {method.Name}"); + method.AccessFlags = MethodAccessFlags.Private; + } else if (method.Name.Contains ("-deprecated")) { + Log.Debug ($"Kotlin: Hiding deprecated method {method.DeclaringType?.ThisClass.Name.Value} - {method.Name}"); + method.AccessFlags = MethodAccessFlags.Private; + } else if (method.AccessFlags.HasFlag (MethodAccessFlags.Static) && method.GetParameters ().FirstOrDefault ()?.Name == "$this") { + Log.Debug ($"Kotlin: Hiding extension method {method.DeclaringType?.ThisClass.Name.Value} - {method.Name}"); + method.AccessFlags = MethodAccessFlags.Private; + } + } + + static void FixupConstructor (MethodInfo method, KotlinConstructor metadata) + { + if (method is null) + return; + + // Hide constructor if it isn't Public/Protected + if (method.IsPubliclyVisible && !metadata.Flags.IsPubliclyVisible ()) { + Log.Debug ($"Kotlin: Hiding internal constructor {method.DeclaringType?.ThisClass.Name.Value} - {metadata.GetSignature ()}"); + method.AccessFlags = MethodAccessFlags.Private; + } + + } + + static void FixupFunction (MethodInfo method, KotlinFunction metadata, KotlinClass kotlinClass) + { + if (method is null) + return; + + // Hide function if it isn't Public/Protected + if (method.IsPubliclyVisible && !metadata.Flags.IsPubliclyVisible ()) { + Log.Debug ($"Kotlin: Hiding internal method {method.DeclaringType?.ThisClass.Name.Value} - {metadata.GetSignature ()}"); + method.AccessFlags = MethodAccessFlags.Private; + return; + } + + var java_parameters = method.GetFilteredParameters (); + + for (var i = 0; i < java_parameters.Length; i++) { + var java_p = java_parameters [i]; + var kotlin_p = metadata.ValueParameters [i]; + + if (TypesMatch (java_p.Type, kotlin_p.Type, kotlinClass) && java_p.IsUnnamedParameter () && !kotlin_p.IsUnnamedParameter ()) { + Log.Debug ($"Kotlin: Renaming parameter {method.DeclaringType?.ThisClass.Name.Value} - {method.Name} - {java_p.Name} -> {kotlin_p.Name}"); + java_p.Name = kotlin_p.Name; + } + } + } + + static void FixupProperty (MethodInfo getter, MethodInfo setter, KotlinProperty metadata) + { + if (getter is null && setter is null) + return; + + // Hide property if it isn't Public/Protected + if (!metadata.Flags.IsPubliclyVisible ()) { + + if (getter?.IsPubliclyVisible == true) { + Log.Debug ($"Kotlin: Hiding internal getter method {getter.DeclaringType?.ThisClass.Name.Value} - {getter.Name}"); + getter.AccessFlags = MethodAccessFlags.Private; + } + + if (setter?.IsPubliclyVisible == true) { + Log.Debug ($"Kotlin: Hiding internal setter method {setter.DeclaringType?.ThisClass.Name.Value} - {setter.Name}"); + setter.AccessFlags = MethodAccessFlags.Private; + } + + return; + } + + if (setter != null) { + var setter_parameter = setter.GetParameters () [0]; + + if (setter_parameter.IsUnnamedParameter () && !metadata.SetterValueParameter.IsUnnamedParameter ()) { + Log.Debug ($"Kotlin: Renaming setter parameter {setter.DeclaringType?.ThisClass.Name.Value} - {setter.Name} - {setter_parameter.Name} -> {metadata.SetterValueParameter.Name}"); + setter_parameter.Name = metadata.SetterValueParameter.Name; + } + } + } + + static MethodInfo FindJavaConstructor (KotlinClass kotlinClass, KotlinConstructor constructor, ClassFile klass) + { + var all_constructors = klass.Methods.Where (method => method.Name == "" || method.Name == ""); + var possible_constructors = all_constructors.Where (method => method.GetFilteredParameters ().Length == constructor.ValueParameters.Count); + + foreach (var method in possible_constructors) { + if (ParametersMatch (kotlinClass, method, constructor.ValueParameters)) + return method; + } + + return null; + } + + static MethodInfo FindJavaMethod (KotlinClass kotlinClass, KotlinFunction function, ClassFile klass) + { + var possible_methods = klass.Methods.Where (method => method.GetMethodNameWithoutSuffix () == function.Name && + method.GetFilteredParameters ().Length == function.ValueParameters.Count); + + foreach (var method in possible_methods) { + if (!TypesMatch (method.ReturnType, function.ReturnType, kotlinClass)) + continue; + + if (!ParametersMatch (kotlinClass, method, function.ValueParameters)) + continue; + + return method; + } + + return null; + } + + static MethodInfo FindJavaPropertyGetter (KotlinClass kotlinClass, KotlinProperty property, ClassFile klass) + { + var possible_methods = klass.Methods.Where (method => (string.Compare (method.GetMethodNameWithoutSuffix (), $"get{property.Name}", true) == 0 || + string.Compare (method.GetMethodNameWithoutSuffix (), property.Name, true) == 0) && + method.GetParameters ().Length == 0 && + TypesMatch (method.ReturnType, property.ReturnType, kotlinClass)); + + return possible_methods.FirstOrDefault (); + } + + static MethodInfo FindJavaPropertySetter (KotlinClass kotlinClass, KotlinProperty property, ClassFile klass) + { + var possible_methods = klass.Methods.Where (method => string.Compare (method.GetMethodNameWithoutSuffix (), $"set{property.Name}", true) == 0 && + property.SetterValueParameter != null && + method.GetParameters ().Length == 1 && + TypesMatch (method.GetParameters () [0].Type, property.SetterValueParameter.Type, kotlinClass)); + + return possible_methods.FirstOrDefault (); + } + + static bool ParametersMatch (KotlinClass kotlinClass, MethodInfo method, List kotlinParameters) + { + var java_parameters = method.GetFilteredParameters (); + + if (java_parameters.Length == 0 && kotlinParameters.Count == 0) + return true; + + for (var i = 0; i < java_parameters.Length; i++) { + var java_p = java_parameters [i]; + var kotlin_p = kotlinParameters [i]; + + if (!TypesMatch (java_p.Type, kotlin_p.Type, kotlinClass)) + return false; + } + + return true; + } + + static bool TypesMatch (TypeInfo javaType, KotlinType kotlinType, KotlinClass kotlinClass) + { + // Generic type + if (!string.IsNullOrWhiteSpace (kotlinType.TypeParameterName) && $"T{kotlinType.TypeParameterName};" == javaType.TypeSignature) + return true; + + if (javaType.BinaryName == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinClass)) + return true; + + // Could be a generic type erasure + if (javaType.BinaryName == "Ljava/lang/Object;") + return true; + + // Sometimes Kotlin keeps its native types rather than converting them to Java native types + // ie: "Lkotlin/UShort;" instead of "S" + if (javaType.BinaryName.StartsWith ("L", StringComparison.Ordinal) && javaType.BinaryName.EndsWith (";", StringComparison.Ordinal)) { + if (KotlinUtilities.ConvertKotlinClassToJava (javaType.BinaryName.Substring (1, javaType.BinaryName.Length - 2)) == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinClass)) + return true; + } + + // Same for some arrays + if (javaType.BinaryName.StartsWith ("[L", StringComparison.Ordinal) && javaType.BinaryName.EndsWith (";", StringComparison.Ordinal)) { + if ("[" + KotlinUtilities.ConvertKotlinClassToJava (javaType.BinaryName.Substring (2, javaType.BinaryName.Length - 3)) == KotlinUtilities.ConvertKotlinTypeSignature (kotlinType, kotlinClass)) + return true; + } + + return false; } } } diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinProtobufDefinition.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinProtobufDefinition.cs index 16aa67176..ef91d97b0 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinProtobufDefinition.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinProtobufDefinition.cs @@ -254,8 +254,8 @@ public bool Nullable { private bool? __pbn__Nullable; [global::ProtoBuf.ProtoMember (4, Name = @"flexible_type_capabilities_id")] - public int FlexibleTypeCapabilitiesId { - get { return __pbn__FlexibleTypeCapabilitiesId.GetValueOrDefault (); } + public int? FlexibleTypeCapabilitiesId { + get { return __pbn__FlexibleTypeCapabilitiesId; } set { __pbn__FlexibleTypeCapabilitiesId = value; } } public bool ShouldSerializeFlexibleTypeCapabilitiesId () => __pbn__FlexibleTypeCapabilitiesId != null; @@ -266,8 +266,8 @@ public int FlexibleTypeCapabilitiesId { public Type FlexibleUpperBound { get; set; } [global::ProtoBuf.ProtoMember (8, Name = @"flexible_upper_bound_id")] - public int FlexibleUpperBoundId { - get { return __pbn__FlexibleUpperBoundId.GetValueOrDefault (); } + public int? FlexibleUpperBoundId { + get { return __pbn__FlexibleUpperBoundId; } set { __pbn__FlexibleUpperBoundId = value; } } public bool ShouldSerializeFlexibleUpperBoundId () => __pbn__FlexibleUpperBoundId != null; @@ -275,8 +275,8 @@ public int FlexibleUpperBoundId { private int? __pbn__FlexibleUpperBoundId; [global::ProtoBuf.ProtoMember (6, Name = @"class_name")] - public int ClassName { - get { return __pbn__ClassName.GetValueOrDefault (); } + public int? ClassName { + get { return __pbn__ClassName; } set { __pbn__ClassName = value; } } public bool ShouldSerializeClassName () => __pbn__ClassName != null; @@ -284,8 +284,8 @@ public int ClassName { private int? __pbn__ClassName; [global::ProtoBuf.ProtoMember (7, Name = @"type_parameter")] - public int TypeParameter { - get { return __pbn__TypeParameter.GetValueOrDefault (); } + public int? TypeParameter { + get { return __pbn__TypeParameter; } set { __pbn__TypeParameter = value; } } public bool ShouldSerializeTypeParameter () => __pbn__TypeParameter != null; @@ -293,8 +293,8 @@ public int TypeParameter { private int? __pbn__TypeParameter; [global::ProtoBuf.ProtoMember (9, Name = @"type_parameter_name")] - public int TypeParameterName { - get { return __pbn__TypeParameterName.GetValueOrDefault (); } + public int? TypeParameterName { + get { return __pbn__TypeParameterName; } set { __pbn__TypeParameterName = value; } } public bool ShouldSerializeTypeParameterName () => __pbn__TypeParameterName != null; @@ -302,8 +302,8 @@ public int TypeParameterName { private int? __pbn__TypeParameterName; [global::ProtoBuf.ProtoMember (12, Name = @"type_alias_name")] - public int TypeAliasName { - get { return __pbn__TypeAliasName.GetValueOrDefault (); } + public int? TypeAliasName { + get { return __pbn__TypeAliasName; } set { __pbn__TypeAliasName = value; } } public bool ShouldSerializeTypeAliasName () => __pbn__TypeAliasName != null; @@ -314,8 +314,8 @@ public int TypeAliasName { public Type OuterType { get; set; } [global::ProtoBuf.ProtoMember (11, Name = @"outer_type_id")] - public int OuterTypeId { - get { return __pbn__OuterTypeId.GetValueOrDefault (); } + public int? OuterTypeId { + get { return __pbn__OuterTypeId; } set { __pbn__OuterTypeId = value; } } public bool ShouldSerializeOuterTypeId () => __pbn__OuterTypeId != null; @@ -326,8 +326,8 @@ public int OuterTypeId { public Type AbbreviatedType { get; set; } [global::ProtoBuf.ProtoMember (14, Name = @"abbreviated_type_id")] - public int AbbreviatedTypeId { - get { return __pbn__AbbreviatedTypeId.GetValueOrDefault (); } + public int? AbbreviatedTypeId { + get { return __pbn__AbbreviatedTypeId; } set { __pbn__AbbreviatedTypeId = value; } } public bool ShouldSerializeAbbreviatedTypeId () => __pbn__AbbreviatedTypeId != null; diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs new file mode 100644 index 000000000..d8a9cee4b --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Xamarin.Android.Tools.Bytecode +{ + public static class KotlinUtilities + { + public static string ConvertKotlinTypeSignature (KotlinType type, KotlinClass klass = null) + { + if (type is null) + return string.Empty; + + var class_name = type.ClassName; + + if (string.IsNullOrWhiteSpace (class_name)) { + if (klass is object) { + + var tp = klass.TypeParameters.FirstOrDefault (t => t.Id == type.TypeParameter); + + if (tp?.UpperBounds.FirstOrDefault ()?.ClassName != null) + return ConvertKotlinClassToJava (tp.UpperBounds.FirstOrDefault ()?.ClassName); + } + + return "Ljava/lang/Object;"; + } + + var result = ConvertKotlinClassToJava (class_name); + + if (result == "[") + result += ConvertKotlinTypeSignature (type.Arguments.FirstOrDefault ()?.Type); + + return result; + } + + public static string ConvertKotlinClassToJava (string className) + { + if (string.IsNullOrWhiteSpace (className)) + return className; + + className = className.Replace ('.', '$'); + + if (type_map.TryGetValue (className.TrimEnd (';'), out var result)) + return result; + + return "L" + className; + } + + public static string GetSignature (this List parameters) + { + return string.Join (string.Empty, parameters.Select (p => ConvertKotlinTypeSignature (p.Type))); + } + + public static ParameterInfo[] GetFilteredParameters (this MethodInfo method) + { + // Kotlin adds this to some constructors but I cannot tell which ones, + // so we'll just ignore them if we see them on the Java side + return method.GetParameters ().Where (p => p.Type.BinaryName != "Lkotlin/jvm/internal/DefaultConstructorMarker;" && !p.Name.StartsWith ("$")).ToArray (); + } + + public static string GetMethodNameWithoutSuffix (this MethodInfo method) + { + // Kotlin will rename some of its constructs to hide them from the Java runtime + // These take the form of thing like: + // - add-impl + // - add-H3FcsT8 + // We strip them for trying to match up the metadata to the MethodInfo + var index = method.Name.IndexOfAny (new [] { '-', '$' }); + + return index >= 0 ? method.Name.Substring (0, index) : method.Name; + } + + public static bool IsPubliclyVisible (this ClassAccessFlags flags) => flags.HasFlag (ClassAccessFlags.Public) || flags.HasFlag (ClassAccessFlags.Protected); + + public static bool IsPubliclyVisible (this KotlinClassFlags flags) => flags.HasFlag (KotlinClassFlags.Public) || flags.HasFlag (KotlinClassFlags.Protected); + + public static bool IsPubliclyVisible (this KotlinFunctionFlags flags) => flags.HasFlag (KotlinFunctionFlags.Public) || flags.HasFlag (KotlinFunctionFlags.Protected); + + public static bool IsPubliclyVisible (this KotlinConstructorFlags flags) => flags.HasFlag (KotlinConstructorFlags.Public) || flags.HasFlag (KotlinConstructorFlags.Protected); + + public static bool IsPubliclyVisible (this KotlinPropertyFlags flags) => flags.HasFlag (KotlinPropertyFlags.Public) || flags.HasFlag (KotlinPropertyFlags.Protected); + + public static bool IsUnnamedParameter (this ParameterInfo parameter) => parameter.Name.Length > 1 && parameter.Name.StartsWith ("p") && int.TryParse (parameter.Name.Substring (1), out var _); + + public static bool IsUnnamedParameter (this KotlinValueParameter parameter) => parameter.Name.Length > 1 && parameter.Name.StartsWith ("p") && int.TryParse (parameter.Name.Substring (1), out var _); + + 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;" }, + { "kotlin/Nothing", "Ljava/lang/Void;" }, + { "kotlin/Annotation", "Ljava/lang/annotation/Annotation;" }, + { "kotlin/String", "Ljava/lang/String;" }, + { "kotlin/CharSequence", "Ljava/lang/CharSequence;" }, + { "kotlin/Throwable", "Ljava/lang/Throwable;" }, + { "kotlin/Cloneable", "Ljava/lang/Cloneable;" }, + { "kotlin/Number", "Ljava/lang/Number;" }, + { "kotlin/Comparable", "Ljava/lang/Comparable;" }, + { "kotlin/Enum", "Ljava/lang/Enum;" }, + + { "kotlin/collections/Iterator", "Ljava/util/Iterator;" }, + { "kotlin/collections/MutableIterator", "Ljava/util/Iterator;" }, + { "kotlin/collections/Collection", "Ljava/util/Collection;" }, + { "kotlin/collections/MutableCollection", "Ljava/util/Collection;" }, + { "kotlin/collections/List", "Ljava/util/List;" }, + { "kotlin/collections/MutableList", "Ljava/util/List;" }, + { "kotlin/collections/Set", "Ljava/util/Set;" }, + { "kotlin/collections/MutableSet", "Ljava/util/Set;" }, + { "kotlin/collections/Map", "Ljava/util/Map;" }, + { "kotlin/collections/MutableMap", "Ljava/util/Map;" }, + { "kotlin/collections/ListIterator", "Ljava/util/ListIterator;" }, + { "kotlin/collections/MutableListIterator", "Ljava/util/ListIterator;" }, + + { "kotlin/collections/Iterable", "Ljava/lang/Iterable;" }, + { "kotlin/collections/MutableIterable", "Ljava/lang/Iterable;" }, + { "kotlin/collections/Map$Entry", "Ljava/util/Map$Entry;" }, + { "kotlin/collections/MutableMap$MutableEntry", "Ljava/util/Map$Entry;" }, + + { "kotlin/Function0", "Lkotlin/jvm/functions/Function0;" }, + { "kotlin/Function1", "Lkotlin/jvm/functions/Function1;" }, + { "kotlin/Function2", "Lkotlin/jvm/functions/Function2;" }, + { "kotlin/Function3", "Lkotlin/jvm/functions/Function3;" }, + { "kotlin/Function4", "Lkotlin/jvm/functions/Function4;" }, + { "kotlin/Function5", "Lkotlin/jvm/functions/Function5;" }, + { "kotlin/Function6", "Lkotlin/jvm/functions/Function6;" }, + { "kotlin/Function7", "Lkotlin/jvm/functions/Function7;" }, + { "kotlin/Function8", "Lkotlin/jvm/functions/Function8;" }, + { "kotlin/Function9", "Lkotlin/jvm/functions/Function9;" }, + { "kotlin/Function10", "Lkotlin/jvm/functions/Function10;" }, + { "kotlin/Function11", "Lkotlin/jvm/functions/Function11;" }, + { "kotlin/Function12", "Lkotlin/jvm/functions/Function12;" }, + { "kotlin/Function13", "Lkotlin/jvm/functions/Function13;" }, + { "kotlin/Function14", "Lkotlin/jvm/functions/Function14;" }, + { "kotlin/Function15", "Lkotlin/jvm/functions/Function15;" }, + { "kotlin/Function16", "Lkotlin/jvm/functions/Function16;" }, + { "kotlin/Function17", "Lkotlin/jvm/functions/Function17;" }, + { "kotlin/Function18", "Lkotlin/jvm/functions/Function18;" }, + { "kotlin/Function19", "Lkotlin/jvm/functions/Function19;" }, + { "kotlin/Function20", "Lkotlin/jvm/functions/Function20;" }, + { "kotlin/Function21", "Lkotlin/jvm/functions/Function21;" }, + { "kotlin/Function22", "Lkotlin/jvm/functions/Function22;" }, + }; + } +} diff --git a/src/Xamarin.Android.Tools.Bytecode/Methods.cs b/src/Xamarin.Android.Tools.Bytecode/Methods.cs index 1f727c79c..68ec6c128 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Methods.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Methods.cs @@ -29,7 +29,7 @@ public sealed class MethodInfo { public ConstantPool ConstantPool {get; private set;} public ClassFile DeclaringType {get; private set;} - public MethodAccessFlags AccessFlags {get; private set;} + public MethodAccessFlags AccessFlags {get; set;} public AttributeCollection Attributes {get; private set;} public MethodInfo (ConstantPool constantPool, ClassFile declaringType, Stream stream) @@ -58,6 +58,8 @@ public bool IsConstructor { get {return Name == "";} } + public bool IsPubliclyVisible => AccessFlags.HasFlag (MethodAccessFlags.Public) || AccessFlags.HasFlag (MethodAccessFlags.Protected); + public TypeInfo ReturnType { get { int endParams; diff --git a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs index 2cfe6097d..7756d8036 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs @@ -243,8 +243,25 @@ public static Method CreateMethod (GenBase declaringType, XElement elem) if (elem.Attribute ("managedName") != null) method.Name = elem.XGetAttribute ("managedName"); - else - method.Name = StringRocks.MemberToPascalCase (method.JavaName); + else { + var name = method.JavaName; + + // Kotlin generates methods that cannot be referenced in Java, + // like `add-impl` and `add-V5j3Lk8`. We mangle them back into + // something a user would expect by truncating anything after the hyphen. + var index = name.IndexOf ("-impl"); + + if (index >= 0) + name = name.Substring (0, index); + + index = name.IndexOf ('-'); + + // `add-V5j3Lk8` is always a 7 character hashcode + if (index >= 0 && name.Length - index == 8) + name = name.Substring (0, index); + + method.Name = StringRocks.MemberToPascalCase (name); + } if (method.IsReturnEnumified) { method.ManagedReturn = elem.XGetAttribute ("enumReturn"); diff --git a/tools/generator/Tests/Unit-Tests/XmlApiImporterTests.cs b/tools/generator/Tests/Unit-Tests/XmlApiImporterTests.cs index c8161224a..a7681be26 100644 --- a/tools/generator/Tests/Unit-Tests/XmlApiImporterTests.cs +++ b/tools/generator/Tests/Unit-Tests/XmlApiImporterTests.cs @@ -89,6 +89,24 @@ public void CreateMethod_EnsureValidNameHyphen () Assert.AreEqual ("_3", klass.Methods [0].Name); } + [Test] + public void CreateMethod_EnsureKotlinImplFix () + { + var xml = XDocument.Parse (""); + var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class")); + + Assert.AreEqual ("Add", klass.Methods [0].Name); + } + + [Test] + public void CreateMethod_EnsureKotlinHashcodeFix () + { + var xml = XDocument.Parse (""); + var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class")); + + Assert.AreEqual ("Add", klass.Methods [0].Name); + } + [Test] public void CreateParameter_EnsureValidName () { From 77c7fe21bd3090b6295b2c204e5fdf648c7c687d Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 31 Oct 2019 11:23:15 -0500 Subject: [PATCH 2/5] Support more Kotlin constructs. --- .../ClassFile.cs | 2 + .../Kotlin/KotlinClassMetadata.cs | 53 +++++++++--- .../Kotlin/KotlinFixups.cs | 84 +++++++++++-------- .../Kotlin/KotlinMetadata.cs | 46 ++++++++++ src/Xamarin.Android.Tools.Bytecode/Methods.cs | 2 + 5 files changed, 139 insertions(+), 48 deletions(-) diff --git a/src/Xamarin.Android.Tools.Bytecode/ClassFile.cs b/src/Xamarin.Android.Tools.Bytecode/ClassFile.cs index 6968509e7..2ab54dd28 100644 --- a/src/Xamarin.Android.Tools.Bytecode/ClassFile.cs +++ b/src/Xamarin.Android.Tools.Bytecode/ClassFile.cs @@ -186,6 +186,8 @@ public bool IsStatic { public bool IsEnum { get {return (AccessFlags & ClassAccessFlags.Enum) != 0;} } + + public override string ToString () => ThisClass?.Name.Value; } [Flags] diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs index cd9395a80..ddc7b9d7c 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs @@ -7,24 +7,39 @@ namespace Xamarin.Android.Tools.Bytecode { - public class KotlinClass + public class KotlinFile + { + public List Functions { get; set; } + public List Properties { get; set; } + public List TypeAliases { get; set; } + public KotlinTypeTable TypeTable { get; set; } + public KotlinVersionRequirementTable VersionRequirementTable { get; set; } + + internal static KotlinFile FromProtobuf (Package c, JvmNameResolver resolver) + { + return new KotlinFile { + Functions = c.Functions?.Select (f => KotlinFunction.FromProtobuf (f, resolver)).ToList (), + Properties = c.Properties?.Select (p => KotlinProperty.FromProtobuf (p, resolver)).ToList (), + TypeAliases = c.TypeAlias?.Select (tp => KotlinTypeAlias.FromProtobuf (tp, resolver)).ToList (), + TypeTable = KotlinTypeTable.FromProtobuf (c.TypeTable, resolver), + VersionRequirementTable = KotlinVersionRequirementTable.FromProtobuf (c.VersionRequirementTable, resolver) + }; + } + } + + public class KotlinClass : KotlinFile { public string CompanionObjectName { get; set; } public List Constructors { get; set; } public List EnumEntries { get; set; } public KotlinClassFlags Flags { get; set; } public string FullyQualifiedName { get; set; } - public List Functions { get; set; } public List NestedClassNames { get; set; } = new List (); - public List Properties { get; set; } public List SealedSubclassFullyQualifiedNames { get; set; } public List SuperTypeIds { get; set; } public List SuperTypes { get; set; } - public List TypeAliases { get; set; } public List TypeParameters { get; set; } - public KotlinTypeTable TypeTable { get; set; } public int [] VersionRequirements { get; set; } - public KotlinVersionRequirementTable VersionRequirementTable { get; set; } internal static KotlinClass FromProtobuf (Class c, JvmNameResolver resolver) { @@ -228,6 +243,18 @@ internal static KotlinFunction FromProtobuf (Function f, JvmNameResolver resolve } public override string ToString () => Name; + + public string GetFlags () + { + var sb = new StringBuilder (); + + foreach (var f in Enum.GetNames (typeof (KotlinFunctionFlags))) { + if (Flags.HasFlag ((KotlinFunctionFlags)Enum.Parse (typeof (KotlinFunctionFlags), f))) + sb.Append (f); + } + + return sb.ToString (); + } } public class KotlinContract @@ -614,13 +641,13 @@ public enum KotlinFunctionFlags Delegation = 0b10_00_000_0, Synthesized = 0b11_00_000_0, - IsOperator = 0b_0000001_000_00_000_0, - IsInfix = 0b_0000010_000_00_000_0, - IsInline = 0b_0000100_000_00_000_0, - IsTailrec = 0b_0001000_000_00_000_0, - IsExternalFunction = 0b_0010000_000_00_000_0, - IsSuspend = 0b_0100000_000_00_000_0, - IsExpectFunction = 0b_1000000_000_00_000_0 + IsOperator = 0b_0000001_00_00_000_0, + IsInfix = 0b_0000010_00_00_000_0, + IsInline = 0b_0000100_00_00_000_0, + IsTailrec = 0b_0001000_00_00_000_0, + IsExternalFunction = 0b_0010000_00_00_000_0, + IsSuspend = 0b_0100000_00_00_000_0, + IsExpectFunction = 0b_1000000_00_00_000_0 } [Flags] diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs index 03d3826aa..168988e1e 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs @@ -21,32 +21,36 @@ public static void Fixup (IList classes) try { var km = KotlinMetadata.FromAnnotation (kotlin); + var metadata = km.ParseMetadata (); - if (km.Kind == KotlinMetadataKind.Class) { - var class_metadata = km.AsClassMetadata (); + if (metadata is null) + return; - FixupClassVisibility (c, class_metadata); + // Do fixups only valid for full classes + var class_metadata = (metadata as KotlinClass); - if (class_metadata.Flags.HasFlag (KotlinClassFlags.EnumClass)) - continue; + if (class_metadata != null) { + FixupClassVisibility (c, class_metadata); foreach (var con in class_metadata.Constructors) FixupConstructor (FindJavaConstructor (class_metadata, con, c), con); + } - foreach (var met in class_metadata.Functions) - FixupFunction (FindJavaMethod (class_metadata, met, c), met, class_metadata); + // Do fixups valid for both classes and modules + // (We pass "class_metadata" even though it's sometimes null because it's + // used for generic type resolution if available for class types) + FixupJavaMethods (c.Methods); - foreach (var prop in class_metadata.Properties) { - var getter = FindJavaPropertyGetter (class_metadata, prop, c); - var setter = FindJavaPropertySetter (class_metadata, prop, c); + foreach (var met in metadata.Functions) + FixupFunction (FindJavaMethod (class_metadata, met, c), met, class_metadata); - FixupProperty (getter, setter, prop); - } - } else { - // We don't have explicit support for other types of Kotlin constructs yet, - // so they are unlikely to work. Mark them as private so they don't get bound. - c.AccessFlags = ClassAccessFlags.Private; + foreach (var prop in metadata.Properties) { + var getter = FindJavaPropertyGetter (class_metadata, prop, c); + var setter = FindJavaPropertySetter (class_metadata, prop, c); + + FixupProperty (getter, setter, prop); } + } catch (Exception ex) { Log.Warning (0, $"class-parse: warning: Unable to parse Kotlin metadata on '{c.ThisClass.Name}': {ex}"); } @@ -61,25 +65,22 @@ static void FixupClassVisibility (ClassFile klass, KotlinClass metadata) klass.AccessFlags = ClassAccessFlags.Private; return; } + } - // We don't have explicit support for these types of Kotlin constructs yet, - // so they are unlikely to work. Mark them as private so they don't get bound. - if (metadata.Flags.HasFlag (KotlinClassFlags.AnnotationClass) || metadata.Flags.HasFlag (KotlinClassFlags.CompanionObject) || metadata.Flags.HasFlag (KotlinClassFlags.Object)) { - Log.Debug ($"Kotlin: Hiding unsupported class {klass.ThisClass.Name.Value} ({metadata.Flags})"); - klass.AccessFlags = ClassAccessFlags.Private; + static void FixupJavaMethods (Methods methods) + { + // We do the following method level fixups here because we can operate on all methods, + // not just ones that have corresponding Kotlin metadata, like FixupFunction does. + + // Hide Kotlin generated methods like "add-impl" that aren't intended for end users + foreach (var method in methods.Where (m => m.IsPubliclyVisible && m.Name.Contains ("-impl"))) { + Log.Debug ($"Kotlin: Hiding implementation method {method.DeclaringType?.ThisClass.Name.Value} - {method.Name}"); + method.AccessFlags = MethodAccessFlags.Private; } - foreach (var method in klass.Methods) - if (method.Name.Contains ("-impl")) { - Log.Debug ($"Kotlin: Hiding implementation method {method.DeclaringType?.ThisClass.Name.Value} - {method.Name}"); - method.AccessFlags = MethodAccessFlags.Private; - } else if (method.Name.Contains ("-deprecated")) { - Log.Debug ($"Kotlin: Hiding deprecated method {method.DeclaringType?.ThisClass.Name.Value} - {method.Name}"); - method.AccessFlags = MethodAccessFlags.Private; - } else if (method.AccessFlags.HasFlag (MethodAccessFlags.Static) && method.GetParameters ().FirstOrDefault ()?.Name == "$this") { - Log.Debug ($"Kotlin: Hiding extension method {method.DeclaringType?.ThisClass.Name.Value} - {method.Name}"); - method.AccessFlags = MethodAccessFlags.Private; - } + // Better parameter names in extension methods + foreach (var method in methods.Where (m => m.IsPubliclyVisible && m.AccessFlags.HasFlag (MethodAccessFlags.Static))) + FixupExtensionMethod (method); } static void FixupConstructor (MethodInfo method, KotlinConstructor metadata) @@ -97,16 +98,17 @@ static void FixupConstructor (MethodInfo method, KotlinConstructor metadata) static void FixupFunction (MethodInfo method, KotlinFunction metadata, KotlinClass kotlinClass) { - if (method is null) + if (method is null || !method.IsPubliclyVisible) return; // Hide function if it isn't Public/Protected - if (method.IsPubliclyVisible && !metadata.Flags.IsPubliclyVisible ()) { + if (!metadata.Flags.IsPubliclyVisible ()) { Log.Debug ($"Kotlin: Hiding internal method {method.DeclaringType?.ThisClass.Name.Value} - {metadata.GetSignature ()}"); method.AccessFlags = MethodAccessFlags.Private; return; } + // Kotlin provides actual parameter names var java_parameters = method.GetFilteredParameters (); for (var i = 0; i < java_parameters.Length; i++) { @@ -120,6 +122,18 @@ static void FixupFunction (MethodInfo method, KotlinFunction metadata, KotlinCla } } + static void FixupExtensionMethod (MethodInfo method) + { + // Kotlin "extension" methods give the first parameter an ugly name + // like "$this$toByteString", we change it to "obj" to be a bit nicer. + var param = method.GetParameters (); + + if (param.Length > 0 && param [0].Name.StartsWith ("$this$")) { + Log.Debug ($"Kotlin: Renaming extension parameter {method.DeclaringType?.ThisClass.Name.Value} - {method.Name} - {param [0].Name} -> obj"); + param [0].Name = "obj"; + } + } + static void FixupProperty (MethodInfo getter, MethodInfo setter, KotlinProperty metadata) { if (getter is null && setter is null) @@ -142,7 +156,7 @@ static void FixupProperty (MethodInfo getter, MethodInfo setter, KotlinProperty } if (setter != null) { - var setter_parameter = setter.GetParameters () [0]; + var setter_parameter = setter.GetParameters ().First (); if (setter_parameter.IsUnnamedParameter () && !metadata.SetterValueParameter.IsUnnamedParameter ()) { Log.Debug ($"Kotlin: Renaming setter parameter {setter.DeclaringType?.ThisClass.Name.Value} - {setter.Name} - {setter_parameter.Name} -> {metadata.SetterValueParameter.Name}"); diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs index 30e84c1b2..4132dcca0 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs @@ -37,6 +37,18 @@ public static KotlinMetadata FromAnnotation (Annotation annotation) return km; } + public KotlinFile ParseMetadata () + { + switch (Kind) { + case KotlinMetadataKind.Class: + return AsClassMetadata (); + case KotlinMetadataKind.File: + return AsFileMetadata (); + default: + return null; + } + } + public KotlinClass AsClassMetadata () { if (Kind != KotlinMetadataKind.Class) @@ -71,6 +83,40 @@ public KotlinClass AsClassMetadata () } } + public KotlinFile AsFileMetadata () + { + if (Kind != KotlinMetadataKind.File) + return null; + + var md = KotlinBitEncoding.DecodeBytes (Data1); + + using (var ms = ToMemoryStream (md)) { + + // The first element is the length of the string table + var first = ms.ReadByte (); + + if (first == -1) + return null; + + ms.Position = 0; + + var size = KotlinBitEncoding.ReadRawVarint32 (ms); + + using (var partial = new PartialStream (ms, ms.Position, size)) { + + // Read the string table from the stream + var string_table = Serializer.Deserialize (partial); + var resolver = new JvmNameResolver (string_table, Data2.ToList ()); + + partial.MoveNext (); + + // Read the metadata structure from the stream + var metadata = Serializer.Deserialize (partial); + return KotlinFile.FromProtobuf (metadata, resolver); + } + } + } + static MemoryStream ToMemoryStream (byte [] bytes) => new MemoryStream (bytes); static Version ParseVersion (Annotation annotation, string key) diff --git a/src/Xamarin.Android.Tools.Bytecode/Methods.cs b/src/Xamarin.Android.Tools.Bytecode/Methods.cs index 68ec6c128..187138ef9 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Methods.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Methods.cs @@ -234,6 +234,8 @@ void UpdateParametersFromMethodParametersAttribute (ParameterInfo[] parameters) } } } + + public override string ToString () => Name; } public sealed class TypeInfo : IEquatable { From 6299d7021df74243f849bd135f26f4da408f9e46 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 31 Oct 2019 11:53:32 -0500 Subject: [PATCH 3/5] Remove duplicate code. --- .../Kotlin/KotlinMetadata.cs | 39 +++++-------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs index 4132dcca0..73d4accdd 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs @@ -54,33 +54,8 @@ public KotlinClass AsClassMetadata () if (Kind != KotlinMetadataKind.Class) return null; - var md = KotlinBitEncoding.DecodeBytes (Data1); - - using (var ms = ToMemoryStream (md)) { - - // The first element is the length of the string table - var first = ms.ReadByte (); - - if (first == -1) - return null; - - ms.Position = 0; - - var size = KotlinBitEncoding.ReadRawVarint32 (ms); - - using (var partial = new PartialStream (ms, ms.Position, size)) { - - // Read the string table from the stream - var string_table = Serializer.Deserialize (partial); - var resolver = new JvmNameResolver (string_table, Data2.ToList ()); - - partial.MoveNext (); - - // Read the metadata structure from the stream - var metadata = Serializer.Deserialize (partial); - return KotlinClass.FromProtobuf (metadata, resolver); - } - } + var data = ParseStream (); + return KotlinClass.FromProtobuf (data.Item1, data.Item2); } public KotlinFile AsFileMetadata () @@ -88,6 +63,12 @@ public KotlinFile AsFileMetadata () if (Kind != KotlinMetadataKind.File) return null; + var data = ParseStream (); + return KotlinFile.FromProtobuf (data.Item1, data.Item2); + } + + Tuple ParseStream () + { var md = KotlinBitEncoding.DecodeBytes (Data1); using (var ms = ToMemoryStream (md)) { @@ -111,8 +92,8 @@ public KotlinFile AsFileMetadata () partial.MoveNext (); // Read the metadata structure from the stream - var metadata = Serializer.Deserialize (partial); - return KotlinFile.FromProtobuf (metadata, resolver); + var metadata = Serializer.Deserialize (partial); + return Tuple.Create (metadata, resolver); } } } From f895fa00f35bc57074d641c3bf17d00c54feeb18 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Mon, 4 Nov 2019 13:49:18 -0600 Subject: [PATCH 4/5] [XA.Tools.Bytecode] Add unit tests. --- .../Kotlin/KotlinClassMetadata.cs | 59 ++++-- .../Kotlin/KotlinFixups.cs | 2 +- .../Kotlin/KotlinMetadata.cs | 2 +- .../Kotlin/KotlinUtilities.cs | 2 +- .../Tests/KotlinMetadataTests.cs | 198 ++++++++++++++++-- .../kotlin/CompanionObject$Companion.class | Bin 0 -> 752 bytes .../Tests/kotlin/CompanionObject.class | Bin 0 -> 705 bytes .../Tests/kotlin/CompanionObject.kt | 3 + .../Tests/kotlin/DataClass.class | Bin 0 -> 2800 bytes .../Tests/kotlin/DataClass.kt | 3 + .../Tests/kotlin/EnumClass.class | Bin 0 -> 1196 bytes .../Tests/kotlin/EnumClass.kt | 3 + .../kotlin/EnumClassWithInterfaces$PLUS.class | Bin 0 -> 928 bytes .../EnumClassWithInterfaces$TIMES.class | Bin 0 -> 931 bytes .../kotlin/EnumClassWithInterfaces.class | Bin 0 -> 1840 bytes .../Tests/kotlin/EnumClassWithInterfaces.kt | 15 ++ .../EnumClassWithInterfacesInterface.class | Bin 0 -> 496 bytes .../Tests/kotlin/ExtensionMethods.class | Bin 0 -> 1295 bytes .../Tests/kotlin/ExtensionMethods.kt | 7 + .../Tests/kotlin/InlineClass.class | Bin 0 -> 2431 bytes .../Tests/kotlin/InlineClass.kt | 1 + .../Tests/kotlin/Interfaces.kt | 26 +++ .../kotlin/MyInterface$DefaultImpls.class | Bin 0 -> 949 bytes .../Tests/kotlin/MyInterface.class | Bin 0 -> 744 bytes .../kotlin/MyInterface2$DefaultImpls.class | Bin 0 -> 866 bytes .../Tests/kotlin/MyInterface2.class | Bin 0 -> 554 bytes .../Tests/kotlin/MyInterfaceChild.class | Bin 0 -> 1136 bytes .../Tests/kotlin/Object.class | Bin 0 -> 538 bytes .../Tests/kotlin/Object.kt | 1 + .../Tests/kotlin/SealedClass.class | Bin 0 -> 455 bytes .../Tests/kotlin/SealedClass.kt | 1 + 31 files changed, 280 insertions(+), 43 deletions(-) create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/CompanionObject$Companion.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/CompanionObject.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/CompanionObject.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/DataClass.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/DataClass.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClass.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClass.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces$PLUS.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces$TIMES.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfacesInterface.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ExtensionMethods.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ExtensionMethods.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InlineClass.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InlineClass.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Interfaces.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface$DefaultImpls.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface2$DefaultImpls.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface2.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterfaceChild.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Object.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Object.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SealedClass.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SealedClass.kt diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs index ddc7b9d7c..901f5f8f5 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs @@ -34,12 +34,15 @@ public class KotlinClass : KotlinFile public List EnumEntries { get; set; } public KotlinClassFlags Flags { get; set; } public string FullyQualifiedName { get; set; } + public KotlinClassInheritability Inheritability { get; set; } public List NestedClassNames { get; set; } = new List (); + public KotlinClassType ObjectType { get; set; } public List SealedSubclassFullyQualifiedNames { get; set; } public List SuperTypeIds { get; set; } public List SuperTypes { get; set; } public List TypeParameters { get; set; } public int [] VersionRequirements { get; set; } + public KotlinClassVisibility Visibility { get; set; } internal static KotlinClass FromProtobuf (Class c, JvmNameResolver resolver) { @@ -50,7 +53,9 @@ internal static KotlinClass FromProtobuf (Class c, JvmNameResolver resolver) Flags = (KotlinClassFlags)c.Flags, FullyQualifiedName = c.FqName > 0 ? resolver.GetString (c.FqName) : null, Functions = c.Functions?.Select (f => KotlinFunction.FromProtobuf (f, resolver)).ToList (), + Inheritability = (KotlinClassInheritability)((c.Flags & 0b110000) >> 4), NestedClassNames = c.NestedClassNames?.Select (n => resolver.GetString (n)).ToList (), + ObjectType = (KotlinClassType) ((c.Flags & 0b111000000) >> 6), Properties = c.Properties?.Select (p => KotlinProperty.FromProtobuf (p, resolver)).ToList (), SealedSubclassFullyQualifiedNames = c.SealedSubclassFqNames?.Select (n => resolver.GetString (n)).ToList (), SuperTypeIds = c.SupertypeIds?.Select (n => resolver.GetString (n)).ToList (), @@ -59,7 +64,8 @@ internal static KotlinClass FromProtobuf (Class c, JvmNameResolver resolver) TypeParameters = c.TypeParameters?.Select (tp => KotlinTypeParameter.FromProtobuf (tp, resolver)).ToList (), VersionRequirements = c.VersionRequirements, TypeTable = KotlinTypeTable.FromProtobuf (c.TypeTable, resolver), - VersionRequirementTable = KotlinVersionRequirementTable.FromProtobuf (c.VersionRequirementTable, resolver) + VersionRequirementTable = KotlinVersionRequirementTable.FromProtobuf (c.VersionRequirementTable, resolver), + Visibility = (KotlinClassVisibility)((c.Flags & 0b1110) >> 1) }; } } @@ -301,6 +307,8 @@ internal static KotlinProperty FromProtobuf (Property p, JvmNameResolver resolve VersionRequirements = p.VersionRequirements }; } + + public override string ToString () => Name; } public class KotlinType @@ -577,26 +585,6 @@ public enum KotlinClassFlags { HasAnnotations = 0b00_00_000_1, - Internal = 0b00_00_000_0, - Private = 0b00_00_001_0, - Protected = 0b00_00_010_0, - Public = 0b00_00_011_0, - PrivateToThis = 0b00_00_100_0, - Local = 0b00_00_101_0, - - Final = 0b00_00_000_0, - Open = 0b00_01_000_0, - Abstract = 0b00_10_000_0, - Sealed = 0b00_11_000_0, - - Class = 0b000_00_000_0, - Interface = 0b001_00_000_0, - EnumClass = 0b010_00_000_0, - EnumEntry = 0b011_00_000_0, - AnnotationClass = 0b100_00_000_0, - Object = 0b101_00_000_0, - CompanionObject = 0b111_00_000_0, - IsInner = 0b_00001_000_00_000_0, IsData = 0b_00010_000_00_000_0, IsExternalClass = 0b_00100_000_00_000_0, @@ -604,6 +592,35 @@ public enum KotlinClassFlags IsInlineClass = 0b_10000_000_00_000_0 } + public enum KotlinClassVisibility + { + Internal = 0, + Private = 1, + Protected = 2, + Public = 3, + PrivateToThis = 4, + Local = 5 + } + + public enum KotlinClassType + { + Class = 0, + Interface = 1, + EnumClass = 2, + EnumEntry = 3, + AnnotationClass = 4, + Object = 5, + CompanionObject = 6 + } + + public enum KotlinClassInheritability + { + Final = 0, + Open = 1, + Abstract = 2, + Sealed = 3 + } + [Flags] public enum KotlinConstructorFlags { diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs index 168988e1e..8bde6910c 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs @@ -60,7 +60,7 @@ public static void Fixup (IList classes) static void FixupClassVisibility (ClassFile klass, KotlinClass metadata) { // Hide class if it isn't Public/Protected - if (klass.AccessFlags.IsPubliclyVisible () && !metadata.Flags.IsPubliclyVisible ()) { + if (klass.AccessFlags.IsPubliclyVisible () && !metadata.Visibility.IsPubliclyVisible ()) { Log.Debug ($"Kotlin: Hiding internal class {klass.ThisClass.Name.Value}"); klass.AccessFlags = ClassAccessFlags.Private; return; diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs index 73d4accdd..cf397d15f 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinMetadata.cs @@ -114,7 +114,7 @@ static Version ParseVersion (Annotation annotation, string key) static string GetValue (Annotation annotation, string key) { - return (annotation.Values.FirstOrDefault (v => v.Key == key).Value as AnnotationElementConstant)?.Value; + return annotation.Values.FirstOrDefault (v => v.Key == key).Value?.ToString (); } static string[] GetValues (Annotation annotation, string key) diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs index d8a9cee4b..3b3cabcc2 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs @@ -74,7 +74,7 @@ public static string GetMethodNameWithoutSuffix (this MethodInfo method) public static bool IsPubliclyVisible (this ClassAccessFlags flags) => flags.HasFlag (ClassAccessFlags.Public) || flags.HasFlag (ClassAccessFlags.Protected); - public static bool IsPubliclyVisible (this KotlinClassFlags flags) => flags.HasFlag (KotlinClassFlags.Public) || flags.HasFlag (KotlinClassFlags.Protected); + public static bool IsPubliclyVisible (this KotlinClassVisibility flags) => flags == KotlinClassVisibility.Public || flags == KotlinClassVisibility.Protected; public static bool IsPubliclyVisible (this KotlinFunctionFlags flags) => flags.HasFlag (KotlinFunctionFlags.Public) || flags.HasFlag (KotlinFunctionFlags.Protected); diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/KotlinMetadataTests.cs b/src/Xamarin.Android.Tools.Bytecode/Tests/KotlinMetadataTests.cs index 1a8da6a85..7126ac34f 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/KotlinMetadataTests.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/KotlinMetadataTests.cs @@ -11,49 +11,197 @@ public class KotlinMetadataTests : ClassFileFixture [Test] public void PublicKotlinClassFile () { - var meta = GetMetadataForClassFile ("PublicClass.class"); + var klass_meta = GetClassMetadata ("PublicClass.class"); - Assert.AreEqual (KotlinMetadataKind.Class, meta.Kind); + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + } - var klass_meta = meta.AsClassMetadata (); + [Test] + public void PrivateKotlinClassFile () + { + var klass_meta = GetClassMetadata ("PrivateClass.class"); - Assert.True (klass_meta.Flags.HasFlag (KotlinClassFlags.Public)); + Assert.AreEqual (KotlinClassVisibility.Private, klass_meta.Visibility); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); } [Test] - public void PrivateKotlinClassFile () + public void InternalKotlinClassFile () { - var meta = GetMetadataForClassFile ("PrivateClass.class"); + var klass_meta = GetClassMetadata ("InternalClass.class"); - Assert.AreEqual (KotlinMetadataKind.Class, meta.Kind); + Assert.AreEqual (KotlinClassVisibility.Internal, klass_meta.Visibility); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + } - var klass_meta = meta.AsClassMetadata (); + [Test] + public void ProtectedKotlinClassFile () + { + var klass_meta = GetClassMetadata ("PublicClass$ProtectedClass.class"); - Assert.True (klass_meta.Flags.HasFlag (KotlinClassFlags.Private)); + Assert.AreEqual (KotlinClassVisibility.Protected, klass_meta.Visibility); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); } [Test] - public void InternalKotlinClassFile () + public void SealedClassFile () { - var meta = GetMetadataForClassFile ("InternalClass.class"); + var klass_meta = GetClassMetadata ("SealedClass.class"); - Assert.AreEqual (KotlinMetadataKind.Class, meta.Kind); + Assert.AreEqual (KotlinClassInheritability.Sealed, klass_meta.Inheritability); + } - var klass_meta = meta.AsClassMetadata (); + [Test] + public void Interface () + { + var klass_meta = GetClassMetadata ("MyInterface.class"); - Assert.True (klass_meta.Flags.HasFlag (KotlinClassFlags.Internal)); + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); + Assert.AreEqual (KotlinClassInheritability.Abstract, klass_meta.Inheritability); + Assert.AreEqual (KotlinClassType.Interface, klass_meta.ObjectType); + + Assert.AreEqual (2, klass_meta.Functions.Count); + Assert.AreEqual (2, klass_meta.Properties.Count); } [Test] - public void ProtectedKotlinClassFile () + public void InterfaceDefaultImpls () { - var meta = GetMetadataForClassFile ("PublicClass$ProtectedClass.class"); + var meta = GetMetadataForClassFile ("MyInterface$DefaultImpls.class"); - Assert.AreEqual (KotlinMetadataKind.Class, meta.Kind); + Assert.AreEqual (KotlinMetadataKind.SyntheticClass, meta.Kind); + + // TODO: We don't support SyntheticClass yet + } - var klass_meta = meta.AsClassMetadata (); + [Test] + public void InterfaceInheritingInterface () + { + var klass_meta = GetClassMetadata ("MyInterface2.class"); + + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); + Assert.AreEqual (KotlinClassInheritability.Abstract, klass_meta.Inheritability); + Assert.AreEqual (KotlinClassType.Interface, klass_meta.ObjectType); + + Assert.AreEqual (0, klass_meta.Functions.Count); + Assert.AreEqual (2, klass_meta.Properties.Count); + Assert.AreEqual ("MyInterface;", klass_meta.SuperTypes [0].ClassName); + } + + [Test] + public void ClassInheritingInterface () + { + var klass_meta = GetClassMetadata ("MyInterfaceChild.class"); + + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + + Assert.AreEqual (1, klass_meta.Functions.Count); + Assert.AreEqual (1, klass_meta.Properties.Count); + Assert.AreEqual ("MyInterface;", klass_meta.SuperTypes [0].ClassName); + } - Assert.True (klass_meta.Flags.HasFlag (KotlinClassFlags.Protected)); + [Test] + public void DataClass () + { + var klass_meta = GetClassMetadata ("DataClass.class"); + + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); + Assert.True (klass_meta.Flags.HasFlag (KotlinClassFlags.IsData)); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + + Assert.AreEqual (1, klass_meta.Constructors.Count); + Assert.AreEqual (6, klass_meta.Functions.Count); + Assert.AreEqual (3, klass_meta.Properties.Count); + } + + [Test] + public void EnumClassFile () + { + var klass_meta = GetClassMetadata ("EnumClass.class"); + + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); + Assert.AreEqual (KotlinClassType.EnumClass, klass_meta.ObjectType); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + + Assert.AreEqual (1, klass_meta.Constructors.Count); + Assert.AreEqual (4, klass_meta.EnumEntries.Count); + } + + [Test] + public void EnumClassWithInterfaces () + { + var klass_meta = GetClassMetadata ("EnumClassWithInterfaces.class"); + + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); + Assert.AreEqual (KotlinClassType.EnumClass, klass_meta.ObjectType); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + + Assert.AreEqual (1, klass_meta.Constructors.Count); + Assert.AreEqual (1, klass_meta.Functions.Count); + Assert.AreEqual (2, klass_meta.EnumEntries.Count); + + Assert.AreEqual ("PLUS", klass_meta.EnumEntries [0]); + Assert.AreEqual ("TIMES", klass_meta.EnumEntries [1]); + + Assert.AreEqual (2, klass_meta.SuperTypes.Count); + + Assert.AreEqual ("kotlin/Enum", klass_meta.SuperTypes [0].ClassName); + Assert.AreEqual ("EnumClassWithInterfacesInterface;", klass_meta.SuperTypes [1].ClassName); + } + + [Test] + public void EnumClassWithFunction () + { + var klass_meta = GetClassMetadata ("EnumClassWithInterfaces$PLUS.class"); + + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); + Assert.AreEqual (KotlinClassType.EnumEntry, klass_meta.ObjectType); + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + + Assert.AreEqual (1, klass_meta.Functions.Count); + Assert.AreEqual (1, klass_meta.SuperTypes.Count); + + Assert.AreEqual ("EnumClassWithInterfaces;", klass_meta.SuperTypes [0].ClassName); + } + + [Test] + public void InlineClass () + { + var klass_meta = GetClassMetadata ("InlineClass.class"); + + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + Assert.True (klass_meta.Flags.HasFlag (KotlinClassFlags.IsInlineClass)); + } + + [Test] + public void Object () + { + var klass_meta = GetClassMetadata ("Object.class"); + + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + + // One would expect this to be "Object" but it doesn't seem to be + Assert.AreEqual (KotlinClassType.Class, klass_meta.ObjectType); + } + + [Test] + public void CompanionObject () + { + var klass_meta = GetClassMetadata ("CompanionObject$Companion.class"); + + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + Assert.AreEqual (KotlinClassType.CompanionObject, klass_meta.ObjectType); + } + + [Test] + public void KotlinExtensionMethods () + { + var klass_meta = GetClassMetadata ("ExtensionMethods.class"); + + Assert.AreEqual (KotlinClassInheritability.Final, klass_meta.Inheritability); + Assert.AreEqual (KotlinClassVisibility.Public, klass_meta.Visibility); } private KotlinMetadata GetMetadataForClassFile (string file) @@ -68,6 +216,18 @@ private KotlinMetadata GetMetadataForClassFile (string file) 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/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/CompanionObject$Companion.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/CompanionObject$Companion.class new file mode 100644 index 0000000000000000000000000000000000000000..d01d39fc631c84372b23b1c1c992f0c9b7aa3a3d GIT binary patch literal 752 zcmb7BO>Yx15PkNh*>nkHn?gw_-vp`U&?UK{0ulrvMM^<}DwiIRvoYXg*NfKPoO9xb z@DDg4aX?Xz{3yhDLxWUtL9%B2-n=&&kAMF9{sTY{_Ziy#Ed3<3%Jl2;RK~Wm#y%KB zI2CgdB|?9QR&^g9L-n!J%06ZA_V!10Y@iyT0-vE`KPtnpJ6v};-}R88yEmL=Hc>j7 z&eKR~D|0Q9=!JYQip2IaZERk|Hp`DhK9l+3{wM%<-M2gO-{QNptTG(@UpI!WGsBUz zVj`@dnRq()XeG>r2ElO&;UK-2&4`+G89K{F5bzcccu2S9qIUY(Yb_T&{*XQQYy9*} zm51DEdyJZ)ac&Jhb{RI;Btvt3U+w8&W@(B2Y$C}#R9e0&(y`2s#W*3RIm}{_j6|*+ z{zKLWTFbnj2xBCzK6slIc`RQlXK24Ev{k7bDWk~oOzX@FOMh+9rIl9bb)d}M57M}| z2t|}>c5FQOhT7M%e4C;xJLw&Y^_2jd2!N20`2sP{{D?BA?9BCln|D@%mDx#g5tqp3 z;Vz+|Y;YYn(8fK&95>OS{Dk-hX~PqE18kM2iS09Vd4Ma}Ar@CpzzkYwQ^_DOxMooO E4XG=ls{jB1 literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/CompanionObject.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/CompanionObject.class new file mode 100644 index 0000000000000000000000000000000000000000..c380cd78a903b41c2e82d3472947e67c04748ef8 GIT binary patch literal 705 zcmZ8e+lmuG5Ixo}T=x5Ri^HEQ(1F?n_Q1#vuDIOLgSp+a!7hR7gUV{@XWU555*>AAeYU0z)+U0&ji)$+jU@h9vl zb6)0ue=YDT&uV*&9z%22cJrVAZ4_~hq1)LynJpq;X)6;gLjRTgDALgOV{L4b1~yKP zMKYJk;btt?U9@n+gNO5FaNJ_3@3nb6w=}hWJe9P7q0;hA8ck&KUQ9w_>ccn?;YcJZ z!)K&7&{`(_P#7a=*6zDFO#=B^WrF5Os;!FTNEt&<4Pv)D2Oq=0Rf*{p}m3>FkR82 zIOOskqNNMEq1!}fFm(`t7|T!u-dU^LHQmUqZm#Ea!`4hgt>y9tv$gaR>GzJnp;A>V zt=(2lB19f}%i}3>BT5U@oof1aFqC_czzgT5%}?*z_nHkPo{jn(23Vo z?V;MJ*|VgTZ8l1F-JDa+HO-u2`5`04xWf_BCBxQnwoV8{O7-=Px}h2Nsn*FPC8|{4 zcqA~|i9)(9-%<7)E4u^)E~Q%D9y~Ge-%GuxODkR!6F9}}&IrhM-90r4y%*#z%{25{ zSu;cUFoJU|R&TQgQ(R*gWt^u(s~a1dQ5HDa;T#96#{->;;sP!*E|o~|#3+>!!Ces; zrgCnf-^RoVn&F^Xx;SYW*P=L!eqQ*Pj#5>v>TJDCWtfM|`Mue8`&_6H3bdj2bwjPu zwHj#UyerjxfwBL(Cv-{Fm6}EuMOIDi8bkeWF*dQI_!RS8j0*y>-8!4oY_&{XgCM2# zO&@gx)|Z!nJ?MIP(Tv>d zpvTaMetNf`#NdznU_=KJt>=m%(eX6N{io>!M0v)r$6|p-z99qI_{b|9%WUKQO!g%P zGs7>DY(E^!!_thp0nNh=c)Ak^f5iy!A$f%*a_1EA*NS?2Q#~K03rLLkzkP;aJY5(G zkP+}*%w&hRk#)DMkcS^qIMmDwZ_YSl9?k%vaJIr3;R}jwoa8`G)Ob_iV@`;hWk=vn zTC|IrYl_+$?1;3s_>40tpK>O&`coR$SQbCfzO1Jo!WX z1uBUcji%wzA7#9=MN*<}W@mEFJ#**Gz4P#W* z*Yfr=B9bdz#IuW8hVIK@6)^^RePeU$6+>5tmRNIRTd=X$1#^p*!jf3DT+MYnCtPHZ zQt2{-I$2&SZ5K=iqk3t-k~`P+r#d{DVZ-tEv&D4TKo1f+Wb{(<@U0Ut3{2W&dB?Nr z_Kx46vFCV}J2}gc{5!>e<#P46q9T(F zy`@7xq)KPj?9ke?LW_FG>qio`or$T3;BEvVphR~*Bx;W?pKTo*Iv?aZPb74%$=caF zHrP6z*OIX$pKTq_vSn4{XYUoB;0Iij=#SAa)31mW^9<0S=v1O!0}518tcMyjgXjo6 z)(|%kZ-ZwVbOU-DJf~-H@!)0-Lu&1czenZmDLJ0K-mLH1jV)`}B_>(&YnEHK8jir{ zWZZQ2Ju7TBNT8d3vr)5G9iblDYiGcu+aPf?0`0#G_c7cw$!M zhiAD1SBOjxbT)c6zmnT%9Mu}FbQ`4iTND2YR63q(vpsr$>=JB=! zSA}2tRP^Cm6j#xY0ot)bEGO@A3}T2PdBXjX0?^aGzMrRKWsdFk6Khh%px+ N5kLu`1&9aGe*?!W>l*+7 literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClass.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClass.kt new file mode 100644 index 000000000..d8026e828 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClass.kt @@ -0,0 +1,3 @@ +enum class EnumClass { + NORTH, SOUTH, WEST, EAST +} \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces$PLUS.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces$PLUS.class new file mode 100644 index 0000000000000000000000000000000000000000..16e36be4b01a61fc9183275d3a49b564eb4b38e9 GIT binary patch literal 928 zcmZ`%&rcIU6#izqZI@DQsR{)uqE>Aov9;G469tKDiZNE}fddZh07JL4WM`Jd8z=u7 zFP=<17!q&(QN}kbsEKwm``&!-$2V`@+wVWVd>--WNk@ z>Mwh5`gt&hg((qvWEisI_&9t|x)B7;fWfne$}p_-rX;1bT?S_9qj;7u?4Nx zM(;?Rh*l`{L91_Lr4PD6bHI>kIJx;hcW)foQ0dmu$)u%R^;(3jO}Q_U&~8N9*f<&5 zDBc$FSjOGvpok($1$Zda%j%LIzpKSW?nW`~k)rH+o1yY=AydO2Fw9+4u`R6_2`lJ@ zd~%YZCNn2W1ou)12dQstOr{Z~cF#^L1zzRpcXnFfWxv3E?tlBhs%NJi-e%9MWxmAQ znNF_ko$IXA&*mLPhhcGAv#AQuNZceDmZnVP{+LGLZA2ruQTAi4BP*<; zsh|x`GjNMjiCN@G)(Jf!>{{e4*WP_hy?01DFW}=Y$<<$e;z|BKWA&FQ0pE9ZA!O*!wSd6$W+K__>gtN#|NXr`Xq ySR=hf_G|Q-`5j~eETp@N#S63)U>P;C;`$Dl!3u8dAZOqktQuquZW_!O%>D#P*u%E~ literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces$TIMES.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces$TIMES.class new file mode 100644 index 0000000000000000000000000000000000000000..571111d3a329c01f496e03a67289ad76fd09c94c GIT binary patch literal 931 zcmZ`%O-~a+7=C73%Cex8s!*T^YSk7JTYIfBQ9k0DmV+hr&;yyaLpyYLmh8-uc;n=+ z@#4wEgCX(ek21cqfSPDGv+q04`|<4iu|Ix({RW_c9fpm4m4v$i*ZRFQV_z8&pYVau zvD6Hkttm-YyiEpX=%oafFyvlHCCy8Q zqV8A{47*BJ``7BV&bIm6OT6>VYokQ+`N zI^lVS#>}i(B-l?OETqA)37LkJ+Pu6dcuvtt-^&Zn$-AE8I_~$6tay3RaO&)BG4HH7 z^-Lp|&tB^ptKTeJiUz~#v}scnpxvaqWLTTBlV=m!MRqqD3Wmj&RN^oR`yxK#{eUcm zRy5#2m&elLYvWu;jubaZOaTkNQX)?OAwjG6I#Ci2#DTQQtM3wJWGK2)OUizwRAji3 zv=-Fi&=Tz9*cy=|Stj(Fu$@tMy)^!mdLNUtUSJ+iNN!#EF$d#VyNBN)~ z-y&`K4#;o$-0htb`+}vYx}ideltYd+RdA}cm}U>f(a*+Wjl)!5RQ(RiRSr!oHv8kfX^ literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces.class new file mode 100644 index 0000000000000000000000000000000000000000..da1b369c80cf6ae87af9218616e77a6ad41df9c6 GIT binary patch literal 1840 zcmb7EU2_sw7=F%SVV5mU5H$hBHY!yh8j#k0ktW6j>sl~2C>y&7kJnD~-qUD7*O%eO)|#YX7j}kzpb$ z7)H?`8EhtS21y+pDe}aBVtEXVK#Txvzz|y;+nXs&B&_ATma|tgitPl_$mob7OZm>5 zl*lbd^vtckOYe^52s>Y4NWHBa3cty)cABlmLEop~%ln7DvL$Ot+~qZMR}5@_weNVo zJLvj-w<+8M)2$WTGA45BY`**EaH|)bGA#T*Hp;`Hum>h3Cs!zjXF7rRF%>7nAE}t4 z40r9mV=|m86ekj}zP)dD{aOMA6m?ufiOLwTukSM4I3@JrJ1To?5+C$>ZNk*JPLtMU zOOoyDSddjKGn{#yil*s{9pMY=H{Lsp&@Ezen5N}+u!LMjs4os^YlrIe{qQKQ^EB6a zGC7i(oNGGQ6z$cIEImA`YAGefE5oA-tMUrJqiX!sPcfd1+)pQ!EU!eXv1Dv4RVnYy z>~l?{FD{K#8F_)rVF@_7NaS-aAS){$ydq@!K8(MuvEBVYvUxIf!kr!C*q;5x!vC?^l)mBG&fqEx(S2@Pl zv|+TK;p(q}0)>U-n50h4AlpGLUC2Gh*`I03(V#I#0Fc!tjdLRi&Pi|RQE?tK!MTcA znsNHk-v^D-uQ!zQxno?AHYX`#A(S~>98-P}P(BFs5=lnXAJU1WUMEKsl4?m(&z(+P z8bKz!{V%nQ;1VtqSsYi8ACr$F0`%W-^*QE;xb_Q~0%5M>#tDW8PU-)vj(LV&LQ8{RFpOuRb-Xwx823zSsow d@Fi-S&^^RGEPD9LL&n2&FhB2M)`R9j{|8JOmu~<7 literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces.kt new file mode 100644 index 000000000..5b2efc376 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfaces.kt @@ -0,0 +1,15 @@ +interface EnumClassWithInterfacesInterface { + fun apply(t: Int, u: Int) : Int + fun applyAsInt(t: Int, u: Int) : Int +} + +enum class EnumClassWithInterfaces : EnumClassWithInterfacesInterface { + PLUS { + override fun apply(t: Int, u: Int): Int = t + u + }, + TIMES { + override fun apply(t: Int, u: Int): Int = t * u + }; + + override fun applyAsInt(t: Int, u: Int) = apply(t, u) +} \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfacesInterface.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/EnumClassWithInterfacesInterface.class new file mode 100644 index 0000000000000000000000000000000000000000..3b63f4fa4e5d44df7fa2587acac8ffc41afbe6b6 GIT binary patch literal 496 zcmah_O;5r=5Ph?Sf)%CUS3wWPgVBp1S5L%ftihP5(F3OiiWJJ0unUPdewPQs*&k(` zHtI=ZGCObI&g`4F`~LCz2B3jG!v2Mtjn5;g^;2jDzB0kIFS~*M+2ue4cPQtw7D@G7 zyX_2vuF1h7WaVTMy)Yj6{;^NUm_eus2d)1!&j@6A@g15GoK&Zo%p{arqu4~Ds@()e z_N0;AEXH$-4O5beOgkw>LfHn5_^5YEcvvpVj_`yd+~vaI5N_TPc`M)dmWzg{)3xV{ zs;FBH+s%AoHo>byuGqg<672@@a8&I}0medtT&bqHf?YmFo1Dkw`tu{zw U8#`^}H1ZmrMmEhW8mk(GPpL#@*Z=?k literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ExtensionMethods.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ExtensionMethods.class new file mode 100644 index 0000000000000000000000000000000000000000..4065ea2b8102e6124408ebd15ee8a01a553dd03f GIT binary patch literal 1295 zcmZuxTT|0O6#h275-OHr6%-Jp3T@Gpig+oapfD4sf{Ifgc-pqHEToB(Y^#s_1b>77 zz!wD{oQ{wFD95vDl#6DPJ(oTEoo~-Mlb^r7eFsp+219!NgR~r9xXyD+?z&As0Y=_E z-sOhPotCjx-?JJrfe1s?f6w1BWap~Ec1H@^s0v?JOfz3&D4g{#_u~Q>(~(xo@+vD~ z@J!Y9TE?Cw>mC=5Z*a$PC6~m*H#S|l*|F^kgPxPS!q2I^NenPd?z_?!jlWW{9*bsmfsHPn_=8sglA7E({`r?5QelcG`8zOW+bil8D#1-LVL0E^nqVhRZre zafNJO0ng5v|Kk2P)dVJ0s;dmc#|;ZBoxn6hqN@^Zy=Ay~nr#?FbZ3y$aShigyKGZ; znMsXSRcFXl&khH*B;DtB+0$Ho%UnD(?3Cv` zG^Nect`=xFNsC~ObPWkSAvqYKpd=~kVQL6yRkW2<$76-BSU6@951cH>p6UmLIW;_` za1AS1C%Hv?Wny=G2N4sC!FvZwf2_ME?%_Up@n8qcM+M6}81PZ>@z96iBNiy6(>w3u F=5LvW9E1P> literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ExtensionMethods.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ExtensionMethods.kt new file mode 100644 index 000000000..4c6ac694e --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ExtensionMethods.kt @@ -0,0 +1,7 @@ +class ExtensionMethods { + fun MutableList.swap(index1: Int, index2: Int) { + val tmp = this[index1] // 'this' corresponds to the list + this[index1] = this[index2] + this[index2] = tmp + } +} \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InlineClass.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InlineClass.class new file mode 100644 index 0000000000000000000000000000000000000000..c3c2b48ae904aa003a1fb628028b6c023c3603bf GIT binary patch literal 2431 zcmah~T~ixn6n@@KLN+0UCH*3xm0H?DQW~0S{h+P=upeoG)>5eyt!%;y3t@x18^%k< z3x9%RZ@uYFFLXvJV`miG887@%j?c+1?FO*Ug!jDfdCz&?=X~tp?|**#9Y7fi3Y`_F zZadbMy6JgICwVj1Ag`?vQccHXo z`Bm4n9j|0MPQy2SyTScr!=G%{>$K7rEPpyc**`SA8z+MVI#SS(R*3tHwx`fF9$`m_ zj&rtS`xg`r4DI$Gp4O32IK15O8Bu9zZKY&8zU4Y*y;O08t7q33V~@h2+M-okzGJ%P zibacx7qV115d{w7AKUAx4EAAv8aW(Lhz$)-XOP9AG~(!G25Su5cbhf8;f~lVt96Cr zQP?nVd!&F#sy4m}eU9wu)1DBursJ@@-JNFQ2ADCw5x|Eahjy0ha?`HQTW%6Z(-;)C zxd`G=tIFqf9A~c0)m6)xR~Xro$A93CWiW(cfi5VhVI3zK(>A9JP9Y_CB@$gUy~Qhy zc`GEkXGJS?-nMJyxst&dyd!yjx2;oyn3H&q4Q+khH0xdn+g8so@sWCl6&`+|F!;JA zW(S+qy2TbuH=XSgWpGZy(QuwLP1m|A5C3lqy?io*k8n|1=#oMvLV9!u#BSE1Mp$dB zr*&KvS=MnqgSXKyjF_j>D9i2^D;a?A_-Z0NKEQ`!dRw7qJp6hlEZ>|reUlMuD{C=M z3nfc8kJZ)@a^zr8%T&#uR`_9SeITv%YH7{bT2J$*8QHX^Cn!59MXyt>KmYDx@78)* z8&#Kcxei0?(?(-2Hr3Yp^B+6ndfZ6d+-K-irpmgJ?u+-TQFY_R_pzHPqm$YU_lo;2 zag+Vso&bX00^b+cJB(z2_pKJ^!+GIXRtx}%hQDaJ3fV}@pIY`6`tLR!-(ImQ&YJDn zEcB&U6O6-1+Ek%?oO5!rxl*;!7GK5e?ToI~Z{POO(>rVX@f$~SAv6Xt5@ zmY!-f-I{gH7T|n{F};fo%c`;aC7PVfOUf^K5}BmTpa(i+h|cj;qLzUpQTPR&KXIeM z;$Yz^@`d8B==_ItV4b5Wu%n-G&XQa>g@&HmZmM8+p8eH$&hNN&)UJ>|-SE$DZ&;VVX{lXx};K4zt(E>$OsH!k5VVee%Wo&6eo6f7>)Vwfa@Hc1}K9TV5@H8W>6xN zH{0E+Wb*HAJsza5-FgC(q4kk4-jsX>#ZBDVZLY!MQyL!PE6Rs-{DggWcNVb@2b#x;%XDA<8Ahy literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InlineClass.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InlineClass.kt new file mode 100644 index 000000000..c137def4e --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InlineClass.kt @@ -0,0 +1 @@ +inline class InlineClass(val value: String) \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Interfaces.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Interfaces.kt new file mode 100644 index 000000000..d4a01df59 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Interfaces.kt @@ -0,0 +1,26 @@ +interface MyInterface { + fun bar() + + val prop: Int // abstract + + val propertyWithImplementation: String + get() = "foo" + + fun foo() { + print(prop) + } +} + +class MyInterfaceChild : MyInterface { + override val prop: Int = 29 + + override fun bar() { + // body + } +} + +interface MyInterface2 : MyInterface { + val value2 : Int + + override val prop: Int get() = 30 +} \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface$DefaultImpls.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface$DefaultImpls.class new file mode 100644 index 0000000000000000000000000000000000000000..e8c60b1dcd59a9c35f9d5eb68037f70bbd35a0ca GIT binary patch literal 949 zcmZWoTTc@~6#j+++ir`cTogpK2ufR!wLJJB8bi1w8x|zi)CZrYWm=}}&XC<%BL7N# zFwqBpfIrH3c3Y&?WOL4Z=bZV@ZT8piA3p(Xqt38!eC;YD!VYhXwSCdyv2Wbq(vK`K zhUp%^;*QT%*LmCOiMFwjVOZ`8a~kSP5t{1{(wrypA`r@OBeh~!sdz)IJ5}$NrC~y; zx;qT3o({WCPncH7rHUM`li7{)7ZwOPOt9@w~#WrmV>p^Yz^wq!K4_&=TQ$-tx&Z>^W9vGM2#mdM) zxavwqlI**;gQrxkQX<^*c@znnw%yQi*cOK}p_}W2ebI`$2cL{kQM&BK0b%n$I@B?S zaq0u4bC|#+=_;XTpvFjNW^$iXXN}}!YO=6F=R)5N8+k&T1FuCohivLiXx9!qPW9{6fYtB&0U!L{1aYEpcf;JWiD)UYa_+hjlDsE4^8PLz*{^cTeq+ zVcSRP`s`Yxd~-UqH_G+$Xw=XH9Qmc}nrGKn>zmszUe@i}fsN;sW}VVMqCUzWk#Vt- Qevh$wgVtO;!BY}{0L?PpQ~&?~ literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface.class new file mode 100644 index 0000000000000000000000000000000000000000..cecdbcd327c2a2f293041678b7d017078cae0109 GIT binary patch literal 744 zcmZ`$O>fgc5PfSqj^ljPEukbSg+dEzFSz7{#0h~AMrlE*R4V1PNjIqzXC1toNWJA( z@B=vTBRHTaM}8DyY*HEtA+2WKyq%fddGqtv_a6Y7c*0OTxelZjNuT#b9*n^o@)`Fd zE(iXH?ojk}9wvj?+$*YFl|myY2%uHl&E zD=Tjh?euGldDA#%?;X!=7{|qTPYIPWwB6qF3_|9`@|(5%YTFIVvAkTn>e;NhVVxb^lYRwkGbu8`0;}NT&*_Ja!pM>kO^p?+Vqkk!ILwPo)mW zB9OCCh22QJx)qU%11UxFI^s$R+M|0GPm`W_8%BbmzN$R`r_QGT!y**{%VNmECcsdD zoB1M2G}$A%waJ$=u9D`E@{h^7#6F-74^hWH#Rm3pK)yr#26gi~FavC6XA5_3qH2J< j*d`YDC{v+^dIx0%N1>sxli7lTt8hQVMTL?=F2j}Is3)eT literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface2$DefaultImpls.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface2$DefaultImpls.class new file mode 100644 index 0000000000000000000000000000000000000000..66bbbb170e8fd9244cbff7b91cffb8ef1ac95db3 GIT binary patch literal 866 zcmZ`$T~8B16g{^KZYhNp@Pmq=Kn04?jeYHt!I*5aR3oN7_%vIlW!miw*_~~|fAT@! zj1R_Vf0XfVsj?KzZtl!Ech3F%_51q|08Kn2)Q&HFZDrgOUD>R^mOYV1)*qcmi3=iB z2VyL|Na()zHXO*Vb>R@)zO?URb52+}2=W114WBSyw`VFLEMIpKHm5Qf+l!CNo@Hs| zNNOvrGMcb68!WiWeqv*#`z^w5VB)?vkT#5k(upUuHkSbsuWf8QjUs-vn5)V~nXsNC zX4+gPY+uXVYTQg*#saD(RIoyDddA$+dWXj~6Q@(g8t!GX`z#2C#zsne$I^;ZVFlM& z7>ylX1!bnfFq}YSkmpQ8LOEA>)5Q~J@>?P-{o7yiVrU6-N9I%#DuL3nosL2oe-L5B z%xYk|BI<}(Wq3+%yic`NBkAk0N)+?2uI9<9e63}C6p18}ywTE$N#m}3qq1~y145nAb3Q3!t6QJ>DBvmE%n8)6$F_XwhKnMj1^!C>DwFO_rNzUa zSo((5FU(O+Q=Dq{na?6FPV{TI%lBLK55E3K@8EzvB~}%%jUBG5!MK239P+u({5m&P V`vk|w!^yvaM}N_#k1afA^9L?y!chPK literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface2.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterface2.class new file mode 100644 index 0000000000000000000000000000000000000000..3f77774fc9c5fe7e586ef0fadb163861dc404652 GIT binary patch literal 554 zcmYjN%WB&|6g_t&ONyL_;-;zXKGK#f(oLOe73iuY6a+RcE|`EfVaP9Zh5cb<8Y5bAkv-0aapAin*liX&h zZgrHEBWWd1#dPVg87j&F!@(A!fcz~@m>PW`bk?g{DC%N+*Q-!OK_~(@7}wXUSe#S$ zC=yL^?p*Y)H$UCYFVAbbfg4r2wa6>@>&@>+){oe}7_slYvQ_rt&bBr|@Rkp&HOkAJ zUa7HMWHy=3GUMYp$5&?f!T(vB_Y=$GL~Av_%%m}jqY1y|i}_G}PBTSlbQjvDQ`JjN zI>^)~t#d1Fnrq{+J-CGn4neq_AOe7Uwdfqp3D=P4FIe{y4ZJRNc!f9I+syBxg1tVR o1pC`_fann&CaB>FvpDL544&e+kH8=_cxF&HXc$yY!88s0f9Z;IV*mgE literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterfaceChild.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MyInterfaceChild.class new file mode 100644 index 0000000000000000000000000000000000000000..34521a36421a3b59ceff197a97419e00cbc31e20 GIT binary patch literal 1136 zcmZuvT~8B16g{*3*k#+owtQH?st9UZl?D7r48}wu#+m{KOf}(Y+74x5yGwSaB)st} z`~g1upovD}gK2p5M;Y(7g%%{)ojdo=Ip^NFbN~GP^&7wvo-$Z#Mn+zMRCb`AxmB^lTdoQd zNF7;*nfzeA%U9${VjNQ@CNa&Bx&Wpd?f5>m@{&d+i5s{XQf@J%%lp1^9nW49ir2Z~ zRFc*_4BN@VWrW;n$3^JU8?b!})fXAo+Q(T_%WB=W<p05dDW#zrtH_aTj7Bd`n}-vX3JAfQ-UL*?$HGbIWf9 zHSyXB`I9|pW79Q#At6KLF)oVY_fC>#ERdM`qF_c(i#$} zn>6x3vJ1d4`Qe2vq-hPw_d7ntzR^abtY^g00U|pm(unqm&j=kRNECl!tZ3zaAk~*J z=@3MUUdW#~ml?+dAubkwg6m%|)i69zLyX8dI)6lec(rR}8I9FhJ^e16V5VP5w+mMZ zX?Wa8H;*S|H%RO@HFkCzkrL)Q-yPgN^X`?9M}eX!Zi7i`+k9(*R(=*jWj-0fmI%Du0 z99BZ`!EeHg0E1UONteMWP0S|*i{-ofe7SHzFjsF+>tdyrqll!hEhQR433BpI-$N@f z9U??)V+u`aBx{k>$S~}U2Bygq+~iS0qk-}L%oC$QmLIe8fWg0B{5;CFH_HGM9Dxw# zUAM!K}+ zp@{*(ak)W!R=iKGv@iSt{xC%HD)ojn-Gt$P6OL`tIM%U4_bFw=w84B8o+2XS6Gd#s Y;zcZAkyI>I!CWk3g;Fj|7YP^sKkq492LJ#7 literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Object.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Object.kt new file mode 100644 index 000000000..fe0184116 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/Object.kt @@ -0,0 +1 @@ +object Object { } \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SealedClass.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SealedClass.class new file mode 100644 index 0000000000000000000000000000000000000000..0a0ff0789666144f9c7899128ae5d6505c40c2d7 GIT binary patch literal 455 zcmYjMIZpyX6#iy_U0gs8FYqX|QVUlpY&;SrF(Fws5ko^YuoE3vb`xfx=C}9{tV}En zvGhk7-=HQ;ChxuP$UDBi-#-A+d&LacILmDna+B_@GPzVBSR+Zp(^%JVegGZVRUQBU literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SealedClass.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SealedClass.kt new file mode 100644 index 000000000..5fdb470d1 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SealedClass.kt @@ -0,0 +1 @@ +sealed class SealedClass \ No newline at end of file From 024530f10cc2789a2a2b3f2f939985200459aa1e Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Tue, 5 Nov 2019 15:02:54 -0600 Subject: [PATCH 5/5] [XA.Tools.Bytecode] Add KotlinFixup unit tests. --- .../Kotlin/KotlinFixups.cs | 17 ++- .../Tests/KotlinFixupsTests.cs | 123 ++++++++++++++++++ ...amarin.Android.Tools.Bytecode-Tests.csproj | 1 + ...marin.Android.Tools.Bytecode-Tests.targets | 2 +- .../Tests/kotlin/InternalConstructor.class | Bin 0 -> 570 bytes .../Tests/kotlin/InternalConstructor.kt | 1 + .../Tests/kotlin/InternalMethod.class | Bin 0 -> 570 bytes .../Tests/kotlin/InternalMethod.kt | 5 + .../Tests/kotlin/InternalProperty.class | Bin 0 -> 1079 bytes .../Tests/kotlin/InternalProperty.kt | 3 + .../Tests/kotlin/MethodImplementation.class | Bin 0 -> 2377 bytes .../Tests/kotlin/MethodImplementation.kt | 7 + .../Tests/kotlin/ParameterName.class | Bin 0 -> 391 bytes .../Tests/kotlin/ParameterName.kt | 3 + .../Tests/kotlin/RenameExtensionParameter.kt | 1 + .../kotlin/RenameExtensionParameterKt.class | Bin 0 -> 1073 bytes .../Tests/kotlin/SetterParameterName.class | Bin 0 -> 641 bytes .../Tests/kotlin/SetterParameterName.kt | 3 + 18 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/KotlinFixupsTests.cs create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalConstructor.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalConstructor.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalMethod.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalMethod.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalProperty.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalProperty.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MethodImplementation.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MethodImplementation.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ParameterName.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ParameterName.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/RenameExtensionParameter.kt create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/RenameExtensionParameterKt.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SetterParameterName.class create mode 100644 src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SetterParameterName.kt diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs index 8bde6910c..c205c06e0 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs @@ -7,7 +7,7 @@ namespace Xamarin.Android.Tools.Bytecode { - static class KotlinFixups + public static class KotlinFixups { public static void Fixup (IList classes) { @@ -24,7 +24,7 @@ public static void Fixup (IList classes) var metadata = km.ParseMetadata (); if (metadata is null) - return; + continue; // Do fixups only valid for full classes var class_metadata = (metadata as KotlinClass); @@ -32,6 +32,9 @@ public static void Fixup (IList classes) if (class_metadata != null) { FixupClassVisibility (c, class_metadata); + if (!c.AccessFlags.IsPubliclyVisible ()) + continue; + foreach (var con in class_metadata.Constructors) FixupConstructor (FindJavaConstructor (class_metadata, con, c), con); } @@ -158,9 +161,9 @@ static void FixupProperty (MethodInfo getter, MethodInfo setter, KotlinProperty if (setter != null) { var setter_parameter = setter.GetParameters ().First (); - if (setter_parameter.IsUnnamedParameter () && !metadata.SetterValueParameter.IsUnnamedParameter ()) { - Log.Debug ($"Kotlin: Renaming setter parameter {setter.DeclaringType?.ThisClass.Name.Value} - {setter.Name} - {setter_parameter.Name} -> {metadata.SetterValueParameter.Name}"); - setter_parameter.Name = metadata.SetterValueParameter.Name; + if (setter_parameter.IsUnnamedParameter ()) { + Log.Debug ($"Kotlin: Renaming setter parameter {setter.DeclaringType?.ThisClass.Name.Value} - {setter.Name} - {setter_parameter.Name} -> value"); + setter_parameter.Name = "value"; } } } @@ -209,9 +212,9 @@ static MethodInfo FindJavaPropertyGetter (KotlinClass kotlinClass, KotlinPropert static MethodInfo FindJavaPropertySetter (KotlinClass kotlinClass, KotlinProperty property, ClassFile klass) { var possible_methods = klass.Methods.Where (method => string.Compare (method.GetMethodNameWithoutSuffix (), $"set{property.Name}", true) == 0 && - property.SetterValueParameter != null && + property.ReturnType != null && method.GetParameters ().Length == 1 && - TypesMatch (method.GetParameters () [0].Type, property.SetterValueParameter.Type, kotlinClass)); + TypesMatch (method.GetParameters () [0].Type, property.ReturnType, kotlinClass)); return possible_methods.FirstOrDefault (); } diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/KotlinFixupsTests.cs b/src/Xamarin.Android.Tools.Bytecode/Tests/KotlinFixupsTests.cs new file mode 100644 index 000000000..2da9a3d35 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/KotlinFixupsTests.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; +using Xamarin.Android.Tools.Bytecode; + +namespace Xamarin.Android.Tools.BytecodeTests +{ + [TestFixture] + public class KotlinFixupsTests : ClassFileFixture + { + [Test] + public void HideInternalClass () + { + var klass = LoadClassFile ("InternalClass.class"); + + Assert.True (klass.AccessFlags.HasFlag (ClassAccessFlags.Public)); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Public)); + } + + [Test] + public void HideInternalConstructor () + { + var klass = LoadClassFile ("InternalConstructor.class"); + var ctor = klass.Methods.First (m => m.Name == ""); + + Assert.True (ctor.AccessFlags.HasFlag (MethodAccessFlags.Public)); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.False (ctor.AccessFlags.HasFlag (MethodAccessFlags.Public)); + } + + [Test] + public void HideImplementationMethod () + { + var klass = LoadClassFile ("MethodImplementation.class"); + var method = klass.Methods.First (m => m.Name == "toString-impl"); + + Assert.True (method.AccessFlags.HasFlag (MethodAccessFlags.Public)); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.False (method.AccessFlags.HasFlag (MethodAccessFlags.Public)); + } + + [Test] + public void RenameExtensionParameter () + { + var klass = LoadClassFile ("RenameExtensionParameterKt.class"); + var method = klass.Methods.First (m => m.Name == "toUtf8String"); + var p = method.GetParameters () [0]; + + Assert.AreEqual ("$this$toUtf8String", p.Name); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.AreEqual ("obj", p.Name); + } + + [Test] + public void HideInternalMethod () + { + var klass = LoadClassFile ("InternalMethod.class"); + var method = klass.Methods.First (m => m.Name == "take$main"); + + Assert.True (method.AccessFlags.HasFlag (MethodAccessFlags.Public)); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.False (method.AccessFlags.HasFlag (MethodAccessFlags.Public)); + } + + [Test] + public void ParameterName () + { + var klass = LoadClassFile ("ParameterName.class"); + var method = klass.Methods.First (m => m.Name == "take"); + var p = method.GetParameters () [0]; + + Assert.AreEqual ("p0", p.Name); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.AreEqual ("count", p.Name); + } + + [Test] + public void HideInternalProperty () + { + var klass = LoadClassFile ("InternalProperty.class"); + var getter = klass.Methods.First (m => m.Name == "getCity$main"); + var setter = klass.Methods.First (m => m.Name == "setCity$main"); + + Assert.True (getter.AccessFlags.HasFlag (MethodAccessFlags.Public)); + Assert.True (setter.AccessFlags.HasFlag (MethodAccessFlags.Public)); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.False (getter.AccessFlags.HasFlag (MethodAccessFlags.Public)); + Assert.False (setter.AccessFlags.HasFlag (MethodAccessFlags.Public)); + } + + [Test] + public void RenameSetterParameter () + { + var klass = LoadClassFile ("SetterParameterName.class"); + var setter = klass.Methods.First (m => m.Name == "setCity"); + var p = setter.GetParameters () [0]; + + Assert.AreEqual ("p0", p.Name); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.AreEqual ("value", p.Name); + } + } +} diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj b/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj index cab5425f0..caeff246d 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj @@ -52,6 +52,7 @@ + diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.targets b/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.targets index b902b9075..fb3706e2e 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.targets +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.targets @@ -27,7 +27,7 @@ Outputs="@(TestKotlinJar->'%(RecursiveDir)%(Filename).class')" Condition=" '$(KotlinCPath)' != '' "> \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalConstructor.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalConstructor.class new file mode 100644 index 0000000000000000000000000000000000000000..2e8406b540d514a42411f15921ee24e8b1772bb6 GIT binary patch literal 570 zcmZvYUuzRV6vfY--E7h|X1A%WY3u)|whyMwt5VQ_un-lh1R+n8WYK9hv#>iU_!j&q zeghvw=tBv8^h1efQ++E8_s;LknLBsx??0DU0D5>P*h;KR3#+q!ZcA4zCN3}P5J7yd zFLXE4_H+0B_&lAsIy^yb{*@O5k_dvA#u|4j@DGy1p};#h95zwMMhG8a6Ey@IP-xQN zzL-+5J-Fqu7lO^fEO(i)-M6XJQ|&Z0HNWuKqf{Xxj@O6^*@nz0O-}^<)iMrMM};b0 zEkiC9H<&uBWltT;>!`6>wu4yhsblZ@d!Sy8r} zYt!ULYNV{9Oitz=g9ukDgo6cygWOPzNsV>|KId6IlnoimYMzC%BPh-o`w`9aorK?T!V>TH?)8A?klc{%; zp^AHIsvUkH!(Yj4I*bmrQ*4~fjMbB#%Jj# zXp=T=`p^&``=Of7fwkV6Y|dPE&Tr<-%ZvEAs z0gK^L$Cs|~>~_Cvi|FAtvFQ<^XoN}R8Ri=2PNQ?o1KxG0HI*DQw1x1gU<9gfaD}uH&Hockc~jE zNop#3EuZ^=x$@dKq`r(8SG+{Tn;pkRJjJ(0`D}_#s%lh~Of3eTs>YXeL6ws#f5&1u z|L_*s9MP!b5q(^3Dxz!Hq>CEL)D+-|FpsDukIxXR;K}HFilr-Sxq=luBQKtxfrVH_ X@eC6oCPS1$EQE-U6eCp5Oo;4X065@F literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalProperty.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalProperty.kt new file mode 100644 index 000000000..45b4922b0 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/InternalProperty.kt @@ -0,0 +1,3 @@ +class InternalProperty { + internal var city: String = "London" +} \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MethodImplementation.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MethodImplementation.class new file mode 100644 index 0000000000000000000000000000000000000000..a046801a3206d45202df3e7f96f2fb5ba4bc10d5 GIT binary patch literal 2377 zcmaJ>-BTM?6#w1LCTx~KmeBH9TUyZ4&^8pcR{0S6C19XP5ZmI{Cah#3*~Q%r`iMUG zCv>K7jxWCSL1(mObVeh6^UZ(9aU9RNyOHprne5(s&pqdNe&=`Y-rxWJ=@$TF_*mf7 zf)$kA^||VH#j09PUEw|k2w&|J0iZ#*XS17w) zFg`UdU>pr@flAp9tOo!sNC;?wTL?Vc*&wIkOU;ndN{EAb*W1W#T0zk>ZO6}O&@>18$z!VUL6k3r=LP48ATLb)xO?3*K9xj}aB84;zogz8}k|kN6wc-*a zdCzfB6RSuNwl`I}SgRH-uLWHKy3bOqnw6R*5F5Tke0tEUqZ@rGbmA1}IU~^iZ=Q%+ ze7Hc_QaFoq>@q<3T+vWdwGwZY2<5ljpkh0@rCPCK`(Q7#Pz&Pt=XZ5luf^U%U!pk$X4sqnpyFqRo8Ig1gRei4E^5`p#Jl5q%!oH z(;$_?8?3qlGN+o7j8ciLG=VDH*Z4I?r8HPcg{cYG!#>?2O~cs#I0X+=cnvo=&07K; zjkb^yCijj~E7hGCeQ|VBjVg_; z^wf80!4B;_YWrVJ_tbaCw2YcjMwL-D)&(+ow`5jUP0!}gD88)_n5C@+b30lO9mQLs z8?0&tmUjp^S+F-8GpKn)K`*#9uVme|nNM#Nzj}-fu2JvfR3+V<$ned>ji$Kd?Wa&2wu-V!*I6$ zN3?U4a;TDrWa*NebzDML;^|Tqx>@;_r9Do3g)mu@SmrXz(F~%1K~mv1IkKP)N-R*4 zZ^($!bsAY9CB}6&qr@djNJWWI*yE~1P9lfV2;(x1OhxHCiXobqC}C@jUTXLl;cyWb zNkxuK{tV?o-!r@tcJ_dN60e>>pYE0@b^D>zTpC>_5%?sHd`k?B{08MQ`lzMKQ%p?u zo%tFq>bFp1j}cd&$ifBI$g>r(ztAJ5#h*~|KJ#K}1YW0iFD5Y+WzEtkMH9cu{ezw< zcvJFap{7|Vql0hJC~lBOE;r7nF+5iXrN*On3(VkV6maC=-wtD%PA~c0!QIIF&|x3h z!|ZYQ7(Sr0?l=o7#keS`CBh|S?``Ojmav09wNJuHzG@2o-iSVmzDr+s97 ltjK?>kDQND9~XQK`skF#t3J;9IOpR|!)?ZgCg*7%#y>JVx~u>I literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MethodImplementation.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MethodImplementation.kt new file mode 100644 index 000000000..5e216da38 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/MethodImplementation.kt @@ -0,0 +1,7 @@ +public inline class MethodImplementation constructor(@PublishedApi internal val data: Short) : Comparable { + public override fun toString(): String = "woof" + + override operator fun compareTo(other: Short): Int { + return 0; + } +} \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ParameterName.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ParameterName.class new file mode 100644 index 0000000000000000000000000000000000000000..0927cd7dffe6a55cbec8db6b27aec4cca2710f96 GIT binary patch literal 391 zcmY*UO;5r=5Ph>P1*^!SsK5^}dJ$~m$&(ji4SqxuE}Rxvb!}-AcHzwL@?bdoqm0uU z4zFc50@a;=Y)#Jibi@G+0hbi9+%bO1Lz; zBwTIQtx&W@DCA}xatJB@s8)MzZ`P6M)18!}EBam(NT0cKds+-f#M>dad|f2$D16D6 zi>V%%l;7w)vr9AABV)}Z)i+s|JLOEC*?^b8XFI&c=YJ4zCeSYN1Rdr9AK2y2-WXnt hdKvp@{3{uwi36_Ua163&;b@G&qGoYyQ7LhU@fX9BOiTa( literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ParameterName.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ParameterName.kt new file mode 100644 index 000000000..43fbf79dc --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/ParameterName.kt @@ -0,0 +1,3 @@ +interface ParameterName { + fun take(count: Int) +} \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/RenameExtensionParameter.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/RenameExtensionParameter.kt new file mode 100644 index 000000000..70f80bd1d --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/RenameExtensionParameter.kt @@ -0,0 +1 @@ +internal fun ByteArray.toUtf8String(): String = String(this, Charsets.UTF_8) \ No newline at end of file diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/RenameExtensionParameterKt.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/RenameExtensionParameterKt.class new file mode 100644 index 0000000000000000000000000000000000000000..00542d8f5710949c2a27543cb8e8410237b41c8b GIT binary patch literal 1073 zcmaJ=T~8B16g{&A3S)&*`H-Ser3w^fDZUU96Fy_Zf`kHz>4TXrW0`h4o9s>-9(>>@ z8DBJ-7@qx6#=C9#(m*nIXU@HM?mcs6_V=G3zW^-a6+?PoYEhS)r$*{f`Fc+T1dR-~ zO&p9NSrtuTdqP+2_vNZ|$s1!x82`|GS}K}A=?cThOlfV_xyo$&ml>uUKd9JMY0806 zI<$q>z7dA{g!Zm)b{n2YS*ff!QDOFa&0h2}Ow@eiDQ#Dqbz5oTq=jb}bksCdZWsq= z7pNrf04(YI9${=IL;pJxU;f`yBVs&oI>KXymD}*H1(cN<)2;4BdwZ zTOXH5uXYKoeA{iuFY=ZXNZ~;r1~GI)wsxa9Mi_eDD6Py}hDj3a_CgV-hZy4+MVi4X ztuYKb9cDYyh+|;{$!67?F}g+;eFg}YTM!YXD`K^1VUhnhPg0(raZY9>`^i%2MDqMB zZ7r~zwP3wFKZ~*!8P&r0ZSP^eW*EBG{bR{6;3zG38}+gb4n)}_Ps;IK;T?%UMY!V} z-*0H6>aw7lDpcfOy}Eve?7z{=OT3ZBi+&?;<(7)t47I_AEH^5bPnKb?Pp@ z&9n=>ctrC&Srm=aY?H-F_ZEm@fuMyP5dc0HcUJfK9m?|U)=THPV&-DC#pg2l%w2nK zi)|a8b9jC#|2ofeo1Ff0V1;-+rsGeP5G!D^H8OZ|fu0sHg=_)O@Vo>TVj43P3eg>6 Gmd0NPuNII1 literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SetterParameterName.class b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SetterParameterName.class new file mode 100644 index 0000000000000000000000000000000000000000..43822c9671efabc6c40b8686c1070a8c014f1c77 GIT binary patch literal 641 zcmZuuO>5gg5Ph?jB}FNY;>4-r^ozEK7hTOhhlQfj|v@$q#x6s{E_WWuSl zX1lW_VU=TlXhUek4@~D{&)iID?5_?vIAg^zOWu5VPpGd72%GUFvx!cl&&tYkX(cxm z(|LtmQ?YF@9F!0RWMed8>iiwyc)6$tqA3CqE*AkW5!M3XJO20Pa?uutblMC>OB`0d zUHq^2N^s@vk%{3&0yD{Rqj;-*B~%h{|6Jt Bn4bUu literal 0 HcmV?d00001 diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SetterParameterName.kt b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SetterParameterName.kt new file mode 100644 index 000000000..7b261cd7b --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/kotlin/SetterParameterName.kt @@ -0,0 +1,3 @@ +interface SetterParameterName { + var city: String +} \ No newline at end of file