This repository was archived by the owner on Aug 27, 2026. It is now read-only.
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
22 changes: 6 additions & 16 deletions Documentation/Architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,21 +237,12 @@ they don't require cleanup *anyway*. Furthermore, these values should be
*cached* -- see `JniPeerMembers` -- so making them GC objects shouldn't be
a long-term problem.

By doing so, we allow Java.Interop to have *two separate implementations*,
controlled by build-time `#define`s:

* `FEATURE_HANDLES_ARE_SAFE_HANDLES`: Causes `JniObjectReference` to
contain a `SafeHandle` wrapping the underlying JNI handle.
* `FEATURE_HANDLES_ARE_INTPTRS`: Causes `JniObjectReference` to contain
an `IntPtr` for the underlying JNI handle.

The rationale for this is twofold:

1. It allows swapping out "safer" `SafeHandle` and "less safe" `IntPtr`
implementations, permitting easier performance comparisons.
2. It allows migrating the existing code, as some of the existing
tests may assume that JNI handles are garbage collected, which
won't be the case when `FEATURE_HANDLES_ARE_INTPTRS` is set.
Historically, `JniObjectReference` retained an optional `SafeHandle`
implementation behind a build-time feature switch so the safer but slower
representation could be compared with the `IntPtr` representation during the
migration. That experiment is no longer active: the supported implementation is
the `IntPtr`-backed `JniObjectReference` struct, with explicit ownership
transfer and disposal via `JniObjectReference.Dispose (ref reference)`.


## Naming Conventions
Expand DownExpand Up@@ -289,4 +280,3 @@ I halted the above loop after reaching 25686556 instances.
I'm not sure when the JDK would stop handing out references, but it's probably
bound to process heap limits (e.g. depends on 32-bit vs. 64-bit process).


47 changes: 15 additions & 32 deletions build-tools/jnienv-gen/Generator.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,19 +88,19 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ("#nullable enable");
o.WriteLine ();
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 ("#if !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 ("#endif // !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("using System;");
o.WriteLine ("using System.Linq;");
Expand All@@ -123,7 +123,6 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("namespace Java.Interop {");
GenerateJniNativeInterface (o);
o.WriteLine ("}");
WriteSection (o, HandleStyle.SafeHandle, "FEATURE_JNIENVIRONMENT_SAFEHANDLES", "Java.Interop.SafeHandles");
WriteSection (o, HandleStyle.JIIntPtr, "FEATURE_JNIENVIRONMENT_JI_INTPTRS", "Java.Interop.JIIntPtrs");
WriteSection (o, HandleStyle.JIIntPtrPinvokeWithErrors, "FEATURE_JNIENVIRONMENT_JI_PINVOKES", "Java.Interop.JIPinvokes");
WriteSection (o, HandleStyle.XAIntPtr, "FEATURE_JNIENVIRONMENT_XA_INTPTRS", "Java.Interop.XAIntPtrs");
Expand All@@ -149,7 +148,6 @@ static void WriteSection (TextWriter o, HandleStyle style, string define, string
o.WriteLine ();
switch (style) {
case HandleStyle.JIIntPtr:
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
GenerateJniNativeInterfaceInvoker (o, style);
break;
Expand All@@ -172,7 +170,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("#pragma warning disable 0169 // Field never used; ignore since these fields make the structure have the right layout.");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("\t[StructLayout (LayoutKind.Sequential)]");
o.WriteLine ("\tpartial struct JniNativeInterfaceStruct {");
o.WriteLine ();
Expand All@@ -186,7 +184,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("\t\tpublic IntPtr {0};{1} // {2}", e.Name, new string (' ', maxName - e.Name.Length), e.Prototype);
}
o.WriteLine ("\t}");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
Expand DownExpand Up@@ -961,7 +959,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
return new [] {
string.Format ("JniEnvironment.LogCreateLocalRef ({0});", variable),
Expand DownExpand Up@@ -1070,7 +1067,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1084,7 +1080,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1099,7 +1094,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1122,7 +1116,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand DownExpand Up@@ -1186,20 +1179,17 @@ protected override bool IsStatic {

abstract class ObjectReferenceTypeInfo : TypeInfo {

string safeType, refType;
string refType;

public ObjectReferenceTypeInfo (string jni, string safeType, string refType)
public ObjectReferenceTypeInfo (string jni, string refType)
: base (jni)
{
this.safeType = safeType;
this.refType = refType;
}

public override string GetMarshalType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
return isReturn ? safeType : "JniReferenceSafeHandle";
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1212,7 +1202,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1226,8 +1215,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
return string.Format ("{0}.SafeHandle", variable);
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1241,7 +1228,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1262,7 +1248,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1283,7 +1268,7 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
class LocalReferenceTypeInfo : ObjectReferenceTypeInfo {

public LocalReferenceTypeInfo (string jni)
: base (jni, "JniLocalReference", "JniObjectReferenceType.Local")
: base (jni, "JniObjectReferenceType.Local")
{
}

Expand All@@ -1300,15 +1285,15 @@ public override string[] GetHandleCreationLogStatements (HandleStyle style, stri
class WeakGlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public WeakGlobalReferenceTypeInfo (string jni)
: base (jni, "JniWeakGlobalReference", "JniObjectReferenceType.WeakGlobal")
: base (jni, "JniObjectReferenceType.WeakGlobal")
{
}
}

class GlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public GlobalReferenceTypeInfo (string jni)
: base (jni, "JniGlobalReference", "JniObjectReferenceType.Global")
: base (jni, "JniObjectReferenceType.Global")
{
}
}
Expand DownExpand Up@@ -1399,11 +1384,9 @@ enum Modifier {
}

enum HandleStyle {
SafeHandle,
JIIntPtr,
JIIntPtrPinvokeWithErrors,
XAIntPtr,
JIFunctionPtrWithErrors,
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<Import Project="..\..\build-tools\trim-analyzers\trim-analyzers.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;$(JavaInteropDefineConstants)</DefineConstants>
<DefineConstants>INTEROP;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand Down
18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaException.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,12 +14,7 @@ unsafe public partial class JavaException : Exception, IJavaPeerable
public int JniIdentityHashCode { get; private set; }
public JniManagedPeerStates JniManagedPeerState { get; private set; }

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand DownExpand Up@@ -96,16 +91,11 @@ protected void Construct (ref JniObjectReference reference, JniObjectReferenceOp

public JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand All@@ -131,10 +121,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -143,7 +129,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -287,15 +272,12 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaObject.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,12 +21,7 @@ unsafe public partial class JavaObject : IJavaPeerable

public JniManagedPeerStates JniManagedPeerState => managedPeerState;

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
[NonSerialized] JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
[NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand All@@ -37,16 +32,11 @@ unsafe public partial class JavaObject : IJavaPeerable

public unsafe JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand DownExpand Up@@ -86,10 +76,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -98,7 +84,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -176,15 +161,12 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);

#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

14 changes: 0 additions & 14 deletions src/Java.Interop/Java.Interop/JniAllocObjectRef.cs

This file was deleted.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
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
22 changes: 6 additions & 16 deletions Documentation/Architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,21 +237,12 @@ they don't require cleanup *anyway*. Furthermore, these values should be
*cached* -- see `JniPeerMembers` -- so making them GC objects shouldn't be
a long-term problem.

By doing so, we allow Java.Interop to have *two separate implementations*,
controlled by build-time `#define`s:

* `FEATURE_HANDLES_ARE_SAFE_HANDLES`: Causes `JniObjectReference` to
contain a `SafeHandle` wrapping the underlying JNI handle.
* `FEATURE_HANDLES_ARE_INTPTRS`: Causes `JniObjectReference` to contain
an `IntPtr` for the underlying JNI handle.

The rationale for this is twofold:

1. It allows swapping out "safer" `SafeHandle` and "less safe" `IntPtr`
implementations, permitting easier performance comparisons.
2. It allows migrating the existing code, as some of the existing
tests may assume that JNI handles are garbage collected, which
won't be the case when `FEATURE_HANDLES_ARE_INTPTRS` is set.
Historically, `JniObjectReference` retained an optional `SafeHandle`
implementation behind a build-time feature switch so the safer but slower
representation could be compared with the `IntPtr` representation during the
migration. That experiment is no longer active: the supported implementation is
the `IntPtr`-backed `JniObjectReference` struct, with explicit ownership
transfer and disposal via `JniObjectReference.Dispose (ref reference)`.


## Naming Conventions
Expand DownExpand Up@@ -289,4 +280,3 @@ I halted the above loop after reaching 25686556 instances.
I'm not sure when the JDK would stop handing out references, but it's probably
bound to process heap limits (e.g. depends on 32-bit vs. 64-bit process).


47 changes: 15 additions & 32 deletions build-tools/jnienv-gen/Generator.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,19 +88,19 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ("#nullable enable");
o.WriteLine ();
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 ("#if !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 ("#endif // !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("using System;");
o.WriteLine ("using System.Linq;");
Expand All@@ -123,7 +123,6 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("namespace Java.Interop {");
GenerateJniNativeInterface (o);
o.WriteLine ("}");
WriteSection (o, HandleStyle.SafeHandle, "FEATURE_JNIENVIRONMENT_SAFEHANDLES", "Java.Interop.SafeHandles");
WriteSection (o, HandleStyle.JIIntPtr, "FEATURE_JNIENVIRONMENT_JI_INTPTRS", "Java.Interop.JIIntPtrs");
WriteSection (o, HandleStyle.JIIntPtrPinvokeWithErrors, "FEATURE_JNIENVIRONMENT_JI_PINVOKES", "Java.Interop.JIPinvokes");
WriteSection (o, HandleStyle.XAIntPtr, "FEATURE_JNIENVIRONMENT_XA_INTPTRS", "Java.Interop.XAIntPtrs");
Expand All@@ -149,7 +148,6 @@ static void WriteSection (TextWriter o, HandleStyle style, string define, string
o.WriteLine ();
switch (style) {
case HandleStyle.JIIntPtr:
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
GenerateJniNativeInterfaceInvoker (o, style);
break;
Expand All@@ -172,7 +170,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("#pragma warning disable 0169 // Field never used; ignore since these fields make the structure have the right layout.");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("\t[StructLayout (LayoutKind.Sequential)]");
o.WriteLine ("\tpartial struct JniNativeInterfaceStruct {");
o.WriteLine ();
Expand All@@ -186,7 +184,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("\t\tpublic IntPtr {0};{1} // {2}", e.Name, new string (' ', maxName - e.Name.Length), e.Prototype);
}
o.WriteLine ("\t}");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
Expand DownExpand Up@@ -961,7 +959,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
return new [] {
string.Format ("JniEnvironment.LogCreateLocalRef ({0});", variable),
Expand DownExpand Up@@ -1070,7 +1067,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1084,7 +1080,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1099,7 +1094,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1122,7 +1116,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand DownExpand Up@@ -1186,20 +1179,17 @@ protected override bool IsStatic {

abstract class ObjectReferenceTypeInfo : TypeInfo {

string safeType, refType;
string refType;

public ObjectReferenceTypeInfo (string jni, string safeType, string refType)
public ObjectReferenceTypeInfo (string jni, string refType)
: base (jni)
{
this.safeType = safeType;
this.refType = refType;
}

public override string GetMarshalType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
return isReturn ? safeType : "JniReferenceSafeHandle";
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1212,7 +1202,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1226,8 +1215,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
return string.Format ("{0}.SafeHandle", variable);
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1241,7 +1228,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1262,7 +1248,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1283,7 +1268,7 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
class LocalReferenceTypeInfo : ObjectReferenceTypeInfo {

public LocalReferenceTypeInfo (string jni)
: base (jni, "JniLocalReference", "JniObjectReferenceType.Local")
: base (jni, "JniObjectReferenceType.Local")
{
}

Expand All@@ -1300,15 +1285,15 @@ public override string[] GetHandleCreationLogStatements (HandleStyle style, stri
class WeakGlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public WeakGlobalReferenceTypeInfo (string jni)
: base (jni, "JniWeakGlobalReference", "JniObjectReferenceType.WeakGlobal")
: base (jni, "JniObjectReferenceType.WeakGlobal")
{
}
}

class GlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public GlobalReferenceTypeInfo (string jni)
: base (jni, "JniGlobalReference", "JniObjectReferenceType.Global")
: base (jni, "JniObjectReferenceType.Global")
{
}
}
Expand DownExpand Up@@ -1399,11 +1384,9 @@ enum Modifier {
}

enum HandleStyle {
SafeHandle,
JIIntPtr,
JIIntPtrPinvokeWithErrors,
XAIntPtr,
JIFunctionPtrWithErrors,
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<Import Project="..\..\build-tools\trim-analyzers\trim-analyzers.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;$(JavaInteropDefineConstants)</DefineConstants>
<DefineConstants>INTEROP;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand Down
18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaException.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,12 +14,7 @@ unsafe public partial class JavaException : Exception, IJavaPeerable
public int JniIdentityHashCode { get; private set; }
public JniManagedPeerStates JniManagedPeerState { get; private set; }

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand DownExpand Up@@ -96,16 +91,11 @@ protected void Construct (ref JniObjectReference reference, JniObjectReferenceOp

public JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand All@@ -131,10 +121,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -143,7 +129,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -287,15 +272,12 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaObject.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,12 +21,7 @@ unsafe public partial class JavaObject : IJavaPeerable

public JniManagedPeerStates JniManagedPeerState => managedPeerState;

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
[NonSerialized] JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
[NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand All@@ -37,16 +32,11 @@ unsafe public partial class JavaObject : IJavaPeerable

public unsafe JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand DownExpand Up@@ -86,10 +76,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -98,7 +84,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -176,15 +161,12 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);

#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

14 changes: 0 additions & 14 deletions src/Java.Interop/Java.Interop/JniAllocObjectRef.cs

This file was deleted.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
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
22 changes: 6 additions & 16 deletions Documentation/Architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,21 +237,12 @@ they don't require cleanup *anyway*. Furthermore, these values should be
*cached* -- see `JniPeerMembers` -- so making them GC objects shouldn't be
a long-term problem.

By doing so, we allow Java.Interop to have *two separate implementations*,
controlled by build-time `#define`s:

* `FEATURE_HANDLES_ARE_SAFE_HANDLES`: Causes `JniObjectReference` to
contain a `SafeHandle` wrapping the underlying JNI handle.
* `FEATURE_HANDLES_ARE_INTPTRS`: Causes `JniObjectReference` to contain
an `IntPtr` for the underlying JNI handle.

The rationale for this is twofold:

1. It allows swapping out "safer" `SafeHandle` and "less safe" `IntPtr`
implementations, permitting easier performance comparisons.
2. It allows migrating the existing code, as some of the existing
tests may assume that JNI handles are garbage collected, which
won't be the case when `FEATURE_HANDLES_ARE_INTPTRS` is set.
Historically, `JniObjectReference` retained an optional `SafeHandle`
implementation behind a build-time feature switch so the safer but slower
representation could be compared with the `IntPtr` representation during the
migration. That experiment is no longer active: the supported implementation is
the `IntPtr`-backed `JniObjectReference` struct, with explicit ownership
transfer and disposal via `JniObjectReference.Dispose (ref reference)`.


## Naming Conventions
Expand DownExpand Up@@ -289,4 +280,3 @@ I halted the above loop after reaching 25686556 instances.
I'm not sure when the JDK would stop handing out references, but it's probably
bound to process heap limits (e.g. depends on 32-bit vs. 64-bit process).


47 changes: 15 additions & 32 deletions build-tools/jnienv-gen/Generator.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,19 +88,19 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ("#nullable enable");
o.WriteLine ();
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 ("#if !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 ("#endif // !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("using System;");
o.WriteLine ("using System.Linq;");
Expand All@@ -123,7 +123,6 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("namespace Java.Interop {");
GenerateJniNativeInterface (o);
o.WriteLine ("}");
WriteSection (o, HandleStyle.SafeHandle, "FEATURE_JNIENVIRONMENT_SAFEHANDLES", "Java.Interop.SafeHandles");
WriteSection (o, HandleStyle.JIIntPtr, "FEATURE_JNIENVIRONMENT_JI_INTPTRS", "Java.Interop.JIIntPtrs");
WriteSection (o, HandleStyle.JIIntPtrPinvokeWithErrors, "FEATURE_JNIENVIRONMENT_JI_PINVOKES", "Java.Interop.JIPinvokes");
WriteSection (o, HandleStyle.XAIntPtr, "FEATURE_JNIENVIRONMENT_XA_INTPTRS", "Java.Interop.XAIntPtrs");
Expand All@@ -149,7 +148,6 @@ static void WriteSection (TextWriter o, HandleStyle style, string define, string
o.WriteLine ();
switch (style) {
case HandleStyle.JIIntPtr:
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
GenerateJniNativeInterfaceInvoker (o, style);
break;
Expand All@@ -172,7 +170,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("#pragma warning disable 0169 // Field never used; ignore since these fields make the structure have the right layout.");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("\t[StructLayout (LayoutKind.Sequential)]");
o.WriteLine ("\tpartial struct JniNativeInterfaceStruct {");
o.WriteLine ();
Expand All@@ -186,7 +184,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("\t\tpublic IntPtr {0};{1} // {2}", e.Name, new string (' ', maxName - e.Name.Length), e.Prototype);
}
o.WriteLine ("\t}");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
Expand DownExpand Up@@ -961,7 +959,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
return new [] {
string.Format ("JniEnvironment.LogCreateLocalRef ({0});", variable),
Expand DownExpand Up@@ -1070,7 +1067,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1084,7 +1080,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1099,7 +1094,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1122,7 +1116,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand DownExpand Up@@ -1186,20 +1179,17 @@ protected override bool IsStatic {

abstract class ObjectReferenceTypeInfo : TypeInfo {

string safeType, refType;
string refType;

public ObjectReferenceTypeInfo (string jni, string safeType, string refType)
public ObjectReferenceTypeInfo (string jni, string refType)
: base (jni)
{
this.safeType = safeType;
this.refType = refType;
}

public override string GetMarshalType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
return isReturn ? safeType : "JniReferenceSafeHandle";
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1212,7 +1202,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1226,8 +1215,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
return string.Format ("{0}.SafeHandle", variable);
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1241,7 +1228,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1262,7 +1248,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1283,7 +1268,7 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
class LocalReferenceTypeInfo : ObjectReferenceTypeInfo {

public LocalReferenceTypeInfo (string jni)
: base (jni, "JniLocalReference", "JniObjectReferenceType.Local")
: base (jni, "JniObjectReferenceType.Local")
{
}

Expand All@@ -1300,15 +1285,15 @@ public override string[] GetHandleCreationLogStatements (HandleStyle style, stri
class WeakGlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public WeakGlobalReferenceTypeInfo (string jni)
: base (jni, "JniWeakGlobalReference", "JniObjectReferenceType.WeakGlobal")
: base (jni, "JniObjectReferenceType.WeakGlobal")
{
}
}

class GlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public GlobalReferenceTypeInfo (string jni)
: base (jni, "JniGlobalReference", "JniObjectReferenceType.Global")
: base (jni, "JniObjectReferenceType.Global")
{
}
}
Expand DownExpand Up@@ -1399,11 +1384,9 @@ enum Modifier {
}

enum HandleStyle {
SafeHandle,
JIIntPtr,
JIIntPtrPinvokeWithErrors,
XAIntPtr,
JIFunctionPtrWithErrors,
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<Import Project="..\..\build-tools\trim-analyzers\trim-analyzers.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;$(JavaInteropDefineConstants)</DefineConstants>
<DefineConstants>INTEROP;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand Down
18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaException.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,12 +14,7 @@ unsafe public partial class JavaException : Exception, IJavaPeerable
public int JniIdentityHashCode { get; private set; }
public JniManagedPeerStates JniManagedPeerState { get; private set; }

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand DownExpand Up@@ -96,16 +91,11 @@ protected void Construct (ref JniObjectReference reference, JniObjectReferenceOp

public JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand All@@ -131,10 +121,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -143,7 +129,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -287,15 +272,12 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaObject.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,12 +21,7 @@ unsafe public partial class JavaObject : IJavaPeerable

public JniManagedPeerStates JniManagedPeerState => managedPeerState;

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
[NonSerialized] JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
[NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand All@@ -37,16 +32,11 @@ unsafe public partial class JavaObject : IJavaPeerable

public unsafe JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand DownExpand Up@@ -86,10 +76,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -98,7 +84,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -176,15 +161,12 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);

#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

14 changes: 0 additions & 14 deletions src/Java.Interop/Java.Interop/JniAllocObjectRef.cs

This file was deleted.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
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
22 changes: 6 additions & 16 deletions Documentation/Architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,21 +237,12 @@ they don't require cleanup *anyway*. Furthermore, these values should be
*cached* -- see `JniPeerMembers` -- so making them GC objects shouldn't be
a long-term problem.

By doing so, we allow Java.Interop to have *two separate implementations*,
controlled by build-time `#define`s:

* `FEATURE_HANDLES_ARE_SAFE_HANDLES`: Causes `JniObjectReference` to
contain a `SafeHandle` wrapping the underlying JNI handle.
* `FEATURE_HANDLES_ARE_INTPTRS`: Causes `JniObjectReference` to contain
an `IntPtr` for the underlying JNI handle.

The rationale for this is twofold:

1. It allows swapping out "safer" `SafeHandle` and "less safe" `IntPtr`
implementations, permitting easier performance comparisons.
2. It allows migrating the existing code, as some of the existing
tests may assume that JNI handles are garbage collected, which
won't be the case when `FEATURE_HANDLES_ARE_INTPTRS` is set.
Historically, `JniObjectReference` retained an optional `SafeHandle`
implementation behind a build-time feature switch so the safer but slower
representation could be compared with the `IntPtr` representation during the
migration. That experiment is no longer active: the supported implementation is
the `IntPtr`-backed `JniObjectReference` struct, with explicit ownership
transfer and disposal via `JniObjectReference.Dispose (ref reference)`.


## Naming Conventions
Expand DownExpand Up@@ -289,4 +280,3 @@ I halted the above loop after reaching 25686556 instances.
I'm not sure when the JDK would stop handing out references, but it's probably
bound to process heap limits (e.g. depends on 32-bit vs. 64-bit process).


47 changes: 15 additions & 32 deletions build-tools/jnienv-gen/Generator.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,19 +88,19 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ("#nullable enable");
o.WriteLine ();
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 ("#if !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 ("#endif // !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("using System;");
o.WriteLine ("using System.Linq;");
Expand All@@ -123,7 +123,6 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("namespace Java.Interop {");
GenerateJniNativeInterface (o);
o.WriteLine ("}");
WriteSection (o, HandleStyle.SafeHandle, "FEATURE_JNIENVIRONMENT_SAFEHANDLES", "Java.Interop.SafeHandles");
WriteSection (o, HandleStyle.JIIntPtr, "FEATURE_JNIENVIRONMENT_JI_INTPTRS", "Java.Interop.JIIntPtrs");
WriteSection (o, HandleStyle.JIIntPtrPinvokeWithErrors, "FEATURE_JNIENVIRONMENT_JI_PINVOKES", "Java.Interop.JIPinvokes");
WriteSection (o, HandleStyle.XAIntPtr, "FEATURE_JNIENVIRONMENT_XA_INTPTRS", "Java.Interop.XAIntPtrs");
Expand All@@ -149,7 +148,6 @@ static void WriteSection (TextWriter o, HandleStyle style, string define, string
o.WriteLine ();
switch (style) {
case HandleStyle.JIIntPtr:
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
GenerateJniNativeInterfaceInvoker (o, style);
break;
Expand All@@ -172,7 +170,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("#pragma warning disable 0169 // Field never used; ignore since these fields make the structure have the right layout.");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("\t[StructLayout (LayoutKind.Sequential)]");
o.WriteLine ("\tpartial struct JniNativeInterfaceStruct {");
o.WriteLine ();
Expand All@@ -186,7 +184,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("\t\tpublic IntPtr {0};{1} // {2}", e.Name, new string (' ', maxName - e.Name.Length), e.Prototype);
}
o.WriteLine ("\t}");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
Expand DownExpand Up@@ -961,7 +959,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
return new [] {
string.Format ("JniEnvironment.LogCreateLocalRef ({0});", variable),
Expand DownExpand Up@@ -1070,7 +1067,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1084,7 +1080,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1099,7 +1094,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1122,7 +1116,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand DownExpand Up@@ -1186,20 +1179,17 @@ protected override bool IsStatic {

abstract class ObjectReferenceTypeInfo : TypeInfo {

string safeType, refType;
string refType;

public ObjectReferenceTypeInfo (string jni, string safeType, string refType)
public ObjectReferenceTypeInfo (string jni, string refType)
: base (jni)
{
this.safeType = safeType;
this.refType = refType;
}

public override string GetMarshalType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
return isReturn ? safeType : "JniReferenceSafeHandle";
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1212,7 +1202,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1226,8 +1215,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
return string.Format ("{0}.SafeHandle", variable);
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1241,7 +1228,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1262,7 +1248,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1283,7 +1268,7 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
class LocalReferenceTypeInfo : ObjectReferenceTypeInfo {

public LocalReferenceTypeInfo (string jni)
: base (jni, "JniLocalReference", "JniObjectReferenceType.Local")
: base (jni, "JniObjectReferenceType.Local")
{
}

Expand All@@ -1300,15 +1285,15 @@ public override string[] GetHandleCreationLogStatements (HandleStyle style, stri
class WeakGlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public WeakGlobalReferenceTypeInfo (string jni)
: base (jni, "JniWeakGlobalReference", "JniObjectReferenceType.WeakGlobal")
: base (jni, "JniObjectReferenceType.WeakGlobal")
{
}
}

class GlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public GlobalReferenceTypeInfo (string jni)
: base (jni, "JniGlobalReference", "JniObjectReferenceType.Global")
: base (jni, "JniObjectReferenceType.Global")
{
}
}
Expand DownExpand Up@@ -1399,11 +1384,9 @@ enum Modifier {
}

enum HandleStyle {
SafeHandle,
JIIntPtr,
JIIntPtrPinvokeWithErrors,
XAIntPtr,
JIFunctionPtrWithErrors,
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<Import Project="..\..\build-tools\trim-analyzers\trim-analyzers.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;$(JavaInteropDefineConstants)</DefineConstants>
<DefineConstants>INTEROP;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand Down
18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaException.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,12 +14,7 @@ unsafe public partial class JavaException : Exception, IJavaPeerable
public int JniIdentityHashCode { get; private set; }
public JniManagedPeerStates JniManagedPeerState { get; private set; }

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand DownExpand Up@@ -96,16 +91,11 @@ protected void Construct (ref JniObjectReference reference, JniObjectReferenceOp

public JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand All@@ -131,10 +121,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -143,7 +129,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -287,15 +272,12 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaObject.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,12 +21,7 @@ unsafe public partial class JavaObject : IJavaPeerable

public JniManagedPeerStates JniManagedPeerState => managedPeerState;

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
[NonSerialized] JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
[NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand All@@ -37,16 +32,11 @@ unsafe public partial class JavaObject : IJavaPeerable

public unsafe JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand DownExpand Up@@ -86,10 +76,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -98,7 +84,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -176,15 +161,12 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);

#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

14 changes: 0 additions & 14 deletions src/Java.Interop/Java.Interop/JniAllocObjectRef.cs

This file was deleted.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
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
22 changes: 6 additions & 16 deletions Documentation/Architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,21 +237,12 @@ they don't require cleanup *anyway*. Furthermore, these values should be
*cached* -- see `JniPeerMembers` -- so making them GC objects shouldn't be
a long-term problem.

By doing so, we allow Java.Interop to have *two separate implementations*,
controlled by build-time `#define`s:

* `FEATURE_HANDLES_ARE_SAFE_HANDLES`: Causes `JniObjectReference` to
contain a `SafeHandle` wrapping the underlying JNI handle.
* `FEATURE_HANDLES_ARE_INTPTRS`: Causes `JniObjectReference` to contain
an `IntPtr` for the underlying JNI handle.

The rationale for this is twofold:

1. It allows swapping out "safer" `SafeHandle` and "less safe" `IntPtr`
implementations, permitting easier performance comparisons.
2. It allows migrating the existing code, as some of the existing
tests may assume that JNI handles are garbage collected, which
won't be the case when `FEATURE_HANDLES_ARE_INTPTRS` is set.
Historically, `JniObjectReference` retained an optional `SafeHandle`
implementation behind a build-time feature switch so the safer but slower
representation could be compared with the `IntPtr` representation during the
migration. That experiment is no longer active: the supported implementation is
the `IntPtr`-backed `JniObjectReference` struct, with explicit ownership
transfer and disposal via `JniObjectReference.Dispose (ref reference)`.


## Naming Conventions
Expand DownExpand Up@@ -289,4 +280,3 @@ I halted the above loop after reaching 25686556 instances.
I'm not sure when the JDK would stop handing out references, but it's probably
bound to process heap limits (e.g. depends on 32-bit vs. 64-bit process).


47 changes: 15 additions & 32 deletions build-tools/jnienv-gen/Generator.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,19 +88,19 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ("#nullable enable");
o.WriteLine ();
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 ("#if !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 ("#endif // !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("using System;");
o.WriteLine ("using System.Linq;");
Expand All@@ -123,7 +123,6 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("namespace Java.Interop {");
GenerateJniNativeInterface (o);
o.WriteLine ("}");
WriteSection (o, HandleStyle.SafeHandle, "FEATURE_JNIENVIRONMENT_SAFEHANDLES", "Java.Interop.SafeHandles");
WriteSection (o, HandleStyle.JIIntPtr, "FEATURE_JNIENVIRONMENT_JI_INTPTRS", "Java.Interop.JIIntPtrs");
WriteSection (o, HandleStyle.JIIntPtrPinvokeWithErrors, "FEATURE_JNIENVIRONMENT_JI_PINVOKES", "Java.Interop.JIPinvokes");
WriteSection (o, HandleStyle.XAIntPtr, "FEATURE_JNIENVIRONMENT_XA_INTPTRS", "Java.Interop.XAIntPtrs");
Expand All@@ -149,7 +148,6 @@ static void WriteSection (TextWriter o, HandleStyle style, string define, string
o.WriteLine ();
switch (style) {
case HandleStyle.JIIntPtr:
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
GenerateJniNativeInterfaceInvoker (o, style);
break;
Expand All@@ -172,7 +170,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("#pragma warning disable 0169 // Field never used; ignore since these fields make the structure have the right layout.");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("\t[StructLayout (LayoutKind.Sequential)]");
o.WriteLine ("\tpartial struct JniNativeInterfaceStruct {");
o.WriteLine ();
Expand All@@ -186,7 +184,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("\t\tpublic IntPtr {0};{1} // {2}", e.Name, new string (' ', maxName - e.Name.Length), e.Prototype);
}
o.WriteLine ("\t}");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
Expand DownExpand Up@@ -961,7 +959,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
return new [] {
string.Format ("JniEnvironment.LogCreateLocalRef ({0});", variable),
Expand DownExpand Up@@ -1070,7 +1067,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1084,7 +1080,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1099,7 +1094,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1122,7 +1116,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand DownExpand Up@@ -1186,20 +1179,17 @@ protected override bool IsStatic {

abstract class ObjectReferenceTypeInfo : TypeInfo {

string safeType, refType;
string refType;

public ObjectReferenceTypeInfo (string jni, string safeType, string refType)
public ObjectReferenceTypeInfo (string jni, string refType)
: base (jni)
{
this.safeType = safeType;
this.refType = refType;
}

public override string GetMarshalType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
return isReturn ? safeType : "JniReferenceSafeHandle";
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1212,7 +1202,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1226,8 +1215,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
return string.Format ("{0}.SafeHandle", variable);
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1241,7 +1228,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1262,7 +1248,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1283,7 +1268,7 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
class LocalReferenceTypeInfo : ObjectReferenceTypeInfo {

public LocalReferenceTypeInfo (string jni)
: base (jni, "JniLocalReference", "JniObjectReferenceType.Local")
: base (jni, "JniObjectReferenceType.Local")
{
}

Expand All@@ -1300,15 +1285,15 @@ public override string[] GetHandleCreationLogStatements (HandleStyle style, stri
class WeakGlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public WeakGlobalReferenceTypeInfo (string jni)
: base (jni, "JniWeakGlobalReference", "JniObjectReferenceType.WeakGlobal")
: base (jni, "JniObjectReferenceType.WeakGlobal")
{
}
}

class GlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public GlobalReferenceTypeInfo (string jni)
: base (jni, "JniGlobalReference", "JniObjectReferenceType.Global")
: base (jni, "JniObjectReferenceType.Global")
{
}
}
Expand DownExpand Up@@ -1399,11 +1384,9 @@ enum Modifier {
}

enum HandleStyle {
SafeHandle,
JIIntPtr,
JIIntPtrPinvokeWithErrors,
XAIntPtr,
JIFunctionPtrWithErrors,
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<Import Project="..\..\build-tools\trim-analyzers\trim-analyzers.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;$(JavaInteropDefineConstants)</DefineConstants>
<DefineConstants>INTEROP;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand Down
18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaException.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,12 +14,7 @@ unsafe public partial class JavaException : Exception, IJavaPeerable
public int JniIdentityHashCode { get; private set; }
public JniManagedPeerStates JniManagedPeerState { get; private set; }

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand DownExpand Up@@ -96,16 +91,11 @@ protected void Construct (ref JniObjectReference reference, JniObjectReferenceOp

public JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand All@@ -131,10 +121,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -143,7 +129,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -287,15 +272,12 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaObject.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,12 +21,7 @@ unsafe public partial class JavaObject : IJavaPeerable

public JniManagedPeerStates JniManagedPeerState => managedPeerState;

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
[NonSerialized] JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
[NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand All@@ -37,16 +32,11 @@ unsafe public partial class JavaObject : IJavaPeerable

public unsafe JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand DownExpand Up@@ -86,10 +76,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -98,7 +84,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -176,15 +161,12 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);

#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

14 changes: 0 additions & 14 deletions src/Java.Interop/Java.Interop/JniAllocObjectRef.cs

This file was deleted.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
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
22 changes: 6 additions & 16 deletions Documentation/Architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,21 +237,12 @@ they don't require cleanup *anyway*. Furthermore, these values should be
*cached* -- see `JniPeerMembers` -- so making them GC objects shouldn't be
a long-term problem.

By doing so, we allow Java.Interop to have *two separate implementations*,
controlled by build-time `#define`s:

* `FEATURE_HANDLES_ARE_SAFE_HANDLES`: Causes `JniObjectReference` to
contain a `SafeHandle` wrapping the underlying JNI handle.
* `FEATURE_HANDLES_ARE_INTPTRS`: Causes `JniObjectReference` to contain
an `IntPtr` for the underlying JNI handle.

The rationale for this is twofold:

1. It allows swapping out "safer" `SafeHandle` and "less safe" `IntPtr`
implementations, permitting easier performance comparisons.
2. It allows migrating the existing code, as some of the existing
tests may assume that JNI handles are garbage collected, which
won't be the case when `FEATURE_HANDLES_ARE_INTPTRS` is set.
Historically, `JniObjectReference` retained an optional `SafeHandle`
implementation behind a build-time feature switch so the safer but slower
representation could be compared with the `IntPtr` representation during the
migration. That experiment is no longer active: the supported implementation is
the `IntPtr`-backed `JniObjectReference` struct, with explicit ownership
transfer and disposal via `JniObjectReference.Dispose (ref reference)`.


## Naming Conventions
Expand DownExpand Up@@ -289,4 +280,3 @@ I halted the above loop after reaching 25686556 instances.
I'm not sure when the JDK would stop handing out references, but it's probably
bound to process heap limits (e.g. depends on 32-bit vs. 64-bit process).


47 changes: 15 additions & 32 deletions build-tools/jnienv-gen/Generator.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,19 +88,19 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ("#nullable enable");
o.WriteLine ();
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 ("#if !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 ("#endif // !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("using System;");
o.WriteLine ("using System.Linq;");
Expand All@@ -123,7 +123,6 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("namespace Java.Interop {");
GenerateJniNativeInterface (o);
o.WriteLine ("}");
WriteSection (o, HandleStyle.SafeHandle, "FEATURE_JNIENVIRONMENT_SAFEHANDLES", "Java.Interop.SafeHandles");
WriteSection (o, HandleStyle.JIIntPtr, "FEATURE_JNIENVIRONMENT_JI_INTPTRS", "Java.Interop.JIIntPtrs");
WriteSection (o, HandleStyle.JIIntPtrPinvokeWithErrors, "FEATURE_JNIENVIRONMENT_JI_PINVOKES", "Java.Interop.JIPinvokes");
WriteSection (o, HandleStyle.XAIntPtr, "FEATURE_JNIENVIRONMENT_XA_INTPTRS", "Java.Interop.XAIntPtrs");
Expand All@@ -149,7 +148,6 @@ static void WriteSection (TextWriter o, HandleStyle style, string define, string
o.WriteLine ();
switch (style) {
case HandleStyle.JIIntPtr:
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
GenerateJniNativeInterfaceInvoker (o, style);
break;
Expand All@@ -172,7 +170,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("#pragma warning disable 0169 // Field never used; ignore since these fields make the structure have the right layout.");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("\t[StructLayout (LayoutKind.Sequential)]");
o.WriteLine ("\tpartial struct JniNativeInterfaceStruct {");
o.WriteLine ();
Expand All@@ -186,7 +184,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("\t\tpublic IntPtr {0};{1} // {2}", e.Name, new string (' ', maxName - e.Name.Length), e.Prototype);
}
o.WriteLine ("\t}");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
Expand DownExpand Up@@ -961,7 +959,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
return new [] {
string.Format ("JniEnvironment.LogCreateLocalRef ({0});", variable),
Expand DownExpand Up@@ -1070,7 +1067,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1084,7 +1080,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1099,7 +1094,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1122,7 +1116,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand DownExpand Up@@ -1186,20 +1179,17 @@ protected override bool IsStatic {

abstract class ObjectReferenceTypeInfo : TypeInfo {

string safeType, refType;
string refType;

public ObjectReferenceTypeInfo (string jni, string safeType, string refType)
public ObjectReferenceTypeInfo (string jni, string refType)
: base (jni)
{
this.safeType = safeType;
this.refType = refType;
}

public override string GetMarshalType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
return isReturn ? safeType : "JniReferenceSafeHandle";
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1212,7 +1202,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1226,8 +1215,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
return string.Format ("{0}.SafeHandle", variable);
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1241,7 +1228,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1262,7 +1248,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1283,7 +1268,7 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
class LocalReferenceTypeInfo : ObjectReferenceTypeInfo {

public LocalReferenceTypeInfo (string jni)
: base (jni, "JniLocalReference", "JniObjectReferenceType.Local")
: base (jni, "JniObjectReferenceType.Local")
{
}

Expand All@@ -1300,15 +1285,15 @@ public override string[] GetHandleCreationLogStatements (HandleStyle style, stri
class WeakGlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public WeakGlobalReferenceTypeInfo (string jni)
: base (jni, "JniWeakGlobalReference", "JniObjectReferenceType.WeakGlobal")
: base (jni, "JniObjectReferenceType.WeakGlobal")
{
}
}

class GlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public GlobalReferenceTypeInfo (string jni)
: base (jni, "JniGlobalReference", "JniObjectReferenceType.Global")
: base (jni, "JniObjectReferenceType.Global")
{
}
}
Expand DownExpand Up@@ -1399,11 +1384,9 @@ enum Modifier {
}

enum HandleStyle {
SafeHandle,
JIIntPtr,
JIIntPtrPinvokeWithErrors,
XAIntPtr,
JIFunctionPtrWithErrors,
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<Import Project="..\..\build-tools\trim-analyzers\trim-analyzers.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;$(JavaInteropDefineConstants)</DefineConstants>
<DefineConstants>INTEROP;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand Down
18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaException.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,12 +14,7 @@ unsafe public partial class JavaException : Exception, IJavaPeerable
public int JniIdentityHashCode { get; private set; }
public JniManagedPeerStates JniManagedPeerState { get; private set; }

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand DownExpand Up@@ -96,16 +91,11 @@ protected void Construct (ref JniObjectReference reference, JniObjectReferenceOp

public JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand All@@ -131,10 +121,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -143,7 +129,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -287,15 +272,12 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaObject.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,12 +21,7 @@ unsafe public partial class JavaObject : IJavaPeerable

public JniManagedPeerStates JniManagedPeerState => managedPeerState;

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
[NonSerialized] JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
[NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand All@@ -37,16 +32,11 @@ unsafe public partial class JavaObject : IJavaPeerable

public unsafe JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand DownExpand Up@@ -86,10 +76,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -98,7 +84,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -176,15 +161,12 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);

#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

14 changes: 0 additions & 14 deletions src/Java.Interop/Java.Interop/JniAllocObjectRef.cs

This file was deleted.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
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
22 changes: 6 additions & 16 deletions Documentation/Architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,21 +237,12 @@ they don't require cleanup *anyway*. Furthermore, these values should be
*cached* -- see `JniPeerMembers` -- so making them GC objects shouldn't be
a long-term problem.

By doing so, we allow Java.Interop to have *two separate implementations*,
controlled by build-time `#define`s:

* `FEATURE_HANDLES_ARE_SAFE_HANDLES`: Causes `JniObjectReference` to
contain a `SafeHandle` wrapping the underlying JNI handle.
* `FEATURE_HANDLES_ARE_INTPTRS`: Causes `JniObjectReference` to contain
an `IntPtr` for the underlying JNI handle.

The rationale for this is twofold:

1. It allows swapping out "safer" `SafeHandle` and "less safe" `IntPtr`
implementations, permitting easier performance comparisons.
2. It allows migrating the existing code, as some of the existing
tests may assume that JNI handles are garbage collected, which
won't be the case when `FEATURE_HANDLES_ARE_INTPTRS` is set.
Historically, `JniObjectReference` retained an optional `SafeHandle`
implementation behind a build-time feature switch so the safer but slower
representation could be compared with the `IntPtr` representation during the
migration. That experiment is no longer active: the supported implementation is
the `IntPtr`-backed `JniObjectReference` struct, with explicit ownership
transfer and disposal via `JniObjectReference.Dispose (ref reference)`.


## Naming Conventions
Expand DownExpand Up@@ -289,4 +280,3 @@ I halted the above loop after reaching 25686556 instances.
I'm not sure when the JDK would stop handing out references, but it's probably
bound to process heap limits (e.g. depends on 32-bit vs. 64-bit process).


47 changes: 15 additions & 32 deletions build-tools/jnienv-gen/Generator.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,19 +88,19 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ("#nullable enable");
o.WriteLine ();
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 ("#if !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 ("#endif // !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("using System;");
o.WriteLine ("using System.Linq;");
Expand All@@ -123,7 +123,6 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("namespace Java.Interop {");
GenerateJniNativeInterface (o);
o.WriteLine ("}");
WriteSection (o, HandleStyle.SafeHandle, "FEATURE_JNIENVIRONMENT_SAFEHANDLES", "Java.Interop.SafeHandles");
WriteSection (o, HandleStyle.JIIntPtr, "FEATURE_JNIENVIRONMENT_JI_INTPTRS", "Java.Interop.JIIntPtrs");
WriteSection (o, HandleStyle.JIIntPtrPinvokeWithErrors, "FEATURE_JNIENVIRONMENT_JI_PINVOKES", "Java.Interop.JIPinvokes");
WriteSection (o, HandleStyle.XAIntPtr, "FEATURE_JNIENVIRONMENT_XA_INTPTRS", "Java.Interop.XAIntPtrs");
Expand All@@ -149,7 +148,6 @@ static void WriteSection (TextWriter o, HandleStyle style, string define, string
o.WriteLine ();
switch (style) {
case HandleStyle.JIIntPtr:
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
GenerateJniNativeInterfaceInvoker (o, style);
break;
Expand All@@ -172,7 +170,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("#pragma warning disable 0169 // Field never used; ignore since these fields make the structure have the right layout.");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("\t[StructLayout (LayoutKind.Sequential)]");
o.WriteLine ("\tpartial struct JniNativeInterfaceStruct {");
o.WriteLine ();
Expand All@@ -186,7 +184,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("\t\tpublic IntPtr {0};{1} // {2}", e.Name, new string (' ', maxName - e.Name.Length), e.Prototype);
}
o.WriteLine ("\t}");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
Expand DownExpand Up@@ -961,7 +959,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
return new [] {
string.Format ("JniEnvironment.LogCreateLocalRef ({0});", variable),
Expand DownExpand Up@@ -1070,7 +1067,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1084,7 +1080,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1099,7 +1094,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1122,7 +1116,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand DownExpand Up@@ -1186,20 +1179,17 @@ protected override bool IsStatic {

abstract class ObjectReferenceTypeInfo : TypeInfo {

string safeType, refType;
string refType;

public ObjectReferenceTypeInfo (string jni, string safeType, string refType)
public ObjectReferenceTypeInfo (string jni, string refType)
: base (jni)
{
this.safeType = safeType;
this.refType = refType;
}

public override string GetMarshalType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
return isReturn ? safeType : "JniReferenceSafeHandle";
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1212,7 +1202,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1226,8 +1215,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
return string.Format ("{0}.SafeHandle", variable);
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1241,7 +1228,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1262,7 +1248,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1283,7 +1268,7 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
class LocalReferenceTypeInfo : ObjectReferenceTypeInfo {

public LocalReferenceTypeInfo (string jni)
: base (jni, "JniLocalReference", "JniObjectReferenceType.Local")
: base (jni, "JniObjectReferenceType.Local")
{
}

Expand All@@ -1300,15 +1285,15 @@ public override string[] GetHandleCreationLogStatements (HandleStyle style, stri
class WeakGlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public WeakGlobalReferenceTypeInfo (string jni)
: base (jni, "JniWeakGlobalReference", "JniObjectReferenceType.WeakGlobal")
: base (jni, "JniObjectReferenceType.WeakGlobal")
{
}
}

class GlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public GlobalReferenceTypeInfo (string jni)
: base (jni, "JniGlobalReference", "JniObjectReferenceType.Global")
: base (jni, "JniObjectReferenceType.Global")
{
}
}
Expand DownExpand Up@@ -1399,11 +1384,9 @@ enum Modifier {
}

enum HandleStyle {
SafeHandle,
JIIntPtr,
JIIntPtrPinvokeWithErrors,
XAIntPtr,
JIFunctionPtrWithErrors,
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<Import Project="..\..\build-tools\trim-analyzers\trim-analyzers.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;$(JavaInteropDefineConstants)</DefineConstants>
<DefineConstants>INTEROP;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand Down
18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaException.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,12 +14,7 @@ unsafe public partial class JavaException : Exception, IJavaPeerable
public int JniIdentityHashCode { get; private set; }
public JniManagedPeerStates JniManagedPeerState { get; private set; }

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand DownExpand Up@@ -96,16 +91,11 @@ protected void Construct (ref JniObjectReference reference, JniObjectReferenceOp

public JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand All@@ -131,10 +121,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -143,7 +129,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -287,15 +272,12 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaObject.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,12 +21,7 @@ unsafe public partial class JavaObject : IJavaPeerable

public JniManagedPeerStates JniManagedPeerState => managedPeerState;

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
[NonSerialized] JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
[NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand All@@ -37,16 +32,11 @@ unsafe public partial class JavaObject : IJavaPeerable

public unsafe JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand DownExpand Up@@ -86,10 +76,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -98,7 +84,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -176,15 +161,12 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);

#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

14 changes: 0 additions & 14 deletions src/Java.Interop/Java.Interop/JniAllocObjectRef.cs

This file was deleted.

Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
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
22 changes: 6 additions & 16 deletions Documentation/Architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,21 +237,12 @@ they don't require cleanup *anyway*. Furthermore, these values should be
*cached* -- see `JniPeerMembers` -- so making them GC objects shouldn't be
a long-term problem.

By doing so, we allow Java.Interop to have *two separate implementations*,
controlled by build-time `#define`s:

* `FEATURE_HANDLES_ARE_SAFE_HANDLES`: Causes `JniObjectReference` to
contain a `SafeHandle` wrapping the underlying JNI handle.
* `FEATURE_HANDLES_ARE_INTPTRS`: Causes `JniObjectReference` to contain
an `IntPtr` for the underlying JNI handle.

The rationale for this is twofold:

1. It allows swapping out "safer" `SafeHandle` and "less safe" `IntPtr`
implementations, permitting easier performance comparisons.
2. It allows migrating the existing code, as some of the existing
tests may assume that JNI handles are garbage collected, which
won't be the case when `FEATURE_HANDLES_ARE_INTPTRS` is set.
Historically, `JniObjectReference` retained an optional `SafeHandle`
implementation behind a build-time feature switch so the safer but slower
representation could be compared with the `IntPtr` representation during the
migration. That experiment is no longer active: the supported implementation is
the `IntPtr`-backed `JniObjectReference` struct, with explicit ownership
transfer and disposal via `JniObjectReference.Dispose (ref reference)`.


## Naming Conventions
Expand DownExpand Up@@ -289,4 +280,3 @@ I halted the above loop after reaching 25686556 instances.
I'm not sure when the JDK would stop handing out references, but it's probably
bound to process heap limits (e.g. depends on 32-bit vs. 64-bit process).


47 changes: 15 additions & 32 deletions build-tools/jnienv-gen/Generator.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,19 +88,19 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ("#nullable enable");
o.WriteLine ();
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 ("#if !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 ("#endif // !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS && (FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES && (FEATURE_JNIENVIRONMENT_XA_INTPTRS || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define _NAMESPACE_PER_HANDLE");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_XA_INTPTRS && FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ();
o.WriteLine ("using System;");
o.WriteLine ("using System.Linq;");
Expand All@@ -123,7 +123,6 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("namespace Java.Interop {");
GenerateJniNativeInterface (o);
o.WriteLine ("}");
WriteSection (o, HandleStyle.SafeHandle, "FEATURE_JNIENVIRONMENT_SAFEHANDLES", "Java.Interop.SafeHandles");
WriteSection (o, HandleStyle.JIIntPtr, "FEATURE_JNIENVIRONMENT_JI_INTPTRS", "Java.Interop.JIIntPtrs");
WriteSection (o, HandleStyle.JIIntPtrPinvokeWithErrors, "FEATURE_JNIENVIRONMENT_JI_PINVOKES", "Java.Interop.JIPinvokes");
WriteSection (o, HandleStyle.XAIntPtr, "FEATURE_JNIENVIRONMENT_XA_INTPTRS", "Java.Interop.XAIntPtrs");
Expand All@@ -149,7 +148,6 @@ static void WriteSection (TextWriter o, HandleStyle style, string define, string
o.WriteLine ();
switch (style) {
case HandleStyle.JIIntPtr:
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
GenerateJniNativeInterfaceInvoker (o, style);
break;
Expand All@@ -172,7 +170,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("#pragma warning disable 0169 // Field never used; ignore since these fields make the structure have the right layout.");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("\t[StructLayout (LayoutKind.Sequential)]");
o.WriteLine ("\tpartial struct JniNativeInterfaceStruct {");
o.WriteLine ();
Expand All@@ -186,7 +184,7 @@ static void GenerateJniNativeInterface (TextWriter o)
o.WriteLine ("\t\tpublic IntPtr {0};{1} // {2}", e.Name, new string (' ', maxName - e.Name.Length), e.Prototype);
}
o.WriteLine ("\t}");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES || FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#endif // FEATURE_JNIENVIRONMENT_JI_INTPTRS || FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();

o.WriteLine ("#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
Expand DownExpand Up@@ -961,7 +959,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.XAIntPtr:
return new [] {
string.Format ("JniEnvironment.LogCreateLocalRef ({0});", variable),
Expand DownExpand Up@@ -1070,7 +1067,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1084,7 +1080,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1099,7 +1094,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1122,7 +1116,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand DownExpand Up@@ -1186,20 +1179,17 @@ protected override bool IsStatic {

abstract class ObjectReferenceTypeInfo : TypeInfo {

string safeType, refType;
string refType;

public ObjectReferenceTypeInfo (string jni, string safeType, string refType)
public ObjectReferenceTypeInfo (string jni, string refType)
: base (jni)
{
this.safeType = safeType;
this.refType = refType;
}

public override string GetMarshalType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
return isReturn ? safeType : "JniReferenceSafeHandle";
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1212,7 +1202,6 @@ public override string GetMarshalType (HandleStyle style, bool isReturn, bool is
public override string GetManagedType (HandleStyle style, bool isReturn, bool isPinvoke)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1226,8 +1215,6 @@ public override string GetManagedType (HandleStyle style, bool isReturn, bool is
public override string GetManagedToMarshalExpression (HandleStyle style, string variable)
{
switch (style) {
case HandleStyle.SafeHandle:
return string.Format ("{0}.SafeHandle", variable);
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1241,7 +1228,6 @@ public override string GetManagedToMarshalExpression (HandleStyle style, string
public override string[] GetMarshalToManagedStatements (HandleStyle style, string variable, JniFunction entry)
{
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1262,7 +1248,6 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
? variable.Substring (1)
: variable;
switch (style) {
case HandleStyle.SafeHandle:
case HandleStyle.JIIntPtr:
case HandleStyle.JIIntPtrPinvokeWithErrors:
case HandleStyle.JIFunctionPtrWithErrors:
Expand All@@ -1283,7 +1268,7 @@ public override string[] VerifyParameter (HandleStyle style, string variable)
class LocalReferenceTypeInfo : ObjectReferenceTypeInfo {

public LocalReferenceTypeInfo (string jni)
: base (jni, "JniLocalReference", "JniObjectReferenceType.Local")
: base (jni, "JniObjectReferenceType.Local")
{
}

Expand All@@ -1300,15 +1285,15 @@ public override string[] GetHandleCreationLogStatements (HandleStyle style, stri
class WeakGlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public WeakGlobalReferenceTypeInfo (string jni)
: base (jni, "JniWeakGlobalReference", "JniObjectReferenceType.WeakGlobal")
: base (jni, "JniObjectReferenceType.WeakGlobal")
{
}
}

class GlobalReferenceTypeInfo : ObjectReferenceTypeInfo {

public GlobalReferenceTypeInfo (string jni)
: base (jni, "JniGlobalReference", "JniObjectReferenceType.Global")
: base (jni, "JniObjectReferenceType.Global")
{
}
}
Expand DownExpand Up@@ -1399,11 +1384,9 @@ enum Modifier {
}

enum HandleStyle {
SafeHandle,
JIIntPtr,
JIIntPtrPinvokeWithErrors,
XAIntPtr,
JIFunctionPtrWithErrors,
}
}

2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<Import Project="..\..\build-tools\trim-analyzers\trim-analyzers.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;$(JavaInteropDefineConstants)</DefineConstants>
<DefineConstants>INTEROP;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand Down
18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaException.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,12 +14,7 @@ unsafe public partial class JavaException : Exception, IJavaPeerable
public int JniIdentityHashCode { get; private set; }
public JniManagedPeerStates JniManagedPeerState { get; private set; }

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand DownExpand Up@@ -96,16 +91,11 @@ protected void Construct (ref JniObjectReference reference, JniObjectReferenceOp

public JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand All@@ -131,10 +121,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -143,7 +129,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -287,15 +272,12 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

18 changes: 0 additions & 18 deletions src/Java.Interop/Java.Interop/JavaObject.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,12 +21,7 @@ unsafe public partial class JavaObject : IJavaPeerable

public JniManagedPeerStates JniManagedPeerState => managedPeerState;

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
[NonSerialized] JniObjectReference reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
[NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

protected static readonly JniObjectReference* InvalidJniObjectReference = null;

Expand All@@ -37,16 +32,11 @@ unsafe public partial class JavaObject : IJavaPeerable

public unsafe JniObjectReference PeerReference {
get {
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
return reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
return default;
}
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}
}

Expand DownExpand Up@@ -86,10 +76,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
return;
}

#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
this.reference = reference;
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
var c = jniObjectReferenceControlBlock;
if (c == null) {
c = jniObjectReferenceControlBlock =
Expand All@@ -98,7 +84,6 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
c->handle = reference.Handle;
c->handle_type = (int) reference.Type;
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

JniObjectReference.Dispose (ref reference, options);
}
Expand DownExpand Up@@ -176,15 +161,12 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
{
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);

#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
}

IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
(IntPtr) jniObjectReferenceControlBlock;
}
}

14 changes: 0 additions & 14 deletions src/Java.Interop/Java.Interop/JniAllocObjectRef.cs

This file was deleted.

Loading