diff --git a/src/Java.Interop.Export/Java.Interop/MarshalMemberBuilder.cs b/src/Java.Interop.Export/Java.Interop/MarshalMemberBuilder.cs index 69919d646..8bf646b05 100644 --- a/src/Java.Interop.Export/Java.Interop/MarshalMemberBuilder.cs +++ b/src/Java.Interop.Export/Java.Interop/MarshalMemberBuilder.cs @@ -254,23 +254,22 @@ static Type GetMarshalerType (Type returnType, List funcTypeParams, Type d funcTypeParams.RemoveRange (0, 2); var marshalDelegateName = new StringBuilder (); marshalDelegateName.Append ("_JniMarshal_PP"); - foreach (var paramType in funcTypeParams) { - marshalDelegateName.Append (GetJniMarshalDelegateParameterIdentifier (paramType)); - } - marshalDelegateName.Append ("_"); - if (returnType == null) { - marshalDelegateName.Append ("V"); - } else { - marshalDelegateName.Append (GetJniMarshalDelegateParameterIdentifier (returnType)); - } + AddMarshalerTypeNameSuffix (marshalDelegateName, returnType, funcTypeParams); Type marshalDelegateType = declaringType.Assembly.GetType (marshalDelegateName.ToString (), throwOnError: false); - // Punt?; System.Linq.Expressions will automagically produce the needed delegate type. - // Unfortunately, this won't work with jnimarshalmethod-gen.exe. return marshalDelegateType; } + public static void AddMarshalerTypeNameSuffix (StringBuilder sb, Type returnType, List funcTypeParams) + { + foreach (var paramType in funcTypeParams) + sb.Append (GetJniMarshalDelegateParameterIdentifier (paramType)); + + sb.Append ("_"); + sb.Append (returnType == null ? 'V' : GetJniMarshalDelegateParameterIdentifier (returnType)); + } + static char GetJniMarshalDelegateParameterIdentifier (Type type) { if (type == typeof (bool)) return 'Z'; diff --git a/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs b/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs index 877448bde..e4c2505b3 100644 --- a/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs +++ b/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs @@ -159,6 +159,66 @@ public static bool StaticFuncThisMethodTakesLotsOfParameters ( return false; return true; } + + [JavaCallable ("staticManyParametersWithAutogeneratedDelegateType", Signature="(ZBCSIJFDLjava/lang/Object;Ljava/lang/String;Ljava/util/ArrayList;Ljava/lang/String;Ljava/lang/Object;DFJII)I")] + public static int staticManyParametersWithAutogeneratedDelegateType ( + bool a, + sbyte b, + char c, + short d, + int e, + long f, + float g, + double h, + IntPtr i, // java.lang.Object + IntPtr j, // java.lang.String + IntPtr k, // java.util.ArrayList + IntPtr l, // java.lang.String + IntPtr m, // java.lang.Object + double n, + float o, + long p, + int q, + int rv) + { + if (a != false) + return -1; + if (b != (byte) 0xb) + return -1; + if (c != 'c') + return -1; + if (d != (short) 0xd) + return -1; + if (e != 0xe) + return -1; + if (f != 0xf) + return -1; + if (g != 1.0f) + return -1; + if (h != 2.0) + return -1; + if (i == IntPtr.Zero) + return -1; + if (j == IntPtr.Zero) + return -1; + if (k == IntPtr.Zero) + return -1; + if (l == IntPtr.Zero) + return -1; + if (m == IntPtr.Zero) + return -1; + if (n != 3.0) + return -1; + if (o != 4.0f) + return -1; + if (p != 0x70) + return -1; + if (q != 111111) + return -1; + if (rv != 222222) + return -1; + return rv; + } } [JniValueMarshaler (typeof (MyColorValueMarshaler))] diff --git a/tests/Java.Interop.Export-Tests/Java.Interop/MarshalMemberBuilderTest.cs b/tests/Java.Interop.Export-Tests/Java.Interop/MarshalMemberBuilderTest.cs index 4aa607aa4..d40735b4f 100644 --- a/tests/Java.Interop.Export-Tests/Java.Interop/MarshalMemberBuilderTest.cs +++ b/tests/Java.Interop.Export-Tests/Java.Interop/MarshalMemberBuilderTest.cs @@ -22,7 +22,7 @@ public void AddExportMethods () var methods = CreateBuilder () .GetExportedMemberRegistrations (typeof (ExportTest)) .ToList (); - Assert.AreEqual (11, methods.Count); + Assert.AreEqual (12, methods.Count); Assert.AreEqual ("action", methods [0].Name); Assert.AreEqual ("()V", methods [0].Signature); diff --git a/tests/Java.Interop.Export-Tests/java/com/xamarin/interop/export/ExportType.java b/tests/Java.Interop.Export-Tests/java/com/xamarin/interop/export/ExportType.java index be8da5aa6..785e820e5 100644 --- a/tests/Java.Interop.Export-Tests/java/com/xamarin/interop/export/ExportType.java +++ b/tests/Java.Interop.Export-Tests/java/com/xamarin/interop/export/ExportType.java @@ -30,6 +30,26 @@ public static void testStaticMethods () { public static native void staticActionInt32String (int i, String s); public static native int staticFuncMyLegacyColorMyColor_MyColor (int color1, int color2); + public static native int staticManyParametersWithAutogeneratedDelegateType ( + boolean a, + byte b, + char c, + short d, + int e, + long f, + float g, + double h, + Object i, + String j, + ArrayList k, + String l, + Object m, + double n, + float o, + long p, + int q, + int rv); + public static native boolean staticFuncThisMethodTakesLotsOfParameters ( boolean a, byte b, @@ -64,6 +84,29 @@ public void testMethods () { staticActionInt (1); staticActionFloat (2.0f); + int ivalue = staticManyParametersWithAutogeneratedDelegateType ( + false, + (byte) 0xb, + 'c', + (short) 0xd, + 0xe, + 0xf, + 1.0f, + 2.0, + new Object (), + "j", + new ArrayList(), + "l", + new Object (), + 3.0, + 4.0f, + 0x70, + 111111, + 222222 + ); + if (ivalue != 222222) + throw new Error ("staticManyParametersWithAutogeneratedDelegateType should return last parameter value, which is 222222"); + boolean r = staticFuncThisMethodTakesLotsOfParameters ( false, (byte) 0xb, diff --git a/tools/jnimarshalmethod-gen/App.cs b/tools/jnimarshalmethod-gen/App.cs index e3ad1710f..c6ff436c1 100644 --- a/tools/jnimarshalmethod-gen/App.cs +++ b/tools/jnimarshalmethod-gen/App.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; @@ -12,6 +13,7 @@ using Mono.Options; using Mono.Collections.Generic; using Java.Interop.Tools.Cecil; +using System.Text; #if _DUMP_REGISTER_NATIVE_MEMBERS using Mono.Linq.Expressions; @@ -290,6 +292,7 @@ void CreateMarshalMethodAssembly (string path) var destDir = string.IsNullOrEmpty (outDirectory) ? Path.GetDirectoryName (path) : outDirectory; var builder = CreateExportedMemberBuilder (); var matchType = typeNameRegexes.Count > 0; + var newDelegates = new List (); if (Verbose) ColorWriteLine ($"Preparing marshal method assembly '{assemblyName}'", ConsoleColor.Cyan); @@ -419,7 +422,7 @@ void CreateMarshalMethodAssembly (string path) if (signature == null) signature = builder.GetJniMethodSignature (method); - registrationElements.Add (CreateRegistration (name, signature, lambda, targetType, methodName)); + registrationElements.Add (CreateRegistration (name, signature, lambda, targetType, methodName, dm, newDelegates)); addedMethods.Add (methodName); } @@ -441,7 +444,7 @@ void CreateMarshalMethodAssembly (string path) if (!string.IsNullOrEmpty (outDirectory)) path = Path.Combine (outDirectory, Path.GetFileName (path)); - var mover = new TypeMover (dstAssembly, ad, path, definedTypes, resolver, cache); + var mover = new TypeMover (dstAssembly, ad, path, newDelegates, definedTypes, resolver, cache); mover.Move (); if (!keepTemporary) @@ -465,7 +468,18 @@ void CreateMarshalMethodAssembly (string path) typeof (string), }); - static Expression CreateRegistration (string method, string signature, LambdaExpression lambda, ParameterExpression targetType, string methodName) + static void CreateDelegateRuntimeManagedMethod (TypeBuilder tb, string name, Type returnType, Type[] parameterTypes) + { + var mb = tb.DefineMethod (name, + System.Reflection.MethodAttributes.Public | + System.Reflection.MethodAttributes.HideBySig | + System.Reflection.MethodAttributes.NewSlot | + System.Reflection.MethodAttributes.Virtual, + CallingConventions.Standard, returnType, parameterTypes); + mb.SetImplementationFlags (System.Reflection.MethodImplAttributes.Runtime | System.Reflection.MethodImplAttributes.Managed); + } + + static Expression CreateRegistration (string method, string signature, LambdaExpression lambda, ParameterExpression targetType, string methodName, ModuleBuilder dm, List createdDelegateList) { Expression registrationDelegateType = null; if (lambda.Type.Assembly == typeof (object).Assembly || @@ -473,9 +487,51 @@ static Expression CreateRegistration (string method, string signature, LambdaExp registrationDelegateType = Expression.Constant (lambda.Type, typeof (Type)); } else { + string delegateTypeName; + if (lambda.Type.Assembly.IsDynamic) { + var typeNameBuilder = new StringBuilder ("__<$>_jni_marshal_"); + var parameterTypes = new List (); + foreach (var p in lambda.Parameters) { + parameterTypes.Add (p.Type); + } + + MarshalMemberBuilder.AddMarshalerTypeNameSuffix (typeNameBuilder, lambda.ReturnType, parameterTypes); + + var typeName = typeNameBuilder.ToString (); + var existingType = dm.GetType (typeName); + if (existingType == null) { + var dtb = dm.DefineType (typeName, + System.Reflection.TypeAttributes.AnsiClass | + System.Reflection.TypeAttributes.Sealed, + typeof (MulticastDelegate)); + + var dc = dtb.DefineConstructor ( + System.Reflection.MethodAttributes.Public | + System.Reflection.MethodAttributes.HideBySig | + System.Reflection.MethodAttributes.RTSpecialName | + System.Reflection.MethodAttributes.SpecialName, + CallingConventions.Standard, new Type [] { typeof (object), typeof (IntPtr) }); + dc.SetImplementationFlags (System.Reflection.MethodImplAttributes.Runtime | System.Reflection.MethodImplAttributes.Managed); + + CreateDelegateRuntimeManagedMethod (dtb, "Invoke", typeof (bool), parameterTypes.ToArray ()); + + parameterTypes.Add (typeof (AsyncCallback)); + parameterTypes.Add (typeof (object)); + + CreateDelegateRuntimeManagedMethod (dtb, "BeginInvoke", typeof (IAsyncResult), parameterTypes.ToArray ()); + CreateDelegateRuntimeManagedMethod (dtb, "EndInvoke", typeof (bool), new Type [] { typeof (IAsyncResult) }); + + existingType = dtb.CreateType (); + createdDelegateList.Add (existingType); + } + + delegateTypeName = existingType.FullName; + } else + delegateTypeName = lambda.Type.FullName; + Func getType = Type.GetType; registrationDelegateType = Expression.Call (getType.GetMethodInfo (), - Expression.Constant (lambda.Type.FullName, typeof (string)), + Expression.Constant (delegateTypeName, typeof (string)), Expression.Constant (true, typeof (bool))); registrationDelegateType = Expression.Convert (registrationDelegateType, typeof (Type)); } diff --git a/tools/jnimarshalmethod-gen/TypeMover.cs b/tools/jnimarshalmethod-gen/TypeMover.cs index 6c3336671..39c5f1e5d 100644 --- a/tools/jnimarshalmethod-gen/TypeMover.cs +++ b/tools/jnimarshalmethod-gen/TypeMover.cs @@ -14,17 +14,19 @@ public class TypeMover AssemblyDefinition Source { get; } AssemblyDefinition Destination { get; } string DestinationPath { get; } + List DelegateTypes { get; } Dictionary Types { get; } DirectoryAssemblyResolver Resolver { get; } MethodReference consoleWriteLine; TypeDefinitionCache cache; - public TypeMover (AssemblyDefinition source, AssemblyDefinition destination, string destinationPath, Dictionary types, DirectoryAssemblyResolver resolver, TypeDefinitionCache cache) + public TypeMover (AssemblyDefinition source, AssemblyDefinition destination, string destinationPath, List delegateTypes, Dictionary types, DirectoryAssemblyResolver resolver, TypeDefinitionCache cache) { Source = source; Destination = destination; DestinationPath = destinationPath; + DelegateTypes = delegateTypes; Types = types; Resolver = resolver; this.cache = cache; @@ -45,6 +47,11 @@ public void Move () typeMap.Clear (); resolvedTypeMap.Clear (); + foreach (var type in DelegateTypes) { + MoveDelegate (type); + movedTypesCount++; + } + foreach (var type in Types.Values) { Move (type); movedTypesCount++; @@ -71,6 +78,34 @@ bool TypeIsEmptyOrHasOnlyDefaultConstructor (TypeDefinition type) return !type.HasMethods || (type.Methods.Count == 1 && type.Methods [0].IsConstructor); } + void MoveDelegate (Type type) + { + var typeSrc = Source.MainModule.GetType (type.GetCecilName ()); + var typeDst = new TypeDefinition ("", typeSrc.Name, typeSrc.Attributes); + var module = Destination.MainModule; + + if (App.Verbose) { + Console.Write ($"Moving delegate type "); + App.ColorWrite ($"{typeSrc.FullName},{typeSrc.Module.FileName}", ConsoleColor.Yellow); + Console.Write (" to "); + App.ColorWriteLine ($"{Destination.MainModule.FileName}", ConsoleColor.Yellow); + } + + typeDst.BaseType = GetUpdatedType (typeSrc.BaseType, module); + + foreach (var m in typeSrc.Methods) { + var md = new MethodDefinition (m.Name, m.Attributes, GetUpdatedType (m.ReturnType, module)); + md.ImplAttributes = m.ImplAttributes; + + foreach (var p in m.Parameters) + md.Parameters.Add (new ParameterDefinition (p.Name, p.Attributes, GetUpdatedType (p.ParameterType, module))); + + typeDst.Methods.Add (md); + } + + Destination.MainModule.Types.Add (typeDst); + } + void Move (Type type) { var typeSrc = Source.MainModule.GetType (type.GetCecilName ()); @@ -343,6 +378,7 @@ MethodReference GetActionConstructor (TypeReference type, ModuleDefinition modul var mr = GetUpdatedMethod (m, module); if (type is GenericInstanceType) mr.DeclaringType = type; + return mr; } }