Skip to content

Commit

Permalink
[Java.Interop] Add JniMemberInfoLookup
Browse files Browse the repository at this point in the history
Context: c6c487b
Context: 312fbf4
Context: 2197579
Context: dotnet/android#7276

There is a desire to remove the "marshal-ilgen" component from
.NET Android, which is responsible for all non-blittable type
marshaling within P/Invoke (and related) invocations.

The largest source of such non-blittable parameter marshaling was
with string marshaling: `JNIEnv::GetFieldID()` was "wrapped" by
`java_interop_jnienv_get_field_id`:

	JI_API jfieldID java_interop_jnienv_get_field_id (JNIEnv *env, jthrowable *_thrown, jclass type, const char* name, const char* signature);

which was P/Invoked within `JniEnvironment.g.cs`:

	partial class NativeMethods {
	    internal static extern unsafe IntPtr java_interop_jnienv_get_field_id (IntPtr jnienv, out IntPtr thrown, jobject type, string name, string signature);
	}

and `string` parameter marshaling is *not* blittable.

Turns out™ that this particular usage of non-blittable parameter
marshaling was fixed and rendered moot by:

  * 312fbf4: C#9 function pointer backend for `JNIEnv` invocations
  * c6c487b: "Standalone" build config to use C#9 function pointers
  * 2197579: Standalone build config is now the default

That said, this code path felt slightly less than ideal: the
"top-level abstraction" for member lookups is an "encoded member",
a string containing the name of the member, a `.`, and the JNI
signature of the member, e.g.:

	_members.InstanceFields.GetBooleanValue("propogateFinallyBlockExecuted.Z", this)

The "encoded member" would need to be split on `.`, and with c6c487b
the name and signature would be separately passed to
`Marshal.StringToCoTaskMemUTF8()`, which performs a memory allocation
and converts the UTF-16 string to UTF-8.

Meanwhile, [C# 11 introduced UTF-8 string literals][0], which allows
the compiler to deal with UTF-8 conversion and memory allocation.

Enter `JniMemberInfoLookup``:

	public ref struct JniMemberInfoLookup {
	    public  string                  EncodedMember   {get;}
	    public  ReadOnlySpan<byte>      MemberName      {get;}
	    public  ReadOnlySpan<byte>      MemberSignature {get;}

	    public JniMemberInfoLookup (string encodedMember, ReadOnlySpan<byte> memberName, ReadOnlySpan<byte> memberSignature);
	}

`JniMemberInfoLookup` removes the need to call
`Marshal.StringToCoTaskMemUTF8()` entirely, at the cost of a more
complicated member invocation:

	// Old and busted:
	bool value = _members.InstanceFields.GetBooleanValue("propogateFinallyBlockExecuted.Z", this);

	// Eventual new hawtness:
	var lookup = new JniMemberInfoLookup (
		"propogateFinallyBlockExecuted.Z",
		"propogateFinallyBlockExecuted"u8,
		"Z"u8);
	bool value = _members.InstanceFields.GetBooleanValue(lookup, this);

Is It Worth It™?  *Maybe*; see the new
`JniFieldLookupTiming.FieldLookupTiming()` test, which allocates a new
`JniPeerMembers` instance and invoke
`members.InstanceFields.GetFieldInfo(string)` and
`members.InstanceFields.GetFieldInfo(JniMemberInfoLookup)`.
(A new `JniPeerMembers` instance is required because `GetFieldInfo()`
caches the field lookup.)  Using `JniMemberInfoLookup` is about
4% faster.

	# FieldLookupTiming Timing: looking up JavaTiming.instanceIntField 10000 times
	#   .InstanceMethods.GetFieldInfo(string):              00:00:02.2780667
	#   .InstanceMethods.GetFieldInfo(JniMemberInfoLookup): 00:00:02.2016146

I'm not sure if this is *actually* worth it, especially as this will
imply an increase in code size.

TODO:

  * Update `JniPeerMembers.*.cs` to use `JniMemberInfoLookup`, so
    that e.g. the above `_members.InstanceFields.GetBooleanValue()`
    overload exists.

  * `generator` changes to use `JniMemberInfoLookup`

[0]: https://learn.microsoft.com/dotnet/csharp/whats-new/csharp-11#utf-8-string-literals
  • Loading branch information
jonpryor committed Mar 29, 2024
1 parent e1c7832 commit 041f066
Show file tree
Hide file tree
Showing 16 changed files with 489 additions and 9 deletions.
4 changes: 4 additions & 0 deletions src/Java.Interop/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

// See: 045b8af7, 6a42bb89, f60906cf, e10f7cb0, etc.
[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniEnvironment.Exceptions")]
[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniEnvironment.InstanceFields")]
[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniEnvironment.InstanceMethods")]
[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniEnvironment.StaticFields")]
[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniEnvironment.StaticMethods")]
[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniPeerMembers.JniStaticMethods")]
[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniRuntime.JniMarshalMemberBuilder")]
[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniPeerMembers.JniStaticFields")]
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
<JNIEnvGenPath>$(BuildToolOutputFullPath)</JNIEnvGenPath>
<LangVersion Condition=" '$(JIBuildingForNetCoreApp)' == 'True' ">9.0</LangVersion>
<LangVersion Condition=" '$(JIBuildingForNetCoreApp)' == 'True' ">12.0</LangVersion>
<LangVersion Condition=" '$(LangVersion)' == '' ">8.0</LangVersion>
<Version>$(JICoreLibVersion)</Version>
<Standalone Condition=" '$(Standalone)' == '' ">true</Standalone>
Expand Down
37 changes: 37 additions & 0 deletions src/Java.Interop/Java.Interop/JniEnvironment.InstanceFields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;

namespace Java.Interop;

partial class JniEnvironment {
partial class InstanceFields {
public static unsafe JniFieldInfo GetFieldID (JniObjectReference type, ReadOnlySpan<byte> name, ReadOnlySpan<byte> signature)

Check failure on line 9 in src/Java.Interop/Java.Interop/JniEnvironment.InstanceFields.cs

View check run for this annotation

Azure Pipelines / Java.Interop (Mac - .NET)

src/Java.Interop/Java.Interop/JniEnvironment.InstanceFields.cs#L9

src/Java.Interop/Java.Interop/JniEnvironment.InstanceFields.cs(9,37): Error RS0016: Symbol 'GetFieldID' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 9 in src/Java.Interop/Java.Interop/JniEnvironment.InstanceFields.cs

View check run for this annotation

Azure Pipelines / Java.Interop

src/Java.Interop/Java.Interop/JniEnvironment.InstanceFields.cs#L9

src/Java.Interop/Java.Interop/JniEnvironment.InstanceFields.cs(9,37): Error RS0016: Symbol 'GetFieldID' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)
{
if (!type.IsValid)
throw new ArgumentException ("Handle must be valid.", "type");

IntPtr env = JniEnvironment.EnvironmentPointer;
IntPtr field;
IntPtr thrown;
fixed (void* name_ptr = &MemoryMarshal.GetReference (name))
fixed (void* signature_ptr = &MemoryMarshal.GetReference (signature)) {
field = JniNativeMethods.GetFieldID (env, type.Handle, (IntPtr) name_ptr, (IntPtr) signature_ptr);
thrown = JniNativeMethods.ExceptionOccurred (env);
}

Exception? __e = JniEnvironment.GetExceptionForLastThrowable (thrown);
if (__e != null)
ExceptionDispatchInfo.Capture (__e).Throw ();

if (field == IntPtr.Zero)
throw new InvalidOperationException ("Should not be reached; `GetFieldID` should have thrown!");

#if DEBUG
return new JniFieldInfo (name.ToString (), signature.ToString (), field, isStatic: false);
#else // DEBUG
return new JniFieldInfo (null!, null!, field, isStatic: false);
#endif // DEBUG
}
}
}
37 changes: 37 additions & 0 deletions src/Java.Interop/Java.Interop/JniEnvironment.InstanceMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;

namespace Java.Interop;

partial class JniEnvironment {
partial class InstanceMethods {
public static unsafe JniMethodInfo GetMethodID (JniObjectReference type, ReadOnlySpan<byte> name, ReadOnlySpan<byte> signature)

Check failure on line 9 in src/Java.Interop/Java.Interop/JniEnvironment.InstanceMethods.cs

View check run for this annotation

Azure Pipelines / Java.Interop (Mac - .NET)

src/Java.Interop/Java.Interop/JniEnvironment.InstanceMethods.cs#L9

src/Java.Interop/Java.Interop/JniEnvironment.InstanceMethods.cs(9,38): Error RS0016: Symbol 'GetMethodID' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 9 in src/Java.Interop/Java.Interop/JniEnvironment.InstanceMethods.cs

View check run for this annotation

Azure Pipelines / Java.Interop

src/Java.Interop/Java.Interop/JniEnvironment.InstanceMethods.cs#L9

src/Java.Interop/Java.Interop/JniEnvironment.InstanceMethods.cs(9,38): Error RS0016: Symbol 'GetMethodID' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)
{
if (!type.IsValid)
throw new ArgumentException ("Handle must be valid.", "type");

IntPtr env = JniEnvironment.EnvironmentPointer;
IntPtr method;
IntPtr thrown;
fixed (void* name_ptr = &MemoryMarshal.GetReference (name))
fixed (void* signature_ptr = &MemoryMarshal.GetReference (signature)) {
method = JniNativeMethods.GetMethodID (env, type.Handle, (IntPtr) name_ptr, (IntPtr) signature_ptr);
thrown = JniNativeMethods.ExceptionOccurred (env);
}

Exception? __e = JniEnvironment.GetExceptionForLastThrowable (thrown);
if (__e != null)
ExceptionDispatchInfo.Capture (__e).Throw ();

if (method == IntPtr.Zero)
throw new InvalidOperationException ("Should not be reached; `GetMethodID` should have thrown!");

#if DEBUG
return new JniMethodInfo (name.ToString (), signature.ToString (), method, isStatic: false);
#else // DEBUG
return new JniMethodInfo (null!, null!, method, isStatic: false);
#endif // DEBUG
}
}
}
38 changes: 38 additions & 0 deletions src/Java.Interop/Java.Interop/JniEnvironment.StaticFields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;

namespace Java.Interop;

partial class JniEnvironment {
partial class StaticFields {

public static unsafe JniFieldInfo GetStaticFieldID (JniObjectReference type, ReadOnlySpan<byte> name, ReadOnlySpan<byte> signature)
{
if (!type.IsValid)
throw new ArgumentException ("Handle must be valid.", "type");

IntPtr env = JniEnvironment.EnvironmentPointer;
IntPtr field;
IntPtr thrown;
fixed (void* name_ptr = &MemoryMarshal.GetReference (name))
fixed (void* signature_ptr = &MemoryMarshal.GetReference (signature)) {
field = JniNativeMethods.GetStaticFieldID (env, type.Handle, (IntPtr) name_ptr, (IntPtr) signature_ptr);
thrown = JniNativeMethods.ExceptionOccurred (env);
}

Exception? __e = JniEnvironment.GetExceptionForLastThrowable (thrown);
if (__e != null)
ExceptionDispatchInfo.Capture (__e).Throw ();

if (field == IntPtr.Zero)
throw new InvalidOperationException ("Should not be reached; `GetFieldID` should have thrown!");

#if DEBUG
return new JniFieldInfo (name.ToString (), signature.ToString (), field, isStatic: false);
#else // DEBUG
return new JniFieldInfo (null!, null!, field, isStatic: false);
#endif // DEBUG
}
}
}
82 changes: 82 additions & 0 deletions src/Java.Interop/Java.Interop/JniEnvironment.StaticMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;

namespace Java.Interop;

partial class JniEnvironment {
partial class StaticMethods {

public static unsafe JniMethodInfo GetStaticMethodID (JniObjectReference type, ReadOnlySpan<byte> name, ReadOnlySpan<byte> signature)

Check failure on line 12 in src/Java.Interop/Java.Interop/JniEnvironment.StaticMethods.cs

View check run for this annotation

Azure Pipelines / Java.Interop (Mac - .NET)

src/Java.Interop/Java.Interop/JniEnvironment.StaticMethods.cs#L12

src/Java.Interop/Java.Interop/JniEnvironment.StaticMethods.cs(12,38): Error RS0016: Symbol 'GetStaticMethodID' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)
{
if (!type.IsValid)
throw new ArgumentException ("Handle must be valid.", "type");

IntPtr env = JniEnvironment.EnvironmentPointer;
IntPtr method;
IntPtr thrown;
fixed (void* name_ptr = &MemoryMarshal.GetReference (name))
fixed (void* signature_ptr = &MemoryMarshal.GetReference (signature)) {
method = JniNativeMethods.GetStaticMethodID (env, type.Handle, (IntPtr) name_ptr, (IntPtr) signature_ptr);
thrown = JniNativeMethods.ExceptionOccurred (env);
}

Exception? __e = JniEnvironment.GetExceptionForLastThrowable (thrown);
if (__e != null)
ExceptionDispatchInfo.Capture (__e).Throw ();

if (method == IntPtr.Zero)
throw new InvalidOperationException ("Should not be reached; `GetStaticMethodID` should have thrown!");

#if DEBUG
return new JniMethodInfo (name.ToString (), signature.ToString (), method, isStatic: true);
#else // DEBUG
return new JniMethodInfo (null!, null!, method, isStatic: true);
#endif // DEBUG
}

internal static unsafe bool TryGetStaticMethod (
JniObjectReference type,
ReadOnlySpan<byte> name,
ReadOnlySpan<byte> signature,
[NotNullWhen(true)]
out JniMethodInfo? method)
{
method = null;

if (!type.IsValid)
throw new ArgumentException ("Handle must be valid.", "type");

IntPtr env = JniEnvironment.EnvironmentPointer;
IntPtr id;
IntPtr thrown;
fixed (void* name_ptr = &MemoryMarshal.GetReference (name))
fixed (void* signature_ptr = &MemoryMarshal.GetReference (signature)) {
id = JniNativeMethods.GetStaticMethodID (env, type.Handle, (IntPtr) name_ptr, (IntPtr) signature_ptr);
thrown = JniNativeMethods.ExceptionOccurred (env);
}

if (thrown != IntPtr.Zero) {
JniNativeMethods.ExceptionClear (env);
JniEnvironment.References.RawDeleteLocalRef (env, thrown);
thrown = IntPtr.Zero;
return false;
}

Debug.Assert (id != IntPtr.Zero);
if (id == IntPtr.Zero) {
return false;
}

#if DEBUG
method = new JniMethodInfo (name.ToString (), signature.ToString (), id, isStatic: true);
#else // DEBUG
method = new JniMethodInfo (null!, null!, id, isStatic: true);
#endif // DEBUG

return true;
}
}
}
16 changes: 16 additions & 0 deletions src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace Java.Interop;

public ref struct JniMemberInfoLookup {

Check failure on line 5 in src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs

View check run for this annotation

Azure Pipelines / Java.Interop (Mac - .NET)

src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs#L5

src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs(5,19): Error RS0016: Symbol 'JniMemberInfoLookup' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 5 in src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs

View check run for this annotation

Azure Pipelines / Java.Interop (Mac - .NET)

src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs#L5

src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs(5,19): Error RS0016: Symbol 'implicit constructor for 'JniMemberInfoLookup'' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 5 in src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs

View check run for this annotation

Azure Pipelines / Java.Interop

src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs#L5

src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs(5,19): Error RS0016: Symbol 'JniMemberInfoLookup' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)

Check failure on line 5 in src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs

View check run for this annotation

Azure Pipelines / Java.Interop

src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs#L5

src/Java.Interop/Java.Interop/JniMemberInfoLookup.cs(5,19): Error RS0016: Symbol 'implicit constructor for 'JniMemberInfoLookup'' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)
public string EncodedMember {get; private set;}
public ReadOnlySpan<byte> MemberName {get; private set;}
public ReadOnlySpan<byte> MemberSignature {get; private set;}

public JniMemberInfoLookup (string encodedMember, ReadOnlySpan<byte> memberName, ReadOnlySpan<byte> memberSignature)
{
EncodedMember = encodedMember;
MemberName = memberName;
MemberSignature = memberSignature;
}
}
11 changes: 11 additions & 0 deletions src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceFields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ public JniFieldInfo GetFieldInfo (string encodedMember)
return f;
}
}

public JniFieldInfo GetFieldInfo (JniMemberInfoLookup member)
{
lock (InstanceFields) {
if (!InstanceFields.TryGetValue (member.EncodedMember, out var f)) {
f = Members.JniPeerType.GetInstanceField (member.MemberName, member.MemberSignature);
InstanceFields.Add (member.EncodedMember, f);
}
return f;
}
}
}}
}

42 changes: 42 additions & 0 deletions src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,48 @@ JniMethodInfo GetMethodInfo (string method, string signature)
return JniPeerType.GetInstanceMethod (method, signature);
}


public JniMethodInfo GetMethodInfo (JniMemberInfoLookup member)
{
lock (InstanceMethods) {
if (InstanceMethods.TryGetValue (member.EncodedMember, out var m)) {
return m;
}
}
var info = GetMethodInfo (member.MemberName, member.MemberSignature);
lock (InstanceMethods) {
if (InstanceMethods.TryGetValue (member.EncodedMember, out var m)) {
return m;
}
InstanceMethods.Add (member.EncodedMember, info);
}
return info;
}

JniMethodInfo GetMethodInfo (ReadOnlySpan<byte> method, ReadOnlySpan<byte> signature)
{
var m = (JniMethodInfo?) null;
var newMethod = JniEnvironment.Runtime.TypeManager.GetReplacementMethodInfo (Members.JniPeerTypeName, method, signature);
if (newMethod.HasValue) {
var typeName = newMethod.Value.TargetJniType ?? Members.JniPeerTypeName;
var methodName = newMethod.Value.TargetJniMethodName ?? method.ToString ();
var methodSig = newMethod.Value.TargetJniMethodSignature ?? signature.ToString ();

using var t = new JniType (typeName);
if (newMethod.Value.TargetJniMethodInstanceToStatic &&
t.TryGetStaticMethod (methodName, methodSig, out m)) {
m.ParameterCount = newMethod.Value.TargetJniMethodParameterCount;
m.StaticRedirect = new JniType (typeName);
return m;
}
if (t.TryGetInstanceMethod (methodName, methodSig, out m)) {
return m;
}
Console.Error.WriteLine ($"warning: For declared method `{Members.JniPeerTypeName}.{method.ToString ()}.{signature.ToString ()}`, could not find requested method `{typeName}.{methodName}.{methodSig}`!");
}
return JniPeerType.GetInstanceMethod (method, signature);
}

public unsafe JniObjectReference StartCreateInstance (string constructorSignature, Type declaringType, JniArgumentValue* parameters)
{
if (constructorSignature == null)
Expand Down
11 changes: 11 additions & 0 deletions src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticFields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ public JniFieldInfo GetFieldInfo (string encodedMember)
}
}

public JniFieldInfo GetFieldInfo (JniMemberInfoLookup member)
{
lock (StaticFields) {
if (!StaticFields.TryGetValue (member.EncodedMember, out var f)) {
f = Members.JniPeerType.GetInstanceField (member.MemberName, member.MemberSignature);
StaticFields.Add (member.EncodedMember, f);
}
return f;
}
}

internal void Dispose ()
{
StaticFields.Clear ();
Expand Down
65 changes: 65 additions & 0 deletions src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,46 @@ JniMethodInfo GetMethodInfo (string method, string signature)
return Members.JniPeerType.GetStaticMethod (method, signature);
}

public JniMethodInfo GetMethodInfo (JniMemberInfoLookup member)
{
lock (StaticMethods) {
if (StaticMethods.TryGetValue (member.EncodedMember, out var m)) {
return m;
}
}
var info = GetMethodInfo (member.MemberName, member.MemberSignature);
lock (StaticMethods) {
if (StaticMethods.TryGetValue (member.EncodedMember, out var m)) {
return m;
}
StaticMethods.Add (member.EncodedMember, info);
}
return info;
}

JniMethodInfo GetMethodInfo (ReadOnlySpan<byte> method, ReadOnlySpan<byte> signature)
{
var m = (JniMethodInfo?) null;
var newMethod = JniEnvironment.Runtime.TypeManager.GetReplacementMethodInfo (Members.JniPeerTypeName, method, signature);
if (newMethod.HasValue) {
using var t = new JniType (newMethod.Value.TargetJniType ?? Members.JniPeerTypeName);
if (t.TryGetStaticMethod (
newMethod.Value.TargetJniMethodName ?? method.ToString (),
newMethod.Value.TargetJniMethodSignature ?? signature.ToString (),
out m)) {
return m;
}
}
if (Members.JniPeerType.TryGetStaticMethod (method, signature, out m)) {
return m;
}
m = FindInFallbackTypes (method, signature);
if (m != null) {
return m;
}
return Members.JniPeerType.GetStaticMethod (method, signature);
}

#pragma warning disable CA1801
JniType GetMethodDeclaringType (JniMethodInfo method)
{
Expand Down Expand Up @@ -105,6 +145,31 @@ JniType GetMethodDeclaringType (JniMethodInfo method)
}
#endif // NET

JniMethodInfo? FindInFallbackTypes (ReadOnlySpan<byte> method, ReadOnlySpan<byte> signature)
{
var fallbackTypes = JniEnvironment.Runtime.TypeManager.GetStaticMethodFallbackTypes (Members.JniPeerTypeName);
if (fallbackTypes == null) {
return null;
}
foreach (var ft in fallbackTypes) {
JniType? t = null;
try {
if (!JniType.TryParse (ft, out t)) {
continue;
}
if (t.TryGetStaticMethod (method, signature, out var m)) {
m.StaticRedirect = t;
t = null;
return m;
}
}
finally {
t?.Dispose ();
}
}
return null;
}

public unsafe void InvokeVoidMethod (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
Expand Down
Loading

0 comments on commit 041f066

Please sign in to comment.