Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,10 @@ void EmitProxyType (JavaPeerProxyData proxy, Dictionary<string, MethodDefinition
wrapperHandles [uco.WrapperName] = handle;
}

// RegisterNatives
// RegisterNatives + GetFunctionPointer
if (proxy.IsAcw) {
EmitRegisterNatives (proxy.NativeRegistrations, wrapperHandles);
EmitGetFunctionPointer (proxy.NativeRegistrations, wrapperHandles);
}
}

Expand Down Expand Up @@ -729,6 +730,52 @@ void AddUnmanagedCallersOnlyAttribute (MethodDefinitionHandle handle)
_pe.Metadata.AddCustomAttribute (handle, _ucoAttrCtorRef, _ucoAttrBlobHandle);
}

/// <summary>
/// Emits GetFunctionPointer(int methodIndex) → IntPtr that maps ordinals to UCO wrapper function pointers.
/// Used as an alternative dispatch mechanism to RegisterNatives.
/// </summary>
void EmitGetFunctionPointer (List<NativeRegistrationData> registrations,
Dictionary<string, MethodDefinitionHandle> wrapperHandles)
{
// Collect resolved handles in registration order
var resolvedHandles = new List<MethodDefinitionHandle> ();
foreach (var reg in registrations) {
if (wrapperHandles.TryGetValue (reg.WrapperMethodName, out var handle)) {
resolvedHandles.Add (handle);
}
}

_pe.EmitBody ("GetFunctionPointer",
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig |
MethodAttributes.NewSlot | MethodAttributes.Final,
sig => sig.MethodSignature (isInstanceMethod: true).Parameters (1,
rt => rt.Type ().IntPtr (),
p => p.AddParameter ().Type ().Int32 ()),
encoder => {
// For each registration, emit:
// ldarg.1 // load methodIndex
// ldc.i4 <index> // load constant
// bne.un.s <skip> // if not equal, skip to next
// ldftn <wrapper> // load function pointer
// ret
// <skip>:
for (int i = 0; i < resolvedHandles.Count; i++) {
encoder.LoadArgument (1);
encoder.LoadConstantI4 (i);
// bne.un.s with offset = 7 (ldftn is 2-byte opcode + 4-byte token = 6, ret is 1 byte)
encoder.OpCode (ILOpCode.Bne_un_s);
encoder.CodeBuilder.WriteSByte (7);
encoder.OpCode (ILOpCode.Ldftn);
encoder.Token (resolvedHandles [i]);
encoder.OpCode (ILOpCode.Ret);
}
// Default: return IntPtr.Zero
encoder.OpCode (ILOpCode.Ldc_i4_0);
encoder.OpCode (ILOpCode.Conv_i);
encoder.OpCode (ILOpCode.Ret);
});
}

void EmitTypeMapAttribute (TypeMapAttributeData entry)
{
var ctorRef = entry.IsUnconditional ? _typeMapAttrCtorRef2Arg : _typeMapAttrCtorRef3Arg;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,4 +504,22 @@ public void ParseParameterTypes_UnterminatedSignature_ReturnsEmptyList ()
{
Assert.Empty (JniSignatureHelper.ParseParameterTypes ("("));
}

[Fact]
public void Generate_AcwProxy_HasGetFunctionPointer ()
{
var peers = ScanFixtures ();
var acwPeer = peers.First (p => p.JavaName == "my/app/MainActivity");
Assert.False (acwPeer.DoNotGenerateAcw);

using var stream = GenerateAssembly (new [] { acwPeer }, "GetFnPtrTest");
using var pe = new PEReader (stream);
var reader = pe.GetMetadataReader ();

var methodDefs = reader.MethodDefinitions
.Select (h => reader.GetMethodDefinition (h))
.Select (m => reader.GetString (m.Name))
.ToList ();
Assert.Contains ("GetFunctionPointer", methodDefs);
}
}