Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Java.Interop] Optional "Standalone" Build Config #1049

Merged
merged 1 commit into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 47 additions & 4 deletions build-tools/jnienv-gen/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("//");
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ();
o.WriteLine ("#if !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#define FEATURE_JNIENVIRONMENT_SAFEHANDLES");
o.WriteLine ("#if !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
Expand Down Expand Up @@ -315,6 +315,9 @@ static void GenerateTypes (TextWriter o, HandleStyle style)
if (style == HandleStyle.JIIntPtrPinvokeWithErrors) {
GenerateNativeMethods (o, style);
}
if (style == HandleStyle.JIFunctionPtrWithErrors) {
GenerateJniNativeMethods (o, style);
}

var visibilities = new Dictionary<string, string> {
{ ArrayOperationsCategory, "public" },
Expand Down Expand Up @@ -367,6 +370,46 @@ static void GenerateNativeMethods (TextWriter o, HandleStyle style)
o.WriteLine ();
}

static void GenerateJniNativeMethods (TextWriter o, HandleStyle style)
{
o.WriteLine ("\tstatic partial class JniNativeMethods {");
o.WriteLine ();
foreach (var entry in JNIEnvEntries) {
if (entry.Parameters == null)
continue;
if (entry.IsPrivate || entry.CustomWrapper)
continue;

var returnType = entry.ReturnType.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: true, isPinvoke: true);

o.WriteLine ();
o.WriteLine ("\t\t[System.Runtime.CompilerServices.MethodImpl (System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]");
o.Write ("\t\tinternal static unsafe ");
o.Write (entry.ReturnType.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: true, isPinvoke: true));
o.Write ($" {entry.Name} (IntPtr env");
foreach (var p in entry.Parameters) {
o.Write (", ");
o.Write (p.Type.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: false, isPinvoke: true));
o.Write ($" {Escape (p.Name)}");
}
o.WriteLine (")");
o.WriteLine ("\t\t{");
o.Write ("\t\t\t");
if (returnType != "void") {
o.Write ("return ");
}
o.Write ($"(*((JNIEnv**)env))->{entry.Name} (env");
foreach (var p in entry.Parameters) {
o.Write (", ");
o.Write (Escape (p.Name));
}
o.WriteLine (");");
o.WriteLine ("\t\t}");
}
o.WriteLine ("\t}");
o.WriteLine ();
}

static string GetPinvokeName (string name)
{
var sb = new StringBuilder ("java_interop_jnienv_".Length + name.Length * 2);
Expand Down Expand Up @@ -427,7 +470,7 @@ static void GenerateJniEnv (TextWriter o, string type, string visibility, Handle
GetPinvokeName (entry.Name),
entry.Throws ? ", out thrown" : "");
} else if (style == HandleStyle.JIFunctionPtrWithErrors) {
o.Write ($"(*((JNIEnv**)__env))->{entry.Name} (__env");
o.Write ($"JniNativeMethods.{entry.Name} (__env");
} else {
o.Write ("__info.Invoker.{0} (__info.EnvironmentPointer", entry.Name);
}
Expand All @@ -444,7 +487,7 @@ static void GenerateJniEnv (TextWriter o, string type, string visibility, Handle
}
o.WriteLine (");");
if (style == HandleStyle.JIFunctionPtrWithErrors && entry.Throws) {
o.WriteLine ("\t\t\tIntPtr thrown = (*((JNIEnv**)__env))->ExceptionOccurred (__env);");
o.WriteLine ("\t\t\tIntPtr thrown = JniNativeMethods.ExceptionOccurred (__env);");
}
CleanupParameters (o, entry.Parameters, style);
RaiseException (o, entry, style);
Expand Down
4 changes: 3 additions & 1 deletion samples/Hello-Core/Hello-Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
</ItemGroup>

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

Expand Down
4 changes: 2 additions & 2 deletions samples/Hello-Java.Base/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ static unsafe void CreateAnotherJVM ()
t.Start ();
waitForCreation.Wait ();
*/
foreach (var h in JniRuntime.GetAvailableInvocationPointers ()) {
foreach (var h in vm.GetAvailableInvocationPointers ()) {
Console.WriteLine ("WITHIN: GetCreatedJavaVMs: {0}", h);
}
// exitThread.Signal ();
}
foreach (var h in JniRuntime.GetAvailableInvocationPointers ()) {
foreach (var h in JniRuntime.GetRegisteredRuntimes ()) {
Console.WriteLine ("POST: GetCreatedJavaVMs: {0}", h);
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
<NoWarn>$(NoWarn);1591</NoWarn>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
<DefineConstants>INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES;$(JavaInteropDefineConstants)</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
Expand All @@ -31,6 +30,7 @@
</PropertyGroup>
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand All @@ -39,6 +39,13 @@
<LangVersion Condition=" '$(LangVersion)' == '' ">8.0</LangVersion>
<Version>$(JICoreLibVersion)</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Standalone)' == 'True' ">
<DefineConstants Condition=" '$(JIBuildingForNetCoreApp)' == 'True' ">FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS;$(DefineConstants)</DefineConstants>
<DefineConstants Condition=" '$(JIBuildingForNetCoreApp)' != 'True' ">FEATURE_JNIENVIRONMENT_JI_PINVOKES;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Standalone)' != 'True' ">
<DefineConstants>FEATURE_JNIENVIRONMENT_JI_PINVOKES;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Condition=" '$(TargetFramework)' == 'netstandard2.0' " Include="..\utils\NullableAttributes.cs" />
<Compile Remove="Java.Interop\JniLocationException.cs" />
Expand Down
77 changes: 61 additions & 16 deletions src/Java.Interop/Java.Interop/JniEnvironment.Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace Java.Interop
{
Expand Down Expand Up @@ -44,22 +45,13 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
throw new ArgumentException ("'classname' cannot be a zero-length string.", nameof (classname));

var info = JniEnvironment.CurrentInfo;
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
IntPtr thrown;
var c = NativeMethods.java_interop_jnienv_find_class (info.EnvironmentPointer, out thrown, classname);
if (thrown == IntPtr.Zero) {
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
if (TryRawFindClass (info.EnvironmentPointer, classname, out var c, out var thrown)) {
var r = new JniObjectReference (c, JniObjectReferenceType.Local);
JniEnvironment.LogCreateLocalRef (r);
return r;
}

// If the Java-side exception stack trace is *lost* a'la 89a5a229,
// change `false` to `true` and rebuild+re-run.
#if false
NativeMethods.java_interop_jnienv_exception_describe (info.EnvironmentPointer);
#endif

NativeMethods.java_interop_jnienv_exception_clear (info.EnvironmentPointer);
RawExceptionClear (info.EnvironmentPointer);

var findClassThrown = new JniObjectReference (thrown, JniObjectReferenceType.Local);
LogCreateLocalRef (findClassThrown);
Expand All @@ -70,18 +62,18 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
var __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue (java);

c = NativeMethods.java_interop_jnienv_call_object_method_a (info.EnvironmentPointer, out thrown, info.Runtime.ClassLoader.Handle, info.Runtime.ClassLoader_LoadClass.ID, (IntPtr) __args);
c = RawCallObjectMethodA (info.EnvironmentPointer, out thrown, info.Runtime.ClassLoader.Handle, info.Runtime.ClassLoader_LoadClass.ID, (IntPtr) __args);
JniObjectReference.Dispose (ref java);
if (thrown == IntPtr.Zero) {
(pendingException as IJavaPeerable)?.Dispose ();
var r = new JniObjectReference (c, JniObjectReferenceType.Local);
JniEnvironment.LogCreateLocalRef (r);
return r;
}
NativeMethods.java_interop_jnienv_exception_clear (info.EnvironmentPointer);
RawExceptionClear (info.EnvironmentPointer);

if (pendingException != null) {
NativeMethods.java_interop_jnienv_delete_local_ref (info.EnvironmentPointer, thrown);
JniEnvironment.References.RawDeleteLocalRef (info.EnvironmentPointer, thrown);
}
else {
var loadClassThrown = new JniObjectReference (thrown, JniObjectReferenceType.Local);
Expand All @@ -95,7 +87,7 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
return default;
}
throw pendingException!;
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
#endif // !(FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
var c = info.Invoker.FindClass (info.EnvironmentPointer, classname);
var thrown = info.Invoker.ExceptionOccurred (info.EnvironmentPointer);
Expand Down Expand Up @@ -137,6 +129,59 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
#endif // !FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
}

static bool TryRawFindClass (IntPtr env, string classname, out IntPtr klass, out IntPtr thrown)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
klass = NativeMethods.java_interop_jnienv_find_class (env, out thrown, classname);
if (thrown == IntPtr.Zero) {
return true;
}
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
var _classname_ptr = Marshal.StringToCoTaskMemUTF8 (classname);
klass = JniNativeMethods.FindClass (env, _classname_ptr);
thrown = JniNativeMethods.ExceptionOccurred (env);
Marshal.ZeroFreeCoTaskMemUTF8 (_classname_ptr);
if (thrown == IntPtr.Zero) {
return true;
}
#endif // !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
return false;
}

static void RawExceptionClear (IntPtr env)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
// If the Java-side exception stack trace is *lost* a'la 89a5a229,
// change `false` to `true` and rebuild+re-run.
#if false
NativeMethods.java_interop_jnienv_exception_describe (env);
#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES

NativeMethods.java_interop_jnienv_exception_clear (env);
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
// If the Java-side exception stack trace is *lost* a'la 89a5a229,
// change `false` to `true` and rebuild+re-run.
#if false
JniNativeMethods.ExceptionDescribe (env);
#endif
JniNativeMethods.ExceptionClear (env);
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
}

static IntPtr RawCallObjectMethodA (IntPtr env, out IntPtr thrown, IntPtr instance, IntPtr jmethodID, IntPtr args)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
return NativeMethods.java_interop_jnienv_call_object_method_a (env, out thrown, instance, jmethodID, args);
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
var r = JniNativeMethods.CallObjectMethodA (env, instance, jmethodID, args);
thrown = JniNativeMethods.ExceptionOccurred (env);
return r;
#else // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
return IntPtr.Zero;
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
}

#if NET
public static bool TryFindClass (string classname, out JniObjectReference instance)
{
Expand Down
36 changes: 25 additions & 11 deletions src/Java.Interop/Java.Interop/JniEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,34 @@ internal static void LogCreateLocalRef (IntPtr value)
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
partial class References {
internal static int GetJavaVM (IntPtr jnienv, out IntPtr vm)

internal static unsafe int GetJavaVM (IntPtr jnienv, out IntPtr vm)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
return NativeMethods.java_interop_jnienv_get_java_vm (jnienv, out vm);
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
IntPtr _vm;
int r = JniNativeMethods.GetJavaVM (jnienv, &_vm);
vm = _vm;
return r;
#else
Invoker = CreateInvoker (environmentPointer);
var r = Invoker.GetJavaVM (EnvironmentPointer, out vm);
return r;
#endif
}

internal static void RawDeleteLocalRef (IntPtr env, IntPtr localRef)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
NativeMethods.java_interop_jnienv_delete_local_ref (env, localRef);
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
JniNativeMethods.DeleteLocalRef (env, localRef);
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
}
}
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES

}

sealed class JniEnvironmentInfo : IDisposable {
Expand Down Expand Up @@ -206,13 +226,7 @@ public IntPtr EnvironmentPointer {

environmentPointer = value;
IntPtr vmh = IntPtr.Zero;
int r = 0;
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
r = JniEnvironment.References.GetJavaVM (EnvironmentPointer, out vmh);
#else
Invoker = CreateInvoker (environmentPointer);
r = Invoker.GetJavaVM (EnvironmentPointer, out vmh);
#endif // #if !FEATURE_JNIENVIRONMENT_JI_PINVOKES
int r = JniEnvironment.References.GetJavaVM (EnvironmentPointer, out vmh);
if (r < 0)
throw new InvalidOperationException ("JNIEnv::GetJavaVM() returned: " + r.ToString ());

Expand Down Expand Up @@ -285,7 +299,7 @@ public void Dispose ()
};
#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES

#if !FEATURE_JNIENVIRONMENT_JI_PINVOKES
#if !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES
internal JniEnvironmentInvoker Invoker {get; private set;}

static unsafe JniEnvironmentInvoker CreateInvoker (IntPtr handle)
Expand Down
40 changes: 9 additions & 31 deletions src/Java.Interop/Java.Interop/JniRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -78,13 +79,6 @@ interface ISetRuntime {
}
}

partial class NativeMethods {
const string JavaInteropLibrary = "java-interop";

[DllImport (JavaInteropLibrary, CallingConvention=CallingConvention.Cdecl)]
internal static extern int java_interop_jvm_list ([Out] IntPtr[]? handles, int bufLen, out int nVMs);
}

public partial class JniRuntime : IDisposable
{
const int JNI_OK = 0;
Expand All @@ -107,22 +101,14 @@ public static IEnumerable<JniRuntime> GetRegisteredRuntimes ()
}
}

internal static int GetCreatedJavaVMs (IntPtr[]? handles, int bufLen, out int nVMs)
{
return NativeMethods.java_interop_jvm_list (handles, bufLen, out nVMs);
}

[Obsolete ("Not sensible/usable at this level, and cannot work on e.g. Android. " +
"Try Java.Interop.JreRuntime.GetAvailableInvocationPointers() in Java.Runtime.Environment.dll, " +
"or rethink your structure.", error: true)]
[SuppressMessage ("Design", "CA1024:Use properties where appropriate",
Justification = "ABI compatibility")]
public static IEnumerable<IntPtr> GetAvailableInvocationPointers ()
{
int nVMs;
int r = GetCreatedJavaVMs (null, 0, out nVMs);
if (r != 0)
throw new NotSupportedException ("JNI_GetCreatedJavaVMs() returned: " + r.ToString ());
var handles = new IntPtr [nVMs];
r = GetCreatedJavaVMs (handles, handles.Length, out nVMs);
if (r != 0)
throw new InvalidOperationException ("JNI_GetCreatedJavaVMs() [take 2!] returned: " + r.ToString ());
return handles;
throw new NotSupportedException ();
}

static JniRuntime? current;
Expand All @@ -143,17 +129,9 @@ public static JniRuntime CurrentRuntime {
return c!;
}
if (count > 1)
throw new NotSupportedException (string.Format ("Found {0} Java Runtimes. Don't know which to use. Use JniRuntime.SetCurrent().", count));
throw new NotSupportedException (string.Format ("Found {0} known Java Runtime instances. Don't know which to use. Use JniRuntime.SetCurrent().", count));
Debug.Assert (count == 0);
var available = GetAvailableInvocationPointers ().FirstOrDefault ();
if (available == IntPtr.Zero)
throw new NotSupportedException ("No available Java runtime to attach to. Please create one.");
var options = new CreationOptions () {
DestroyRuntimeOnDispose = false,
InvocationPointer = available,
};
// Sets `current`
return new JniRuntime (options);
throw new NotSupportedException ("No available Java runtime to attach to. Please create one.");
}
}

Expand Down
Loading