From 661fa7e30bcdea53f55cd0a7bc2887ac487b2702 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Fri, 7 Oct 2022 10:56:50 -0400 Subject: [PATCH] [Hello-Core] Add "low level" sample. Rename `samples/Hello` to `samples/Hello-Java.Base`, as that sample exercises the `src/Java.Base` binding. --- Java.Interop.sln | 9 +- samples/Hello-Core/Hello-Core.csproj | 21 +++++ samples/Hello-Core/Program.cs | 87 +++++++++++++++++++ samples/Hello-Core/README.md | 35 ++++++++ .../Hello-Java.Base.csproj} | 0 samples/{Hello => Hello-Java.Base}/Program.cs | 0 6 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 samples/Hello-Core/Hello-Core.csproj create mode 100644 samples/Hello-Core/Program.cs create mode 100644 samples/Hello-Core/README.md rename samples/{Hello/Hello.csproj => Hello-Java.Base/Hello-Java.Base.csproj} (100%) rename samples/{Hello => Hello-Java.Base}/Program.cs (100%) diff --git a/Java.Interop.sln b/Java.Interop.sln index b4366ee6d..2abb51d62 100644 --- a/Java.Interop.sln +++ b/Java.Interop.sln @@ -59,7 +59,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "generator-Tests", "tests\ge EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{D5A93398-AEB1-49F3-89DC-3904A47DB0C7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hello", "samples\Hello\Hello.csproj", "{F3ECB73D-9263-4E42-A5B4-3FC0D1D829F9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hello-Java.Base", "samples\Hello-Java.Base\Hello-Java.Base.csproj", "{F3ECB73D-9263-4E42-A5B4-3FC0D1D829F9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hello-Core", "samples\Hello-Core\Hello-Core.csproj", "{0E6DE9F9-35B1-4DFB-BB8B-7E4A2D362461}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build-Tools", "Build-Tools", "{172B608B-E6F3-41CC-9949-203A76BA247C}" EndProject @@ -214,6 +216,10 @@ Global {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 {F3ECB73D-9263-4E42-A5B4-3FC0D1D829F9}.Release|Any CPU.Build.0 = Release|Any CPU + {0E6DE9F9-35B1-4DFB-BB8B-7E4A2D362461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E6DE9F9-35B1-4DFB-BB8B-7E4A2D362461}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E6DE9F9-35B1-4DFB-BB8B-7E4A2D362461}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E6DE9F9-35B1-4DFB-BB8B-7E4A2D362461}.Release|Any CPU.Build.0 = Release|Any CPU {6410DA0F-5E14-4FC0-9AEE-F4C542C96C7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6410DA0F-5E14-4FC0-9AEE-F4C542C96C7A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6410DA0F-5E14-4FC0-9AEE-F4C542C96C7A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -331,6 +337,7 @@ Global {891F2E04-5614-4A26-A78F-3778025ECF43} = {271C9F30-F679-4793-942B-0D9527CB3E2F} {4EEAB1A7-99C1-4302-9C18-01A7B481409B} = {271C9F30-F679-4793-942B-0D9527CB3E2F} {F3ECB73D-9263-4E42-A5B4-3FC0D1D829F9} = {D5A93398-AEB1-49F3-89DC-3904A47DB0C7} + {0E6DE9F9-35B1-4DFB-BB8B-7E4A2D362461} = {D5A93398-AEB1-49F3-89DC-3904A47DB0C7} {6410DA0F-5E14-4FC0-9AEE-F4C542C96C7A} = {172B608B-E6F3-41CC-9949-203A76BA247C} {D18FCF91-8876-48A0-A693-2DC1E7D3D80A} = {0998E45F-8BCE-4791-A944-962CD54E2D80} {D48EE8D0-0A0A-4493-AEF5-DAF5F8CF86AD} = {0998E45F-8BCE-4791-A944-962CD54E2D80} diff --git a/samples/Hello-Core/Hello-Core.csproj b/samples/Hello-Core/Hello-Core.csproj new file mode 100644 index 000000000..8ea4c642d --- /dev/null +++ b/samples/Hello-Core/Hello-Core.csproj @@ -0,0 +1,21 @@ + + + + $(DotNetTargetFramework) + Exe + Hello + enable + enable + true + + + + + + + + + + + + diff --git a/samples/Hello-Core/Program.cs b/samples/Hello-Core/Program.cs new file mode 100644 index 000000000..157aa80b2 --- /dev/null +++ b/samples/Hello-Core/Program.cs @@ -0,0 +1,87 @@ +using Java.Interop; + +using Mono.Options; + +bool showHelp = false; + +var jreOptions = new JreRuntimeOptions { +}; + +var options = new OptionSet { + "Using the JVM from C#!", + "", + "Options:", + { "jvm=", + $"{{PATH}} to JVM to use.", + v => jreOptions.JvmLibraryPath = v }, + { "cp=|classpath", + $"Add {{JAR-OR-DIRECTORY}} to JVM classpath.", + v => jreOptions.ClassPath.Add (v)}, + { "J=", + $"Pass the specified option to the JVM.", + v => jreOptions.AddOption (v) }, + { "h|help", + "Show this message and exit.", + v => showHelp = v != null }, +}; +options.Parse (args); + +if (showHelp) { + options.WriteOptionDescriptions (Console.Out); + return; +} + +if (string.IsNullOrEmpty (jreOptions.JvmLibraryPath) || !File.Exists (jreOptions.JvmLibraryPath)) { + Error ("Option -jvm=PATH is required. PATH is a full path to the JVM native library to use, e.g. `libjli.dylib`."); + return; +} + +var jre = jreOptions.CreateJreVM (); + +// We now have a JVM! +// The current thread is implicitly attached to the JVM. +// Access of `JniEnvironment` members on other threads will implicitly attach those threads to the JVM. + +// +// Useful background info: the JNI documentation! https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html +// + +var Object_class = JniEnvironment.Types.FindClass ("java/lang/Object"); +Console.WriteLine ($"Object_class={Object_class}"); +var Object_ctor = JniEnvironment.InstanceMethods.GetMethodID (Object_class, "", "()V"); +var Object_val = JniEnvironment.Object.NewObject (Object_class, Object_ctor); + +Console.WriteLine ($"Object_val={Object_val}"); + +// Invoke `Object.toString()` +var Object_toString = JniEnvironment.InstanceMethods.GetMethodID (Object_class, "toString", "()Ljava/lang/String;"); +unsafe { + var Object_desc = JniEnvironment.InstanceMethods.CallObjectMethod (Object_val, Object_toString, null); + Console.WriteLine ($"Object_val.toString()={JniEnvironment.Strings.ToString (Object_desc)}"); + + // When JNI returns a `jobject` or `jclass` value, JNI returns a *JNI Object Reference*. + // The `JniObjectReference` struct is used to store JNI Local, Global, and Weak Global references. + // + // When an object reference is no longer required, it should be explicitly deleted. + + JniObjectReference.Dispose (ref Object_desc); +} + +JniObjectReference.Dispose (ref Object_class); +JniObjectReference.Dispose (ref Object_val); + +// There are some OO wrappers over the core `JniEnvironment` members. `JniType` is useful. +var Object_type = new JniType ("java/lang/Object"); +var Object_ctor2 = Object_type.GetConstructor ("()V"); + +unsafe { + var Object_val2 = Object_type.NewObject (Object_ctor2, null); + var Object_desc = JniEnvironment.InstanceMethods.CallObjectMethod (Object_val2, Object_toString, null); + Console.WriteLine ($"Object_val.toString()={JniEnvironment.Strings.ToString (Object_desc)}"); +} + +void Error (string message) +{ + var app = Path.GetFileNameWithoutExtension (Environment.GetCommandLineArgs ()[0]); + Console.Error.WriteLine ($"{app}: {message}"); +} diff --git a/samples/Hello-Core/README.md b/samples/Hello-Core/README.md new file mode 100644 index 000000000..30893d6b8 --- /dev/null +++ b/samples/Hello-Core/README.md @@ -0,0 +1,35 @@ +# Hello-Core + +Use as little of `Java.Interop.dll` as possible. No object mapping. + +Usage: + +``` +Options: + --jvm=PATH PATH to JVM to use. + --cp, --classpath=JAR-OR-DIRECTORY + Add JAR-OR-DIRECTORY to JVM classpath. + -J=VALUE Pass the specified option to the JVM. + -h, --help Show this message and exit. +``` + +`-J` can be used to add runtime options to the JVM instance, e.g. + +```shell +# Enable verbose JNI logging from the JVM +% dotnet run -- --jvm /Library/Java/JavaVirtualMachines/microsoft-11.jdk/Contents/Home/lib/jli/libjli.dylib -J-verbose:jni +[Dynamic-linking native method java.lang.Object.registerNatives ... JNI] +[Registering JNI native method java.lang.Object.hashCode] +[Registering JNI native method java.lang.Object.wait] +… +``` + +The sample will create a `java.lang.Object` instance and invoke `Object.toString()` on it. + +``` +% dotnet run -- --jvm /Library/Java/JavaVirtualMachines/microsoft-11.jdk/Contents/Home/lib/jli/libjli.dylib +Object_class=0x7ff04f105b98/L +Object_val=0x7ff04f105ba8/L +Object_val.toString()=java.lang.Object@5cbc508c +Object_val.toString()=java.lang.Object@3419866c +``` diff --git a/samples/Hello/Hello.csproj b/samples/Hello-Java.Base/Hello-Java.Base.csproj similarity index 100% rename from samples/Hello/Hello.csproj rename to samples/Hello-Java.Base/Hello-Java.Base.csproj diff --git a/samples/Hello/Program.cs b/samples/Hello-Java.Base/Program.cs similarity index 100% rename from samples/Hello/Program.cs rename to samples/Hello-Java.Base/Program.cs