From 3d64690462bd410523db69ae21d2b4a240713782 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 12 Dec 2019 11:03:47 -0600 Subject: [PATCH 1/5] [Bytecode] Surface Kotlin's unsigned types in .class-parse XML --- src/Xamarin.Android.Tools.Bytecode/Fields.cs | 1 + .../Kotlin/KotlinClassMetadata.cs | 9 ++ .../Kotlin/KotlinFixups.cs | 80 +++++++++++++---- .../Kotlin/KotlinUtilities.cs | 4 +- src/Xamarin.Android.Tools.Bytecode/Methods.cs | 2 + .../XmlClassDeclarationBuilder.cs | 9 +- .../KotlinFixupsTests.cs | 82 ++++++++++++++++++ .../kotlin/UnsignedTypes.class | Bin 0 -> 1633 bytes .../kotlin/UnsignedTypes.kt | 23 +++++ .../kotlin/UnsignedTypesKt.class | Bin 0 -> 1101 bytes 10 files changed, 190 insertions(+), 20 deletions(-) create mode 100644 tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.class create mode 100644 tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.kt create mode 100644 tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypesKt.class diff --git a/src/Xamarin.Android.Tools.Bytecode/Fields.cs b/src/Xamarin.Android.Tools.Bytecode/Fields.cs index df316e3ac..28f9685e1 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Fields.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Fields.cs @@ -33,6 +33,7 @@ public sealed class FieldInfo { public ConstantPool ConstantPool {get; private set;} public FieldAccessFlags AccessFlags {get; private set;} public AttributeCollection Attributes {get; private set;} + public string KotlinType { get; set; } public FieldInfo (ConstantPool constantPool, Stream stream) { diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs index 901f5f8f5..1b395fecb 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using org.jetbrains.kotlin.metadata.jvm; +using ProtoBuf; using Type = org.jetbrains.kotlin.metadata.jvm.Type; namespace Xamarin.Android.Tools.Bytecode { + // https://github.com/JetBrains/kotlin/blob/master/core/metadata.jvm/src/jvm_metadata.proto public class KotlinFile { public List Functions { get; set; } @@ -220,6 +223,8 @@ internal static KotlinExpression FromProtobuf (Expression exp, JvmNameResolver r public class KotlinFunction : KotlinMethodBase { public string Name { get; set; } + public string JvmName { get; set; } + public string JvmSignature { get; set; } public KotlinFunctionFlags Flags { get; set; } public KotlinType ReturnType { get; set; } public int ReturnTypeId { get; set; } @@ -235,9 +240,13 @@ internal static KotlinFunction FromProtobuf (Function f, JvmNameResolver resolve if (f is null) return null; + var sig = Extensible.GetValue (f, 100); + return new KotlinFunction { Flags = (KotlinFunctionFlags)f.Flags, Name = resolver.GetString (f.Name), + JvmName = resolver.GetString ((sig?.Name ?? 0) > 0 ? sig.Name : f.Name), + JvmSignature = sig is null ? null : resolver.GetString (sig.Desc), ReturnType = KotlinType.FromProtobuf (f.ReturnType, resolver), ReturnTypeId = f.ReturnTypeId, ReceiverType = KotlinType.FromProtobuf (f.ReceiverType, resolver), diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs index c205c06e0..053c73ca2 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs @@ -45,13 +45,15 @@ public static void Fixup (IList classes) FixupJavaMethods (c.Methods); foreach (var met in metadata.Functions) - FixupFunction (FindJavaMethod (class_metadata, met, c), met, class_metadata); + FixupFunction (FindJavaMethod (metadata, met, c), met, class_metadata); foreach (var prop in metadata.Properties) { - var getter = FindJavaPropertyGetter (class_metadata, prop, c); - var setter = FindJavaPropertySetter (class_metadata, prop, c); + var getter = FindJavaPropertyGetter (metadata, prop, c); + var setter = FindJavaPropertySetter (metadata, prop, c); FixupProperty (getter, setter, prop); + + FixupField (FindJavaFieldProperty (metadata, prop, c), prop); } } catch (Exception ex) { @@ -96,7 +98,6 @@ static void FixupConstructor (MethodInfo method, KotlinConstructor metadata) 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) @@ -111,18 +112,24 @@ static void FixupFunction (MethodInfo method, KotlinFunction metadata, KotlinCla return; } - // Kotlin provides actual parameter names 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]; + // Kotlin provides actual parameter names 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; } + + // Handle erasure of Kotlin unsigned types + java_p.KotlinType = GetKotlinType (java_p.Type.TypeSignature, kotlin_p.Type.ClassName); } + + // Handle erasure of Kotlin unsigned types + method.KotlinReturnType = GetKotlinType (method.ReturnType.TypeSignature, metadata.ReturnType.ClassName); } static void FixupExtensionMethod (MethodInfo method) @@ -158,16 +165,32 @@ static void FixupProperty (MethodInfo getter, MethodInfo setter, KotlinProperty return; } + // Handle erasure of Kotlin unsigned types + if (getter != null) + getter.KotlinReturnType = GetKotlinType (getter.ReturnType.TypeSignature, metadata.ReturnType.ClassName); + if (setter != null) { var setter_parameter = setter.GetParameters ().First (); - if (setter_parameter.IsUnnamedParameter ()) { + if (setter_parameter.IsUnnamedParameter () || setter_parameter.Name == "") { Log.Debug ($"Kotlin: Renaming setter parameter {setter.DeclaringType?.ThisClass.Name.Value} - {setter.Name} - {setter_parameter.Name} -> value"); setter_parameter.Name = "value"; } + + // Handle erasure of Kotlin unsigned types + setter_parameter.KotlinType = GetKotlinType (setter_parameter.Type.TypeSignature, metadata.ReturnType.ClassName); } } + static void FixupField (FieldInfo field, KotlinProperty metadata) + { + if (field is null) + return; + + // Handle erasure of Kotlin unsigned types + field.KotlinType = GetKotlinType (field.Descriptor, metadata.ReturnType.ClassName); + } + static MethodInfo FindJavaConstructor (KotlinClass kotlinClass, KotlinConstructor constructor, ClassFile klass) { var all_constructors = klass.Methods.Where (method => method.Name == "" || method.Name == ""); @@ -181,16 +204,16 @@ static MethodInfo FindJavaConstructor (KotlinClass kotlinClass, KotlinConstructo return null; } - static MethodInfo FindJavaMethod (KotlinClass kotlinClass, KotlinFunction function, ClassFile klass) + static MethodInfo FindJavaMethod (KotlinFile kotlinFile, KotlinFunction function, ClassFile klass) { - var possible_methods = klass.Methods.Where (method => method.GetMethodNameWithoutSuffix () == function.Name && + var possible_methods = klass.Methods.Where (method => method.Name == function.JvmName && method.GetFilteredParameters ().Length == function.ValueParameters.Count); foreach (var method in possible_methods) { - if (!TypesMatch (method.ReturnType, function.ReturnType, kotlinClass)) + if (!TypesMatch (method.ReturnType, function.ReturnType, kotlinFile)) continue; - if (!ParametersMatch (kotlinClass, method, function.ValueParameters)) + if (!ParametersMatch (kotlinFile, method, function.ValueParameters)) continue; return method; @@ -199,7 +222,15 @@ static MethodInfo FindJavaMethod (KotlinClass kotlinClass, KotlinFunction functi return null; } - static MethodInfo FindJavaPropertyGetter (KotlinClass kotlinClass, KotlinProperty property, ClassFile klass) + static FieldInfo FindJavaFieldProperty (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass) + { + var possible_methods = klass.Fields.Where (field => field.Name == property.Name && + TypesMatch (new TypeInfo (field.Descriptor, field.Descriptor), property.ReturnType, kotlinClass)); + + return possible_methods.FirstOrDefault (); + } + + static MethodInfo FindJavaPropertyGetter (KotlinFile 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) && @@ -209,7 +240,7 @@ static MethodInfo FindJavaPropertyGetter (KotlinClass kotlinClass, KotlinPropert return possible_methods.FirstOrDefault (); } - static MethodInfo FindJavaPropertySetter (KotlinClass kotlinClass, KotlinProperty property, ClassFile klass) + static MethodInfo FindJavaPropertySetter (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass) { var possible_methods = klass.Methods.Where (method => string.Compare (method.GetMethodNameWithoutSuffix (), $"set{property.Name}", true) == 0 && property.ReturnType != null && @@ -219,7 +250,7 @@ static MethodInfo FindJavaPropertySetter (KotlinClass kotlinClass, KotlinPropert return possible_methods.FirstOrDefault (); } - static bool ParametersMatch (KotlinClass kotlinClass, MethodInfo method, List kotlinParameters) + static bool ParametersMatch (KotlinFile kotlinClass, MethodInfo method, List kotlinParameters) { var java_parameters = method.GetFilteredParameters (); @@ -237,13 +268,13 @@ static bool ParametersMatch (KotlinClass kotlinClass, MethodInfo method, List t.Id == type.TypeParameter); diff --git a/src/Xamarin.Android.Tools.Bytecode/Methods.cs b/src/Xamarin.Android.Tools.Bytecode/Methods.cs index 187138ef9..3add9fcdd 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Methods.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Methods.cs @@ -31,6 +31,7 @@ public sealed class MethodInfo { public ClassFile DeclaringType {get; private set;} public MethodAccessFlags AccessFlags {get; set;} public AttributeCollection Attributes {get; private set;} + public string KotlinReturnType {get; set;} public MethodInfo (ConstantPool constantPool, ClassFile declaringType, Stream stream) { @@ -290,6 +291,7 @@ public sealed class ParameterInfo : IEquatable { public string Name; public int Position; public TypeInfo Type = new TypeInfo (); + public string KotlinType; public MethodParameterAccessFlags AccessFlags; diff --git a/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs b/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs index 32758428f..a5d5b35e8 100644 --- a/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs +++ b/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs @@ -332,6 +332,8 @@ XElement GetMethod (string element, string name, MethodInfo method, string retur var ret = returns != null ? new XAttribute ("return", SignatureToGenericJavaTypeName (returns)) : null; + if (!string.IsNullOrWhiteSpace (method.KotlinReturnType)) + ret?.SetValue (method.KotlinReturnType); var jniRet = returns != null ? new XAttribute ("jni-return", returns) : null; @@ -400,6 +402,8 @@ IEnumerable GetMethodParameters (MethodInfo method) genericType = genericType.Substring (1); } genericType = SignatureToGenericJavaTypeName (genericType); + if (!string.IsNullOrWhiteSpace (p.KotlinType)) + genericType = p.KotlinType; if (varargArray) { type += "..."; genericType += "..."; @@ -480,6 +484,9 @@ IEnumerable GetFields () var visibility = GetVisibility (field.AccessFlags); if (visibility == "private" || visibility == "") continue; + var type = new XAttribute ("type", SignatureToJavaTypeName (field.Descriptor)); + if (!string.IsNullOrWhiteSpace (field.KotlinType)) + type.SetValue (field.KotlinType); yield return new XElement ("field", new XAttribute ("deprecated", GetDeprecatedValue (field.Attributes)), new XAttribute ("final", (field.AccessFlags & FieldAccessFlags.Final) != 0), @@ -487,7 +494,7 @@ IEnumerable GetFields () new XAttribute ("static", (field.AccessFlags & FieldAccessFlags.Static) != 0), new XAttribute ("synthetic", (field.AccessFlags & FieldAccessFlags.Synthetic) != 0), new XAttribute ("transient", (field.AccessFlags & FieldAccessFlags.Transient) != 0), - new XAttribute ("type", SignatureToJavaTypeName (field.Descriptor)), + type, new XAttribute ("type-generic-aware", GetGenericType (field)), new XAttribute ("jni-signature", field.Descriptor), GetNotNull (field), diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs index 2da9a3d35..1bf1f7826 100644 --- a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs +++ b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs @@ -119,5 +119,87 @@ public void RenameSetterParameter () Assert.AreEqual ("value", p.Name); } + + [Test] + public void UnsignedMethods () + { + var klass = LoadClassFile ("UnsignedTypes.class"); + + var uint_method = klass.Methods.First (m => m.Name.Contains ('-') && m.GetParameters () [0].Type.BinaryName == "I"); + var ushort_method = klass.Methods.First (m => m.Name.Contains ('-') && m.GetParameters () [0].Type.BinaryName == "S"); + var ulong_method = klass.Methods.First (m => m.Name.Contains ('-') && m.GetParameters () [0].Type.BinaryName == "J"); + var ubyte_method = klass.Methods.First (m => m.Name.Contains ('-') && m.GetParameters () [0].Type.BinaryName == "B"); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.AreEqual ("uint", uint_method.GetParameters () [0].KotlinType); + Assert.AreEqual ("uint", uint_method.KotlinReturnType); + + Assert.AreEqual ("ushort", ushort_method.GetParameters () [0].KotlinType); + Assert.AreEqual ("ushort", ushort_method.KotlinReturnType); + + Assert.AreEqual ("ulong", ulong_method.GetParameters () [0].KotlinType); + Assert.AreEqual ("ulong", ulong_method.KotlinReturnType); + + Assert.AreEqual ("ubyte", ubyte_method.GetParameters () [0].KotlinType); + Assert.AreEqual ("ubyte", ubyte_method.KotlinReturnType); + } + + [Test] + public void UnsignedFields () + { + var klass = LoadClassFile ("UnsignedTypesKt.class"); + + var uint_field = klass.Fields.Single (m => m.Name == "UINT_CONST"); + var ushort_field = klass.Fields.Single (m => m.Name == "USHORT_CONST"); + var ulong_field = klass.Fields.Single (m => m.Name == "ULONG_CONST"); + var ubyte_field = klass.Fields.Single (m => m.Name == "UBYTE_CONST"); + + KotlinFixups.Fixup (new [] { klass }); + + Assert.AreEqual ("uint", uint_field.KotlinType); + Assert.AreEqual ("ushort", ushort_field.KotlinType); + Assert.AreEqual ("ulong", ulong_field.KotlinType); + Assert.AreEqual ("ubyte", ubyte_field.KotlinType); + } + + [Test] + public void UnsignedFieldsXml () + { + // Ensure Kotlin unsigned types end up in the xml + var klass = LoadClassFile ("UnsignedTypesKt.class"); + + KotlinFixups.Fixup (new [] { klass }); + + var xml = new XmlClassDeclarationBuilder (klass).ToXElement (); + + Assert.AreEqual ("uint", xml.Elements ("field").Single (f => f.Attribute ("name").Value == "UINT_CONST").Attribute ("type").Value); + Assert.AreEqual ("ushort", xml.Elements ("field").Single (f => f.Attribute ("name").Value == "USHORT_CONST").Attribute ("type").Value); + Assert.AreEqual ("ulong", xml.Elements ("field").Single (f => f.Attribute ("name").Value == "ULONG_CONST").Attribute ("type").Value); + Assert.AreEqual ("ubyte", xml.Elements ("field").Single (f => f.Attribute ("name").Value == "UBYTE_CONST").Attribute ("type").Value); + } + + [Test] + public void UnsignedMethodsXml () + { + // Ensure Kotlin unsigned types end up in the xml + var klass = LoadClassFile ("UnsignedTypes.class"); + + KotlinFixups.Fixup (new [] { klass }); + + var xml = new XmlClassDeclarationBuilder (klass).ToXElement (); + + Assert.AreEqual ("uint", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_uint-WZ4Q5Ns").Attribute ("return").Value); + Assert.AreEqual ("uint", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_uint-WZ4Q5Ns").Element ("parameter").Attribute ("type").Value); + + Assert.AreEqual ("ushort", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ushort-xj2QHRw").Attribute ("return").Value); + Assert.AreEqual ("ushort", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ushort-xj2QHRw").Element ("parameter").Attribute ("type").Value); + + Assert.AreEqual ("ulong", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ulong-VKZWuLQ").Attribute ("return").Value); + Assert.AreEqual ("ulong", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ulong-VKZWuLQ").Element ("parameter").Attribute ("type").Value); + + Assert.AreEqual ("ubyte", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ubyte-7apg3OU").Attribute ("return").Value); + Assert.AreEqual ("ubyte", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ubyte-7apg3OU").Element ("parameter").Attribute ("type").Value); + } } } diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.class b/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.class new file mode 100644 index 0000000000000000000000000000000000000000..629647b993f8443473c57d5fb689f45233f5c5b9 GIT binary patch literal 1633 zcma)6T~8B16g@NjUiz^uEtI07_(joDD5wt_6KgcKRtOYI0uN>jD=d^<(%B*;J`jJ3 z&-&n##s@?2%^zjFvur6~u-$ZL?m73&-Pt>5`}@z2p8yJ&VTdf5!q_nN^~Hl-T?D`w z)GcjK%QrN0BR^N)(%0+&JPf0iZOd*LX8y(guHH0ubko)v=N3O^2)?tdw=Ki88N6eq zY>6g&(+~`?%0;PfPiwSv21X*ay)3u9GBf{RmKG=uB{r?5Q>&V-l9cX5htKVA73N

y_nJWxg$~AK3J0?^rQgBq`k|Qc33?X}cTKb4v{V zCx&U*PZ>O8*;*Jw$SCk)n8fW?ywPoKU9&ZMbH20Zp`J21#0b_M2nnfqY@4X{DaO>J z!>0;QbA?l+@SqHQiVP(e^bic5i9=^%#f^C|kUlyr@JUwm9e?t3m6xNcC&MSb1;6T( zb2;r`zl;69zO$o4l6r#yHAvzRi4-@fkXYhA7x#6K1 zxSx_qH<=`{#3>i2I^qF!@c1M5(hCpCz7!ZW4$*d3mx^=@)Y8Z)Q8I199YKXonuyq` zpelVrjC#&x6Wh`|&#iSj>sZAw_1V@=U2iUG^#(DCinXRSYFg8f*d>*!)oQNkFAb>} zT4S2tA~k)69oI%Mp1R z{lt-FFiGRUNftp`Coc{KeUy(Q+(QkuON|JkUH1{cv_{O?huTApM3)+IBu@9ioiBus zd~x2k(~tg3YxFz&NcK=8*`-E``rYkZQkT{kboP<%p~k>}Ymm*5arcp@S-QbD{tZ{Y zUT8_wDHA>sxEAq{)uc^w^k)uypxjmXaBUTy5=NZgb=+v9(GqUr7O}X!3MOy|cUO@X y2nmb{WCT)9pa^7z>0y}mMj@Js3U4>srzzlKdyvyFKps@XabAp*j)K$L*U2#672 z&H@Pn;v*nQKx!7S39xK^_*o-^(R5&ahaq*o>+D5~N`NfB1 z7X5DLh`;n&G%tOIR%SqKLxVwgVIYkV!gM$Hf}25v@;DMxc?#Azw`qIzL~H*u?qIf(^{+Y(C>F^NTqkVHlzDiM}QN!*c`m#`$_5*rdp GiNrsSy3{xT literal 0 HcmV?d00001 From 7508321db6ab26f4852bfc828ad975c887d07bda Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Mon, 16 Dec 2019 09:15:55 -0600 Subject: [PATCH 2/5] [generator] Add support for unsigned types. --- .../Java.Interop/JniArgumentValue.cs | 8 +++++ .../JavaTypeReference.cs | 16 ++++++++-- .../CodeGenerator.cs | 2 +- .../JavaInteropCodeGenerator.cs | 16 +++++++--- .../Field.cs | 2 +- .../GenBase.cs | 2 ++ .../Method.cs | 2 +- .../ReturnValue.cs | 11 ++----- .../Symbols/ArraySymbol.cs | 2 ++ .../Symbols/CharSequenceSymbol.cs | 2 ++ .../Symbols/CollectionSymbol.cs | 2 ++ .../Symbols/EnumSymbol.cs | 2 ++ .../Symbols/FormatSymbol.cs | 6 +++- .../Symbols/GeneratedEnumSymbol.cs | 2 ++ .../Symbols/GenericSymbol.cs | 2 ++ .../Symbols/GenericTypeParameter.cs | 2 ++ .../Symbols/ISymbol.cs | 1 + .../Symbols/SimpleSymbol.cs | 4 +-- .../Symbols/StreamSymbol.cs | 2 ++ .../Symbols/StringSymbol.cs | 2 ++ .../Symbols/SymbolTable.cs | 8 +++++ .../Symbols/XmlPullParserSymbol.cs | 2 ++ .../Symbols/XmlResourceParserSymbol.cs | 2 ++ .../KotlinFixups.cs | 32 +++++++++++++------ .../generator/Utilities/TypeNameUtilities.cs | 23 +++++++++++++ 25 files changed, 124 insertions(+), 31 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniArgumentValue.cs b/src/Java.Interop/Java.Interop/JniArgumentValue.cs index 86248e312..1e9a6dbda 100644 --- a/src/Java.Interop/Java.Interop/JniArgumentValue.cs +++ b/src/Java.Interop/Java.Interop/JniArgumentValue.cs @@ -34,6 +34,8 @@ public JniArgumentValue (sbyte value) b = value; } + public JniArgumentValue (byte value) : this ((sbyte)value) { } + public JniArgumentValue (char value) { this = new JniArgumentValue (); @@ -46,18 +48,24 @@ public JniArgumentValue (short value) s = value; } + public JniArgumentValue (ushort value) : this ((short)value) { } + public JniArgumentValue (int value) { this = new JniArgumentValue (); i = value; } + public JniArgumentValue (uint value) : this ((int)value) { } + public JniArgumentValue (long value) { this = new JniArgumentValue (); j = value; } + public JniArgumentValue (ulong value) : this ((long) value) { } + public JniArgumentValue (float value) { this = new JniArgumentValue (); diff --git a/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeReference.cs b/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeReference.cs index 61cd2dbca..877b7d58b 100644 --- a/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeReference.cs +++ b/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeReference.cs @@ -16,7 +16,11 @@ public class JavaTypeReference public static readonly JavaTypeReference Float; public static readonly JavaTypeReference Double; public static readonly JavaTypeReference GenericWildcard; - + public static readonly JavaTypeReference UInt; + public static readonly JavaTypeReference UShort; + public static readonly JavaTypeReference ULong; + public static readonly JavaTypeReference UByte; + internal static JavaTypeReference GetSpecialType (string name) { switch (name) { @@ -29,6 +33,10 @@ internal static JavaTypeReference GetSpecialType (string name) case "long": return Long; case "float": return Float; case "double": return Double; + case "uint": return UInt; + case "ushort": return UShort; + case "ulong": return ULong; + case "ubyte": return UByte; case "?": return GenericWildcard; } return null; @@ -46,8 +54,12 @@ static JavaTypeReference () Float = new JavaTypeReference ("float"); Double = new JavaTypeReference ("double"); GenericWildcard = new JavaTypeReference ("?"); + UInt = new JavaTypeReference ("uint"); + UShort = new JavaTypeReference ("ushort"); + ULong = new JavaTypeReference ("ulong"); + UByte = new JavaTypeReference ("ubyte"); } - + JavaTypeReference (string specialName) { SpecialName = specialName; diff --git a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs index f8b7c7005..a0a1d77ea 100644 --- a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs +++ b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs @@ -1212,7 +1212,7 @@ public void WriteMethodInvokerBody (Method method, string indent) writer.WriteLine ("{0}{1}", indent, prep); WriteParameterListCallArgs (method.Parameters, indent, invoker: true); string env_method = "Call" + method.RetVal.CallMethodPrefix + "Method"; - string call = "JNIEnv." + env_method + " (" + + string call = method.RetVal.ReturnCast + "JNIEnv." + env_method + " (" + Context.ContextType.GetObjectHandleProperty ("this") + ", " + method.EscapedIdName + method.Parameters.GetCallArgs (opt, invoker: true) + ")"; if (method.IsVoid) writer.WriteLine ("{0}{1};", indent, call); diff --git a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/JavaInteropCodeGenerator.cs b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/JavaInteropCodeGenerator.cs index 5032a5085..7554007d7 100644 --- a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/JavaInteropCodeGenerator.cs +++ b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/JavaInteropCodeGenerator.cs @@ -19,6 +19,10 @@ static string GetInvokeType (string type) case "Short": return "Int16"; case "Long": return "Int64"; case "Float": return "Single"; + case "UInt": return "Int32"; + case "UShort": return "Int16"; + case "ULong": return "Int64"; + case "UByte": return "SByte"; default: return type; } } @@ -171,7 +175,7 @@ internal override void WriteMethodBody (Method method, string indent, GenBase ty if (!method.IsVoid) { var r = invokeType == "Object" ? "__rm.Handle" : "__rm"; - writer.WriteLine ("{0}return {1};", indent, method.RetVal.FromNative (opt, r, true)); + writer.WriteLine ("{0}return {2}{1};", indent, method.RetVal.FromNative (opt, r, true), method.RetVal.ReturnCast); } indent = oldindent; @@ -196,19 +200,21 @@ internal override void WriteFieldGetBody (Field field, string indent, GenBase ty var invoke = "Get{0}Value"; invoke = string.Format (invoke, invokeType); - writer.WriteLine ("{0}var __v = _members.{1}.{2} (__id{3});", + writer.WriteLine ("{0}var __v = {4}_members.{1}.{2} (__id{3});", indent, indirect, invoke, - field.IsStatic ? "" : ", this"); + field.IsStatic ? "" : ", this", + field.Symbol.ReturnCast); if (field.Symbol.IsArray) { writer.WriteLine ("{0}return global::Android.Runtime.JavaArray<{1}>.FromJniHandle (__v.Handle, JniHandleOwnership.TransferLocalRef);", indent, opt.GetOutputName (field.Symbol.ElementType)); } else if (field.Symbol.NativeType != field.Symbol.FullName) { - writer.WriteLine ("{0}return {1};", + writer.WriteLine ("{0}return {2}{1};", indent, - field.Symbol.FromNative (opt, invokeType != "Object" ? "__v" : "__v.Handle", true)); + field.Symbol.FromNative (opt, invokeType != "Object" ? "__v" : "__v.Handle", true), + field.Symbol.ReturnCast); } else { writer.WriteLine ("{0}return __v;", indent); } diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs index b74ba3d8a..3f5b51fac 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs @@ -23,7 +23,7 @@ public class Field : ApiVersionsSupport.IApiAvailability public string Value { get; set; } public string Visibility { get; set; } - internal string GetMethodPrefix => (Symbol is SimpleSymbol || Symbol.IsEnum) ? StringRocks.MemberToPascalCase (Symbol.JavaName) : "Object"; + internal string GetMethodPrefix => TypeNameUtilities.GetCallPrefix (Symbol); internal string ID => JavaName + "_jfieldId"; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs index ec22211c1..a20d331f4 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs @@ -33,6 +33,8 @@ protected GenBase (GenBaseSupport support) public string DefaultValue { get; set; } public bool HasVirtualMethods { get; set; } + public string ReturnCast => string.Empty; + // This means Ctors/Methods/Properties/Fields has not been populated yet. // If this type is retrieved from the SymbolTable, it will call PopulateAction // to fill in members before returning it to the user. diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Method.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Method.cs index 67e857b92..60c84c6aa 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Method.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Method.cs @@ -108,7 +108,7 @@ internal string CalculateEventName (Func checkNameDuplicate) public string EscapedCallbackName => IdentifierValidator.CreateValidIdentifier ($"cb_{JavaName}{IDSignature}", true); - public string EscapedIdName => "id_" + JavaName.Replace ("<", "_x60_").Replace (">", "_x62_") + IDSignature; + public string EscapedIdName => IdentifierValidator.CreateValidIdentifier ($"id_{JavaName}{IDSignature}", true); internal void FillReturnType () { diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ReturnValue.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ReturnValue.cs index 8ee014132..bcdacec12 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ReturnValue.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ReturnValue.cs @@ -22,14 +22,7 @@ public ReturnValue (Method owner, string java_type, string managed_type, bool is this.is_enumified = isEnumified; } - public string CallMethodPrefix { - get { - if (sym is SimpleSymbol || sym.IsEnum) - return StringRocks.MemberToPascalCase (sym.JavaName); - else - return "Object"; - } - } + public string CallMethodPrefix => TypeNameUtilities.GetCallPrefix (sym); public string DefaultValue { get { return sym.DefaultValue; } @@ -89,6 +82,8 @@ public string RawJavaType { get { return raw_type; } } + public string ReturnCast => sym?.ReturnCast ?? string.Empty; + public string FromNative (CodeGenerationOptions opt, string var_name, bool owned) { if (!string.IsNullOrEmpty (managed_type) && (sym is ClassGen || sym is InterfaceGen)) { diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ArraySymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ArraySymbol.cs index cb59c5b16..b42412fe6 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ArraySymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ArraySymbol.cs @@ -64,6 +64,8 @@ public bool IsArray { get { return true; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return sym.GetObjectHandleProperty (variable); diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/CharSequenceSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/CharSequenceSymbol.cs index 8a026cd4d..329e66eb3 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/CharSequenceSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/CharSequenceSymbol.cs @@ -41,6 +41,8 @@ public string ElementType { get { return null; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return $"((global::Java.Lang.Object) {variable}).Handle"; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/CollectionSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/CollectionSymbol.cs index 42ef65a5f..e97dabc92 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/CollectionSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/CollectionSymbol.cs @@ -56,6 +56,8 @@ public bool MayHaveManagedGenericArguments { get { return true; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return $"((global::Java.Lang.Object) {variable}).Handle"; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/EnumSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/EnumSymbol.cs index a404cbdd1..8782b556b 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/EnumSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/EnumSymbol.cs @@ -46,6 +46,8 @@ public string ElementType { get { return null; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return null; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/FormatSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/FormatSymbol.cs index 465d6be82..859a5f162 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/FormatSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/FormatSymbol.cs @@ -13,8 +13,9 @@ public class FormatSymbol : ISymbol { string native_type; string to_fmt; string type; + string return_cast; - public FormatSymbol (string default_value, string java_type, string jni_type, string native_type, string type, string from_fmt, string to_fmt) + public FormatSymbol (string default_value, string java_type, string jni_type, string native_type, string type, string from_fmt, string to_fmt, string returnCast) { this.default_value = default_value; this.java_type = java_type; @@ -23,6 +24,7 @@ public FormatSymbol (string default_value, string java_type, string jni_type, st this.type = type; this.from_fmt = from_fmt; this.to_fmt = to_fmt; + this.return_cast = returnCast ?? string.Empty; } public string DefaultValue { @@ -61,6 +63,8 @@ public string ElementType { get { return null; } } + public string ReturnCast => return_cast; + public string GetObjectHandleProperty (string variable) { return null; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GeneratedEnumSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GeneratedEnumSymbol.cs index 09f61bd61..55638ddd0 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GeneratedEnumSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GeneratedEnumSymbol.cs @@ -77,6 +77,8 @@ public string ElementType { get { return enum_type; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return null; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GenericSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GenericSymbol.cs index 0865d333f..2c9fc2a47 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GenericSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GenericSymbol.cs @@ -63,6 +63,8 @@ public ISymbol [] TypeParams { get { return type_params; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return gen.GetObjectHandleProperty (variable); diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GenericTypeParameter.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GenericTypeParameter.cs index b1642f290..d1f901910 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GenericTypeParameter.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/GenericTypeParameter.cs @@ -63,6 +63,8 @@ public string ElementType { get { return null; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return null; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ISymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ISymbol.cs index c8831d722..c5182c83a 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ISymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ISymbol.cs @@ -13,6 +13,7 @@ public interface ISymbol { bool IsEnum { get; } bool IsArray { get; } string ElementType { get; } + string ReturnCast { get; } string GetObjectHandleProperty (string variable); diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SimpleSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SimpleSymbol.cs index e5e04eca6..c8d3e9e2b 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SimpleSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SimpleSymbol.cs @@ -4,8 +4,8 @@ namespace MonoDroid.Generation { public class SimpleSymbol : FormatSymbol { - public SimpleSymbol (string default_value, string java_type, string type, string jni_type, string native_type = null, string from_fmt="{0}", string to_fmt="{0}") - : base (default_value, java_type, jni_type, native_type ?? type, type, from_fmt, to_fmt) + public SimpleSymbol (string default_value, string java_type, string type, string jni_type, string native_type = null, string from_fmt="{0}", string to_fmt="{0}", string returnCast = null) + : base (default_value, java_type, jni_type, native_type ?? type, type, from_fmt, to_fmt, returnCast) { } diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/StreamSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/StreamSymbol.cs index 78068e45e..bf660341d 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/StreamSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/StreamSymbol.cs @@ -54,6 +54,8 @@ public string ElementType { get { return null; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return $"((global::Java.Lang.Object) {variable}).Handle"; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/StringSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/StringSymbol.cs index fe50266c2..d78799149 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/StringSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/StringSymbol.cs @@ -44,6 +44,8 @@ public string ElementType { get { return null; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return $"((global::Java.Lang.Object) {variable}).Handle"; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SymbolTable.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SymbolTable.cs index 5f6465209..8d3f279ec 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SymbolTable.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SymbolTable.cs @@ -33,6 +33,10 @@ public class SymbolTable { "int", "long", "short", + "ubyte", + "uint", + "ulong", + "ushort", "void", }; @@ -55,6 +59,10 @@ public SymbolTable () AddType (new SimpleSymbol ("0", "int", "int", "I")); AddType (new SimpleSymbol ("0L", "long", "long", "J")); AddType (new SimpleSymbol ("0", "short", "short", "S")); + AddType (new SimpleSymbol ("0", "uint", "uint", "I", returnCast: "(uint)")); + AddType (new SimpleSymbol ("0", "ushort", "ushort", "S", returnCast: "(ushort)")); + AddType (new SimpleSymbol ("0", "ulong", "ulong", "J", returnCast: "(ulong)")); + AddType (new SimpleSymbol ("0", "ubyte", "byte", "B", returnCast: "(byte)")); AddType ("Android.Graphics.Color", new ColorSymbol ()); char_seq = new CharSequenceSymbol (); instream_sym = new StreamSymbol ("InputStream"); diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/XmlPullParserSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/XmlPullParserSymbol.cs index c497d31b3..aff529793 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/XmlPullParserSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/XmlPullParserSymbol.cs @@ -41,6 +41,8 @@ public string ElementType { get { return null; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return $"((global::Java.Lang.Object) {variable}).Handle"; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/XmlResourceParserSymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/XmlResourceParserSymbol.cs index 08a1b85b6..fa8df65f4 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/XmlResourceParserSymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/XmlResourceParserSymbol.cs @@ -41,6 +41,8 @@ public string ElementType { get { return null; } } + public string ReturnCast => string.Empty; + public string GetObjectHandleProperty (string variable) { return $"((global::Java.Lang.Object) {variable}).Handle"; diff --git a/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs b/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs index fc901fc5c..9cc702c65 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs @@ -12,6 +12,8 @@ public static void Fixup (List gens) { foreach (var c in gens.OfType ()) FixupClass (c); + foreach (var i in gens.OfType ()) + FixupInterface (i); } private static void FixupClass (ClassGen c) @@ -20,9 +22,8 @@ private static void FixupClass (ClassGen c) // inaccessible from Java, like `add-impl` and `add-V5j3Lk8`. // We need to generate C# compatible names as well as prevent overriding // them as we cannot generate JCW for them. - var invalid_methods = c.Methods.Where (m => m.IsKotlinNameMangled).ToList (); - foreach (var method in invalid_methods) { + foreach (var method in c.Methods.Where (m => m.IsKotlinNameMangled)) { // If the method is virtual, mark it as !virtual as it can't be overridden in Java if (!method.IsFinal) @@ -31,15 +32,26 @@ private static void FixupClass (ClassGen c) if (method.IsVirtual) method.IsVirtual = false; - // Only run this if it's the default name (ie: not a user's "managedName") - if (method.Name == StringRocks.MemberToPascalCase (method.JavaName).Replace ('-', '_')) { - // We want to remove the hyphen and anything afterwards to fix mangled names, - // but a previous step converted it to an underscore. Remove the final - // underscore and anything after it. - var index = method.Name.LastIndexOf ('_'); + FixMethodName (method); + } + } + + private static void FixupInterface (InterfaceGen gen) + { + foreach (var method in gen.Methods.Where (m => m.IsKotlinNameMangled)) + FixMethodName (method); + } + + private static void FixMethodName (Method method) + { + // Only run this if it's the default name (ie: not a user's "managedName") + if (method.Name == StringRocks.MemberToPascalCase (method.JavaName).Replace ('-', '_')) { + // We want to remove the hyphen and anything afterwards to fix mangled names, + // but a previous step converted it to an underscore. Remove the final + // underscore and anything after it. + var index = method.Name.LastIndexOf ('_'); - method.Name = method.Name.Substring (0, index); - } + method.Name = method.Name.Substring (0, index); } } } diff --git a/tools/generator/Utilities/TypeNameUtilities.cs b/tools/generator/Utilities/TypeNameUtilities.cs index 371ab068e..56a620f0a 100644 --- a/tools/generator/Utilities/TypeNameUtilities.cs +++ b/tools/generator/Utilities/TypeNameUtilities.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using MonoDroid.Utils; namespace MonoDroid.Generation { @@ -101,5 +102,27 @@ public static string StudlyCase (string name) } return builder.ToString (); } + + public static string GetCallPrefix (ISymbol symbol) + { + if (symbol is SimpleSymbol || symbol.IsEnum) { + var ret = StringRocks.MemberToPascalCase (symbol.JavaName); + + // We do not have unsigned versions of GetIntValue, etc. + // We use the signed versions and cast the result + if (ret == "Uint") + return "Int"; + if (ret == "Ushort") + return "Short"; + if (ret == "Ulong") + return "Long"; + if (ret == "Ubyte") + return "Byte"; + + return ret; + + } else + return "Object"; + } } } From 0ef3d55e67f433e2aad2c2a3a94be57fef300b65 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Wed, 18 Dec 2019 09:38:06 -0600 Subject: [PATCH 3/5] Add support for Kotlin unsigned array types. --- .../JavaNativeTypeManager.cs | 6 ++++++ .../Kotlin/KotlinFixups.cs | 8 ++++++++ .../MethodBase.cs | 5 +---- .../Symbols/ArraySymbol.cs | 17 ++++++++++++++++- .../KotlinFixups.cs | 7 +++++-- 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs b/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs index 41ea7cd22..97c34f0b1 100644 --- a/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs +++ b/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs @@ -232,10 +232,16 @@ static string GetPrimitiveClass (Type type) return "F"; if (type == typeof (int)) return "I"; + if (type == typeof (uint)) + return "I"; if (type == typeof (long)) return "J"; + if (type == typeof (ulong)) + return "J"; if (type == typeof (short)) return "S"; + if (type == typeof (ushort)) + return "S"; if (type == typeof (bool)) return "Z"; return null; diff --git a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs index 053c73ca2..edc893233 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs @@ -302,12 +302,20 @@ static string GetKotlinType (string jvmType, string kotlinClass) // Handle erasure of Kotlin unsigned types if (jvmType == "I" && kotlinClass == "kotlin/UInt;") return "uint"; + if (jvmType == "[I" && kotlinClass == "kotlin/UIntArray;") + return "uint[]"; if (jvmType == "S" && kotlinClass == "kotlin/UShort;") return "ushort"; + if (jvmType == "[S" && kotlinClass == "kotlin/UShortArray;") + return "ushort[]"; if (jvmType == "J" && kotlinClass == "kotlin/ULong;") return "ulong"; + if (jvmType == "[J" && kotlinClass == "kotlin/ULongArray;") + return "ulong[]"; if (jvmType == "B" && kotlinClass == "kotlin/UByte;") return "ubyte"; + if (jvmType == "[B" && kotlinClass == "kotlin/UByteArray;") + return "ubyte[]"; return null; } diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs index 4a738d501..be0869f3a 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs @@ -87,10 +87,7 @@ public bool IsKotlinNameMangled { if (method.JavaName.IndexOf ("-impl") >= 0) return true; - var index = method.JavaName.IndexOf ('-'); - - // `add-V5j3Lk8` is always a 7 character hashcode - return index >= 0 && method.JavaName.Length - index == 8; + return method.JavaName.Length >= 8 && method.JavaName [method.JavaName.Length - 8] == '-'; } return false; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ArraySymbol.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ArraySymbol.cs index b42412fe6..deb5f82ad 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ArraySymbol.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/ArraySymbol.cs @@ -122,10 +122,25 @@ public string[] PreCallback (CodeGenerationOptions opt, string var_name, bool ow public string[] PreCall (CodeGenerationOptions opt, string var_name) { - return new string[] { String.Format ("IntPtr {0} = JNIEnv.NewArray ({1});", opt.GetSafeIdentifier (TypeNameUtilities.GetNativeName (var_name)), opt.GetSafeIdentifier (var_name)) }; + + return new string[] { String.Format ("IntPtr {0} = JNIEnv.NewArray ({2}{1});", opt.GetSafeIdentifier (TypeNameUtilities.GetNativeName (var_name)), opt.GetSafeIdentifier (var_name), GetPreCallCast ()) }; } public bool NeedsPrep { get { return true; } } + + string GetPreCallCast () + { + switch (sym.FullName) { + case "uint": + return "(int[])(object)"; + case "ushort": + return "(short[])(object)"; + case "ulong": + return "(long[])(object)"; + default: + return string.Empty; + } + } } } diff --git a/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs b/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs index 9cc702c65..6545a0818 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs @@ -49,9 +49,12 @@ private static void FixMethodName (Method method) // We want to remove the hyphen and anything afterwards to fix mangled names, // but a previous step converted it to an underscore. Remove the final // underscore and anything after it. - var index = method.Name.LastIndexOf ('_'); + var index = method.Name.IndexOf ("_impl"); - method.Name = method.Name.Substring (0, index); + if (index >= 0) + method.Name = method.Name.Substring (0, index); + else + method.Name = method.Name.Substring (0, method.Name.Length - 8); } } } From dd8a6a09a480700583b70976a71f0e6dd43ae892 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Wed, 18 Dec 2019 14:12:01 -0600 Subject: [PATCH 4/5] Add unit tests. --- .../Xamarin.Android.Tools.Bytecode.csproj | 2 +- .../KotlinFixupsTests.cs | 36 ++++- .../kotlin/UnsignedTypes.class | Bin 1633 -> 2731 bytes .../kotlin/UnsignedTypes.kt | 4 + ...iteKotlinUnsignedArrayTypeMethodsClass.txt | 90 ++++++++++++ ...KotlinUnsignedArrayTypePropertiesClass.txt | 130 ++++++++++++++++++ .../WriteKotlinUnsignedTypeMethodsClass.txt | 70 ++++++++++ ...WriteKotlinUnsignedTypePropertiesClass.txt | 110 +++++++++++++++ .../Unit-Tests/CodeGeneratorTests.cs | 88 ++++++++++++ .../Unit-Tests/KotlinFixupsTests.cs | 2 +- 10 files changed, 526 insertions(+), 6 deletions(-) create mode 100644 tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedArrayTypeMethodsClass.txt create mode 100644 tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedArrayTypePropertiesClass.txt create mode 100644 tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedTypeMethodsClass.txt create mode 100644 tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedTypePropertiesClass.txt diff --git a/src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj b/src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj index 0f65a140b..b521d79b5 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj +++ b/src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj @@ -13,7 +13,7 @@ - + diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs index 1bf1f7826..4cef70ac6 100644 --- a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs +++ b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs @@ -125,10 +125,14 @@ public void UnsignedMethods () { var klass = LoadClassFile ("UnsignedTypes.class"); - var uint_method = klass.Methods.First (m => m.Name.Contains ('-') && m.GetParameters () [0].Type.BinaryName == "I"); - var ushort_method = klass.Methods.First (m => m.Name.Contains ('-') && m.GetParameters () [0].Type.BinaryName == "S"); - var ulong_method = klass.Methods.First (m => m.Name.Contains ('-') && m.GetParameters () [0].Type.BinaryName == "J"); - var ubyte_method = klass.Methods.First (m => m.Name.Contains ('-') && m.GetParameters () [0].Type.BinaryName == "B"); + var uint_method = klass.Methods.Single (m => m.Name.Contains ("foo_uint-")); + var ushort_method = klass.Methods.Single (m => m.Name.Contains ("foo_ushort-")); + var ulong_method = klass.Methods.Single (m => m.Name.Contains ("foo_ulong-")); + var ubyte_method = klass.Methods.Single (m => m.Name.Contains ("foo_ubyte-")); + var uintarray_method = klass.Methods.Single (m => m.Name.Contains ("foo_uintarray-")); + var ushortarray_method = klass.Methods.Single (m => m.Name.Contains ("foo_ushortarray-")); + var ulongarray_method = klass.Methods.Single (m => m.Name.Contains ("foo_ulongarray-")); + var ubytearray_method = klass.Methods.Single (m => m.Name.Contains ("foo_ubytearray-")); KotlinFixups.Fixup (new [] { klass }); @@ -143,6 +147,18 @@ public void UnsignedMethods () Assert.AreEqual ("ubyte", ubyte_method.GetParameters () [0].KotlinType); Assert.AreEqual ("ubyte", ubyte_method.KotlinReturnType); + + Assert.AreEqual ("uint[]", uintarray_method.GetParameters () [0].KotlinType); + Assert.AreEqual ("uint[]", uintarray_method.KotlinReturnType); + + Assert.AreEqual ("ushort[]", ushortarray_method.GetParameters () [0].KotlinType); + Assert.AreEqual ("ushort[]", ushortarray_method.KotlinReturnType); + + Assert.AreEqual ("ulong[]", ulongarray_method.GetParameters () [0].KotlinType); + Assert.AreEqual ("ulong[]", ulongarray_method.KotlinReturnType); + + Assert.AreEqual ("ubyte[]", ubytearray_method.GetParameters () [0].KotlinType); + Assert.AreEqual ("ubyte[]", ubytearray_method.KotlinReturnType); } [Test] @@ -200,6 +216,18 @@ public void UnsignedMethodsXml () Assert.AreEqual ("ubyte", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ubyte-7apg3OU").Attribute ("return").Value); Assert.AreEqual ("ubyte", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ubyte-7apg3OU").Element ("parameter").Attribute ("type").Value); + + Assert.AreEqual ("uint[]", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_uintarray--ajY-9A").Attribute ("return").Value); + Assert.AreEqual ("uint[]", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_uintarray--ajY-9A").Element ("parameter").Attribute ("type").Value); + + Assert.AreEqual ("ushort[]", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ushortarray-rL5Bavg").Attribute ("return").Value); + Assert.AreEqual ("ushort[]", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ushortarray-rL5Bavg").Element ("parameter").Attribute ("type").Value); + + Assert.AreEqual ("ulong[]", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ulongarray-QwZRm1k").Attribute ("return").Value); + Assert.AreEqual ("ulong[]", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ulongarray-QwZRm1k").Element ("parameter").Attribute ("type").Value); + + Assert.AreEqual ("ubyte[]", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ubytearray-GBYM_sE").Attribute ("return").Value); + Assert.AreEqual ("ubyte[]", xml.Elements ("method").Single (f => f.Attribute ("name").Value == "foo_ubytearray-GBYM_sE").Element ("parameter").Attribute ("type").Value); } } } diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.class b/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.class index 629647b993f8443473c57d5fb689f45233f5c5b9..84f1c4edcb83b61b57fe8a3a4bda89166f9b1a40 100644 GIT binary patch literal 2731 zcma)7T~pge6g?|rV-#%5=F5D9Z-Naq1}7vP1cFd8A92cRPVW3C~FLiNJ@2C;?4ept;G#mAloi}QZj9=o%jw>>4~`h z#D{!p>&5nah7Q|VC>h5h?pl0}xb%c;?!?~9{Ws+-eQ$+&aA8sCwpcozCMrD@s&FSB zsb|OcH+LC&?B(<^s%fehiG-Sen|Qp!;EUxl@f>Y?B3m+#lX=ZLFjd{)N!2h)mTKuG zdfzBn8|6Y_$q$)f*hyXfyqF|C%{0_PGGkaKtvtLY4o|eh(^smg7BymIxTC-@ z9n0Q4k)>;%9U*hP6yNK_2u5WLV~jx(`|ERaRrv%nyO>tbDM|DRJMkQ)QxZdOEl(BN z*8BbK;=-v-TZ`u?lad%JG?AkU?Rom``a8aA)6(%AWl<7?Z&^2V>j{G=CiaxToQyUk zDaX|cTGuS~NVQZd7^!&fp;BaG2$6YUn-C_|9HTu*M+=Poe0BM~EJY<*Qm!s#$uC~K zvUn9fzsH>wzx!Url#c)L$v1qGzdd?&nUdyN+WYw@pQK1@LB%s5&9|j|idW2QQQP)8 zw(qY`?CO%JZT@z}Phba;WCxU8@Q%_+N&y0c4j3e|utSa=a>uw-j z{_A*kksB;a4WJfGbOXCiuLl#1>bg)(Y)5n%S{S-4;iN zD61RVM!9&PnXlD@0(m0Y(xF<|Q%zmij&pFkY*^GoGsd~jb@H#ct%G6mCR*zXbu@XW zR5lN_XSxW}@2uJTj~*5v(FGKL0fgZLHF>O&^^j(XW@+%F81a%uANlAeB1>a$1%)3S zWC@0hJA{YP*@A}BL?eJ;(|v>+YJ}{4C@pA2nrMU(sqI6u8;>;di~Y53KDrueblLmp zZb75FiAE3QyV<$)G}P#`_YrMDqqm7hKL#iZqCS@C#6^AhXCp(OFh2WhT~>VPyQbu? zRSEszVS;wEPHa(4gEVi@(@yQ&Ho}<1REr^^4MW_!J;XF(Er#fC7$Saqh#Abb7-FDd zh%asn!NhMT5yV*}X%;urJM%l{f1{V{*EMMCcUB0_L3prfvod%^^GCuK;6*A2PX_nx z{|8vCS`RaLgfGd9$2l;LudtLujH82NnPZNl$9|DHzUGK>^m07m_=aPKW0u2bqu+9L zajD=d^<(%B*;J`jJ3 z&-&n##s@?2%^zjFvur6~u-$ZL?m73&-Pt>5`}@z2p8yJ&VTdf5!q_nN^~Hl-T?D`w z)GcjK%QrN0BR^N)(%0+&JPf0iZOd*LX8y(guHH0ubko)v=N3O^2)?tdw=Ki88N6eq zY>6g&(+~`?%0;PfPiwSv21X*ay)3u9GBf{RmKG=uB{r?5Q>&V-l9cX5htKVA73N

y_nJWxg$~AK3J0?^rQgBq`k|Qc33?X}cTKb4v{V zCx&U*PZ>O8*;*Jw$SCk)n8fW?ywPoKU9&ZMbH20Zp`J21#0b_M2nnfqY@4X{DaO>J z!>0;QbA?l+@SqHQiVP(e^bic5i9=^%#f^C|kUlyr@JUwm9e?t3m6xNcC&MSb1;6T( zb2;r`zl;69zO$o4l6r#yHAvzRi4-@fkXYhA7x#6K1 zxSx_qH<=`{#3>i2I^qF!@c1M5(hCpCz7!ZW4$*d3mx^=@)Y8Z)Q8I199YKXonuyq` zpelVrjC#&x6Wh`|&#iSj>sZAw_1V@=U2iUG^#(DCinXRSYFg8f*d>*!)oQNkFAb>} zT4S2tA~k)69oI%Mp1R z{lt-FFiGRUNftp`Coc{KeUy(Q+(QkuON|JkUH1{cv_{O?huTApM3)+IBu@9ioiBus zd~x2k(~tg3YxFz&NcK=8*`-E``rYkZQkT{kboP<%p~k>}Ymm*5arcp@S-QbD{tZ{Y zUT8_wDHA>sxEAq{)uc^w^k)uypxjmXaBUTy5=NZgb=+v9(GqUr7O}X!3MOy|cUO@X y2nmb{WCT)9pa^7z CodeGenerationTarget.JavaInterop1; + + [Test] + public void WriteKotlinUnsignedTypeMethodsClass () + { + var @class = new TestClass ("Object", "java.code.MyClass"); + + @class.AddMethod (SupportTypeBuilder.CreateMethod (@class, "Echo", options, "uint", false, false, new Parameter ("value", "uint", "uint", false))); + @class.AddMethod (SupportTypeBuilder.CreateMethod (@class, "Echo", options, "ushort", false, false, new Parameter ("value", "ushort", "ushort", false))); + @class.AddMethod (SupportTypeBuilder.CreateMethod (@class, "Echo", options, "ulong", false, false, new Parameter ("value", "ulong", "ulong", false))); + @class.AddMethod (SupportTypeBuilder.CreateMethod (@class, "Echo", options, "ubyte", false, false, new Parameter ("value", "ubyte", "byte", false))); + + // Kotlin methods with unsigned types are name-mangled and don't support virtual + foreach (var m in @class.Methods) + m.IsVirtual = false; + + generator.Context.ContextTypes.Push (@class); + generator.WriteClass (@class, string.Empty, new GenerationInfo ("", "", "MyAssembly")); + generator.Context.ContextTypes.Pop (); + + Assert.AreEqual (GetTargetedExpected (nameof (WriteKotlinUnsignedTypeMethodsClass)), writer.ToString ().NormalizeLineEndings ()); + } + + [Test] + public void WriteKotlinUnsignedTypePropertiesClass () + { + var @class = new TestClass ("Object", "java.code.MyClass"); + + @class.Properties.Add (SupportTypeBuilder.CreateProperty (@class, "UIntProp", "uint", options, false, false)); + @class.Properties.Add (SupportTypeBuilder.CreateProperty (@class, "UShortProp", "ushort", options, false, false)); + @class.Properties.Add (SupportTypeBuilder.CreateProperty (@class, "ULongProp", "ulong", options, false, false)); + @class.Properties.Add (SupportTypeBuilder.CreateProperty (@class, "UByteProp", "ubyte", options, false, false)); + + // Kotlin methods with unsigned types are name-mangled and don't support virtual + foreach (var m in @class.Properties) { + m.Getter.IsVirtual = false; + m.Setter.IsVirtual = false; + } + + generator.Context.ContextTypes.Push (@class); + generator.WriteClass (@class, string.Empty, new GenerationInfo ("", "", "MyAssembly")); + generator.Context.ContextTypes.Pop (); + + Assert.AreEqual (GetTargetedExpected (nameof (WriteKotlinUnsignedTypePropertiesClass)), writer.ToString ().NormalizeLineEndings ()); + } + + [Test] + public void WriteKotlinUnsignedArrayTypeMethodsClass () + { + var @class = new TestClass ("Object", "java.code.MyClass"); + + @class.AddMethod (SupportTypeBuilder.CreateMethod (@class, "Echo", options, "uint[]", false, false, new Parameter ("value", "uint[]", "uint[]", false))); + @class.AddMethod (SupportTypeBuilder.CreateMethod (@class, "Echo", options, "ushort[]", false, false, new Parameter ("value", "ushort[]", "ushort[]", false))); + @class.AddMethod (SupportTypeBuilder.CreateMethod (@class, "Echo", options, "ulong[]", false, false, new Parameter ("value", "ulong[]", "ulong[]", false))); + @class.AddMethod (SupportTypeBuilder.CreateMethod (@class, "Echo", options, "ubyte[]", false, false, new Parameter ("value", "ubyte[]", "byte[]", false))); + + // Kotlin methods with unsigned types are name-mangled and don't support virtual + foreach (var m in @class.Methods) + m.IsVirtual = false; + + generator.Context.ContextTypes.Push (@class); + generator.WriteClass (@class, string.Empty, new GenerationInfo ("", "", "MyAssembly")); + generator.Context.ContextTypes.Pop (); + + Assert.AreEqual (GetTargetedExpected (nameof (WriteKotlinUnsignedArrayTypeMethodsClass)), writer.ToString ().NormalizeLineEndings ()); + } + + [Test] + public void WriteKotlinUnsignedArrayTypePropertiesClass () + { + var @class = new TestClass ("Object", "java.code.MyClass"); + + @class.Properties.Add (SupportTypeBuilder.CreateProperty (@class, "UIntProp", "uint[]", options, false, false)); + @class.Properties.Add (SupportTypeBuilder.CreateProperty (@class, "UShortProp", "ushort[]", options, false, false)); + @class.Properties.Add (SupportTypeBuilder.CreateProperty (@class, "ULongProp", "ulong[]", options, false, false)); + @class.Properties.Add (SupportTypeBuilder.CreateProperty (@class, "UByteProp", "ubyte[]", options, false, false)); + + // Kotlin methods with unsigned types are name-mangled and don't support virtual + foreach (var m in @class.Properties) { + m.Getter.IsVirtual = false; + m.Setter.IsVirtual = false; + } + + generator.Context.ContextTypes.Push (@class); + generator.WriteClass (@class, string.Empty, new GenerationInfo ("", "", "MyAssembly")); + generator.Context.ContextTypes.Pop (); + + Assert.AreEqual (GetTargetedExpected (nameof (WriteKotlinUnsignedArrayTypePropertiesClass)), writer.ToString ().NormalizeLineEndings ()); + } } [TestFixture] diff --git a/tests/generator-Tests/Unit-Tests/KotlinFixupsTests.cs b/tests/generator-Tests/Unit-Tests/KotlinFixupsTests.cs index 1e4c90081..1c4ac1a89 100644 --- a/tests/generator-Tests/Unit-Tests/KotlinFixupsTests.cs +++ b/tests/generator-Tests/Unit-Tests/KotlinFixupsTests.cs @@ -26,7 +26,7 @@ public void CreateMethod_EnsureKotlinImplFix () [Test] public void CreateMethod_EnsureKotlinHashcodeFix () { - var xml = XDocument.Parse (""); + var xml = XDocument.Parse (""); var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class")); KotlinFixups.Fixup (new [] { (GenBase) klass }.ToList ()); From 59f610cc4bc04790ecbc7aac55636bd698366c9e Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Mon, 6 Jan 2020 16:50:42 -0600 Subject: [PATCH 5/5] Add test showing Kotlin's "Array" is encoded as "UIntArray[]" so we do not do any Kotlin fixups on it. --- .../KotlinFixupsTests.cs | 7 +++++++ .../kotlin/UnsignedTypes.class | Bin 2731 -> 2972 bytes .../kotlin/UnsignedTypes.kt | 1 + 3 files changed, 8 insertions(+) diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs index 4cef70ac6..dcfb774da 100644 --- a/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs +++ b/tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs @@ -133,6 +133,7 @@ public void UnsignedMethods () var ushortarray_method = klass.Methods.Single (m => m.Name.Contains ("foo_ushortarray-")); var ulongarray_method = klass.Methods.Single (m => m.Name.Contains ("foo_ulongarray-")); var ubytearray_method = klass.Methods.Single (m => m.Name.Contains ("foo_ubytearray-")); + var uintarrayarray_method = klass.Methods.Single (m => m.Name.Contains ("foo_uintarrayarray")); KotlinFixups.Fixup (new [] { klass }); @@ -159,6 +160,12 @@ public void UnsignedMethods () Assert.AreEqual ("ubyte[]", ubytearray_method.GetParameters () [0].KotlinType); Assert.AreEqual ("ubyte[]", ubytearray_method.KotlinReturnType); + + // Kotlin's Array does not trigger this code because it is not + // encoded as Java's "[[I", instead it is exposed as "UIntArray[]", so + // we treat it as a normal class array. + Assert.Null (uintarrayarray_method.GetParameters () [0].KotlinType); + Assert.Null (uintarrayarray_method.KotlinReturnType); } [Test] diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.class b/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.class index 84f1c4edcb83b61b57fe8a3a4bda89166f9b1a40..b55f4c2bd046c1d61f034fe468c6a3e38bb4d240 100644 GIT binary patch literal 2972 zcma)7TT|Oc6#iDeAQZ4=b2BCh36Nkz9Rs;FB~6RdTnIvya ze@dU*nf9U6Odm2#`qtO}pH9!pl7(puX3*}Q^L_iB)!C!tzyJO76MzM5F!XPl+^CxR z?#73=I`08vQ1`S$EnCyfYIb#JPp{ZL2rx_(_v?1eFtd-|ztt_{KsRlz*0y<(L4IAY zziJq!%@9oG(|MZgHwI^j7Ee<{hgz+nGcY2l-nPt(?OSWNS7?LcxcHm8a<>|+bfM)ZBd58aM?}W-r+%(^mNPAYT3MLTeQolP}VOoj8xv}mHp?Mr5)(R$a7bL zVLDYjeItt}JS8DhT}*HFVH{T!T*g%fNt|!Q&sFmktm5sQc1TI0S16^+luk(u!>v3u zX>0Gc*AM3R9arc6p=XwpQEnzSdmm&>pCBZrntmnn;q7@9ZarOlx-q*7(Cem3dF zmqbPv(_ip041CcWzHgX@{eU5m5^p+(c?Cf%(9LFk3kk;#yLR}mkL>t}s6n|$l?m)6lH!7j4_;9F zNU5K|hzmxDEbORjM}4xY#t4iPIN*W^&_^e0SsqbR6BKZi zG?K1Hk~D=q=GtQ(H7H+HFA;c|z;PEG_rWXbRZ^NDaMA@Qi7f0X*Pimp*VO9-P7|1N z!ITfC)tS#fNv83x-q-d44(qu$sTeez*_m-p#0rgJ^3jKjRT_tzC}i}K zB^U}W5FSHc7aHn08vS&d^Ue|Ls1bF}p?0AWKSv{mc6#L=V%O3zR%0YP)Cgs=NySHG=|U77)6q@AnM~j-MFX^c{YCWBPM2lZOcj+BPW#P zR+Z4NE+*+T&xtLnX^iGIdU~k)J4OssxYlKeM8^=<&kiw-RF@$}JBCP~9byKvU4}?@ z4Dr?3Au_npWr(qkA+l$MVB*)D2;#a0QGEQNnLlvzH(H$R2oXoWuSsz4I)HVDRYZyA z-wC^g5N?+d$m5Rle-~eS*1bHw!6JEazYNClEgqCH%hAj69mhP!kh4%YzUN4A409}T z9VpMCazr=^ P9M3qWIMtz7rXk=zCn5vF delta 979 zcmaJ<$!^n76g{?+=Oj4BPLp^@Lz`x73LOHJp#zjTp-_+zn@H@tfyHH?{)4xy5G#g7 z#R9|vMX;j_SnxfF`y4kx)QV*LzH`qx?_R(AUODdynIC_?dpeu1v%WR+Y&bll4x1RJT518&>n<@5Ec> zR2DV_bSUTrwmy@+&%Y-a0jIGWtm#fL6o&FYXN@_bXJN*;?NCuY;mn2ka??gmy133d zRA?O4xexM-Ne44gm0!A1nXb%_l|nC|7}HOI6+5Yu{S?D|S@+;`7ro#`+8dvNfWz$1 zRB7P>cP3Az#PK1&x)K2!HPmB_ZfJBG4c1LDH_?hQdy#o@y19*|7_&bz56(_AEAp+3g`x90VdGYTEOzd*T7zvOdK diff --git a/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.kt b/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.kt index 7ecd5f952..269d9c43a 100644 --- a/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.kt +++ b/tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/UnsignedTypes.kt @@ -24,4 +24,5 @@ public class UnsignedTypes { public fun foo_ushortarray (value : UShortArray) : UShortArray { return value; } public fun foo_ulongarray (value : ULongArray) : ULongArray { return value; } public fun foo_ubytearray (value : UByteArray) : UByteArray { return value; } + public fun foo_uintarrayarray (value : Array) : Array { return value; } } \ No newline at end of file