Skip to content

Commit

Permalink
[Hello-Core] Add "low level" sample. (#1047)
Browse files Browse the repository at this point in the history
Rename `samples/Hello` to `samples/Hello-Java.Base`, as that sample
exercises the `src/Java.Base` binding.
  • Loading branch information
jonpryor authored Oct 7, 2022
1 parent e1ee4b1 commit 8e4c7d2
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 1 deletion.
9 changes: 8 additions & 1 deletion Java.Interop.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
21 changes: 21 additions & 0 deletions samples/Hello-Core/Hello-Core.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(DotNetTargetFramework)</TargetFramework>
<OutputType>Exe</OutputType>
<RootNamespace>Hello</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Mono.Options" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj" />
<ProjectReference Include="..\..\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
</ItemGroup>

</Project>
87 changes: 87 additions & 0 deletions samples/Hello-Core/Program.cs
Original file line number Diff line number Diff line change
@@ -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, "<init>", "()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}");
}
35 changes: 35 additions & 0 deletions samples/Hello-Core/README.md
Original file line number Diff line number Diff line change
@@ -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
```
File renamed without changes.
File renamed without changes.

0 comments on commit 8e4c7d2

Please sign in to comment.