From 176240d2b1da51de03f01b61aa113e0e82ef17ed Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Wed, 14 Jan 2015 12:54:52 -0500 Subject: [PATCH] [jnimarshalmethod-gen] Add jnimarshalmethod-gen.exe. jnimarshalmethod-gen.exe is a utility to generate all of the JNI Marshal Methods needed to support Java invoking [Export]ed methods. It was alluded to in commit 06cfd838: > Instead, long-term it would be "interesting" if we could use > ExportedMemberBuilder to process assemblies, generate the JNI method > marshaling code for ~everything, store that into a NEW assembly (as > part of the build process), and AOT the marshaling assembly! jnimarshalmethod-gen.exe is the *beginning* of that process: it only handles [Export]ed methods right now, and not [Register]d methods, and there's no facililty within Java.Interop to actually use this generated assembly (all TODOs!), but that said... jnimarshalmethod-gen.exe ASSEMBLY.dll will generate the file ASSEMBLY-JniMarshalMethods.dll which contains all of the JNI marshal methods that could (should) be registered with JNIEnv::RegisterNatives() to support Java > managed invocations. TODO: * Generate marshal methods for constructors. Related: commit 8c83f64f, and finishing Java activation. * Generate marshal methods for overridden [Register]ed methods. Note: this needs to be aware of the inheritance hierarchy, and only emit marshal methods for methods overridden by managed code, NOT Java method overrides. * Fixup/improve JniType.RegisterNativeMethods() so that it will check for the *-JniMarshalMethods.dll assemblies and use any methods there. * Plausible variation: have the *-JniMarshalMethods.dll type contain a per-type "registration method" which calls JNIEnv::RegisterNatives()? The "glue logic" needs thinking. --- Java.Interop.sln | 7 ++ Makefile | 4 + tools/jnimarshalmethod-gen/App.cs | 89 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 27 ++++++ ...oid.Tools.JniMarshalMethodGenerator.csproj | 54 +++++++++++ 5 files changed, 181 insertions(+) create mode 100644 tools/jnimarshalmethod-gen/App.cs create mode 100644 tools/jnimarshalmethod-gen/Properties/AssemblyInfo.cs create mode 100644 tools/jnimarshalmethod-gen/Xamarin.Android.Tools.JniMarshalMethodGenerator.csproj diff --git a/Java.Interop.sln b/Java.Interop.sln index 9a55bac4b..c714d6692 100644 --- a/Java.Interop.sln +++ b/Java.Interop.sln @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Linq.Expressions", "li EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop-PerformanceTests", "tests\Java.Interop-PerformanceTests\Java.Interop-PerformanceTests.csproj", "{6970466B-F6D1-417A-8A27-4FED8555EBD0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Tools.JniMarshalMethodGenerator", "tools\jnimarshalmethod-gen\Xamarin.Android.Tools.JniMarshalMethodGenerator.csproj", "{D1295A8F-4F42-461D-A046-564476C10002}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{D5A93398-AEB1-49F3-89DC-3904A47DB0C7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hello", "samples\Hello\Hello.csproj", "{F3ECB73D-9263-4E42-A5B4-3FC0D1D829F9}" @@ -75,6 +77,10 @@ Global {B501D075-6183-4E1D-92C9-F7B5002475B1}.Debug|Any CPU.Build.0 = Debug|Any CPU {B501D075-6183-4E1D-92C9-F7B5002475B1}.Release|Any CPU.ActiveCfg = Release|Any CPU {B501D075-6183-4E1D-92C9-F7B5002475B1}.Release|Any CPU.Build.0 = Release|Any CPU + {D1295A8F-4F42-461D-A046-564476C10002}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1295A8F-4F42-461D-A046-564476C10002}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1295A8F-4F42-461D-A046-564476C10002}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1295A8F-4F42-461D-A046-564476C10002}.Release|Any CPU.Build.0 = Release|Any CPU {F3ECB73D-9263-4E42-A5B4-3FC0D1D829F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F3ECB73D-9263-4E42-A5B4-3FC0D1D829F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {F3ECB73D-9263-4E42-A5B4-3FC0D1D829F9}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -99,6 +105,7 @@ Global {B501D075-6183-4E1D-92C9-F7B5002475B1} = {4C173212-371D-45D8-BA83-9226194F48DC} {5887B410-D448-4257-A46B-EAC03C80BE93} = {4C173212-371D-45D8-BA83-9226194F48DC} {6410DA0F-5E14-4FC0-9AEE-F4C542C96C7A} = {C8F58966-94BF-407F-914A-8654F8B8AE3B} + {D1295A8F-4F42-461D-A046-564476C10002} = {C8F58966-94BF-407F-914A-8654F8B8AE3B} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = tools\jnienv-gen\jnienv-gen.csproj diff --git a/Makefile b/Makefile index a6d6d700b..24078de0a 100644 --- a/Makefile +++ b/Makefile @@ -61,3 +61,7 @@ run-tests: $(TESTS) run-ptests: $(PTESTS) $(foreach t,$(PTESTS), $(call RUN_TEST,$(t))) + +run-test-jnimarshal: bin/$(CONFIGURATION)/Java.Interop.Export-Tests.dll + MONO_TRACE_LISTENER=Console.Out \ + mono --debug bin/$(CONFIGURATION)/jnimarshalmethod-gen.exe bin/$(CONFIGURATION)/Java.Interop.Export-Tests.dll diff --git a/tools/jnimarshalmethod-gen/App.cs b/tools/jnimarshalmethod-gen/App.cs new file mode 100644 index 000000000..283d8eba2 --- /dev/null +++ b/tools/jnimarshalmethod-gen/App.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; + +using Java.Interop; + +namespace Xamarin.Android.Tools.JniMarshalMethodGenerator { + + class App { + + internal const string Name = "jnimarshalmethod-gen"; + + public static void Main (string[] args) + { + var jvm = CreateJavaVM (); + + foreach (var path in args) { + if (!File.Exists (path)) { + Console.Error.WriteLine ("{0}: Path '{1}' does not exist.", Name, path); + continue; + } + try { + CreateMarshalMethodAssembly (path); + } + catch (Exception e) { + Console.Error.WriteLine ("{0}: {1}", Name, e.Message); + Console.WriteLine (e); + Environment.ExitCode = 1; + } + } + + jvm.Dispose (); + } + + static JavaVM CreateJavaVM () + { + var builder = new JreVMBuilder (); + return builder.CreateJreVM (); + } + + static ExportedMemberBuilder CreateExportedMemberBuilder () + { + return new ExportedMemberBuilder (JniEnvironment.Current.JavaVM); + } + + static void CreateMarshalMethodAssembly (string path) + { + var assembly = Assembly.LoadFile (path); + + var baseName = Path.GetFileNameWithoutExtension (path); + var assemblyName = new AssemblyName (baseName + "-JniMarshalMethods"); + var destPath = assemblyName.Name + ".dll"; + var builder = CreateExportedMemberBuilder (); + + var da = AppDomain.CurrentDomain.DefineDynamicAssembly ( + assemblyName, + AssemblyBuilderAccess.Save, + Path.GetDirectoryName (path)); + + var dm = da.DefineDynamicModule ("", destPath); + + foreach (var type in assembly.DefinedTypes) { + TypeBuilder dt = null; + + var flags = BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance | BindingFlags.Static; + foreach (var method in type.GetMethods (flags )) { + // TODO: Constructors, [Register] methods + var export = method.GetCustomAttribute (); + if (export == null) + continue; + if (dt == null) + dt = dm.DefineType (type.FullName, TypeAttributes.Public | TypeAttributes.Sealed); + + var mb = dt.DefineMethod ( + method.Name, + MethodAttributes.Public | MethodAttributes.Static); + var lambda = builder.CreateMarshalFromJniMethodExpression (export, type, method); + lambda.CompileToMethod (mb); + } + if (dt != null) + dt.CreateType (); + } + da.Save (destPath); + } + } +} diff --git a/tools/jnimarshalmethod-gen/Properties/AssemblyInfo.cs b/tools/jnimarshalmethod-gen/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..7f5576a1d --- /dev/null +++ b/tools/jnimarshalmethod-gen/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("jnimarshalmethod-gen")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("Xamarin Inc.")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("Xamarin Inc.")] +[assembly: AssemblyTrademark ("Xamarin")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/tools/jnimarshalmethod-gen/Xamarin.Android.Tools.JniMarshalMethodGenerator.csproj b/tools/jnimarshalmethod-gen/Xamarin.Android.Tools.JniMarshalMethodGenerator.csproj new file mode 100644 index 000000000..247d21c17 --- /dev/null +++ b/tools/jnimarshalmethod-gen/Xamarin.Android.Tools.JniMarshalMethodGenerator.csproj @@ -0,0 +1,54 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {D1295A8F-4F42-461D-A046-564476C10002} + Exe + Xamarin.Android.Tools.JniMarshalMethodGenerator + jnimarshalmethod-gen + v4.5 + + + true + full + false + ..\..\bin\Debug + DEBUG; + prompt + 4 + true + + + full + true + ..\..\bin\Release + prompt + 4 + true + + + + + + + + + + + + {B501D075-6183-4E1D-92C9-F7B5002475B1} + Java.Interop.Export + + + {94BD81F7-B06F-4295-9636-F8A3B6BDC762} + Java.Interop + + + {5887B410-D448-4257-A46B-EAC03C80BE93} + Java.Runtime.Environment + + + \ No newline at end of file