Skip to content
This repository was archived by the owner on Jul 6, 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
1 change: 1 addition & 0 deletions src/Java.Interop.Export/Java.Interop.Export.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<ProjectGuid>{B501D075-6183-4E1D-92C9-F7B5002475B1}</ProjectGuid>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
Expand Down
63 changes: 56 additions & 7 deletions src/Java.Interop.Export/Java.Interop/MarshalMemberBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,64 @@ public LambdaExpression CreateMarshalToManagedExpression (MethodInfo method, Jav
}
foreach (var p in marshalParameters)
funcTypeParams.Add (p.Type);
if (ret != null)
funcTypeParams.Add (ret.Type);
var marshalerType = ret == null
? Expression.GetActionType (funcTypeParams.ToArray ())
: Expression.GetFuncType (funcTypeParams.ToArray ());
var marshalerType = GetMarshalerType (ret?.Type, funcTypeParams, method.DeclaringType);

bodyParams.AddRange (marshalParameters);
var body = Expression.Block (envpVars, envpBody);
return Expression.Lambda (marshalerType, body, bodyParams);

return marshalerType == null
? Expression.Lambda (body, bodyParams)
: Expression.Lambda (marshalerType, body, bodyParams);
}

static Type GetMarshalerType (Type returnType, List<Type> funcTypeParams, Type declaringType)
{
// `mscorlib.dll` & `System.Core.dll` only provide Action<…>/Func<…> types for up to 16 parameters
if (funcTypeParams.Count <= 16) {
if (returnType != null)
funcTypeParams.Add (returnType);
return returnType == null
? Expression.GetActionType (funcTypeParams.ToArray ())
: Expression.GetFuncType (funcTypeParams.ToArray ());
}

// Too many parameters; does a `_JniMarshal_*` type exist in the type's declaring assembly?
funcTypeParams.RemoveRange (0, 2);
var marshalDelegateName = new StringBuilder ();
marshalDelegateName.Append ("_JniMarshal_PP");
foreach (var paramType in funcTypeParams) {
marshalDelegateName.Append (GetJniMarshalDelegateParameterIdentifier (paramType));
}
marshalDelegateName.Append ("_");
if (returnType == null) {
marshalDelegateName.Append ("V");
} else {
marshalDelegateName.Append (GetJniMarshalDelegateParameterIdentifier (returnType));
}

Type marshalDelegateType = declaringType.Assembly.GetType (marshalDelegateName.ToString (), throwOnError: false);

// Punt?; System.Linq.Expressions will automagically produce the needed delegate type.
// Unfortunately, this won't work with jnimarshalmethod-gen.exe.
return marshalDelegateType;
}

static char GetJniMarshalDelegateParameterIdentifier (Type type)
{
if (type == typeof (bool)) return 'Z';
if (type == typeof (byte)) return 'B';
if (type == typeof (sbyte)) return 'B';
if (type == typeof (char)) return 'C';
if (type == typeof (short)) return 'S';
if (type == typeof (ushort)) return 's';
if (type == typeof (int)) return 'I';
if (type == typeof (uint)) return 'i';
if (type == typeof (long)) return 'J';
if (type == typeof (ulong)) return 'j';
if (type == typeof (float)) return 'F';
if (type == typeof (double)) return 'D';
if (type == typeof (void)) return 'V';
return 'L';
}

void CheckMarshalTypesMatch (MethodInfo method, string signature, ParameterInfo[] methodParameters)
Expand All @@ -258,7 +307,7 @@ void CheckMarshalTypesMatch (MethodInfo method, string signature, ParameterInfo[
var jni = vm.MarshalType;
if (mptypes [i] != jni)
throw new ArgumentException (
string.Format ("JNI parameter type mismatch. Type '{0}' != '{1}.", jni, mptypes [i]),
$"JNI parameter type mismatch. Type `{jni}` != `{mptypes [i]}` at index {i} in `{signature}`.",
"signature");
}

Expand Down
76 changes: 76 additions & 0 deletions tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@
using Java.Interop;
using Java.Interop.Expressions;

// For use by `jnimarshalmethod-gen.exe` & `make run-test-jnimarshal`
delegate bool _JniMarshal_PPZBCSIJFDLLLLLDFJ_Z (
IntPtr jnienv,
IntPtr klass,
bool a,
sbyte b,
char c,
short d,
int e,
long f,
float g,
double h,
IntPtr i, // java.lang.Object
IntPtr j, // java.lang.String
IntPtr k, // java.util.ArrayList<String>
IntPtr l, // java.lang.String
IntPtr m, // java.lang.Object
double n,
float o,
long p
);

namespace Java.InteropTests
{
[JniTypeSignature ("com/xamarin/interop/export/ExportType")]
Expand Down Expand Up @@ -83,6 +105,60 @@ public static void StaticActionInt (int i)
public static void StaticActionFloat (float f)
{
}

[JavaCallable ("staticFuncThisMethodTakesLotsOfParameters", Signature="(ZBCSIJFDLjava/lang/Object;Ljava/lang/String;Ljava/util/ArrayList;Ljava/lang/String;Ljava/lang/Object;DFJ)Z")]
public static bool StaticFuncThisMethodTakesLotsOfParameters (
bool a,
sbyte b,
char c,
short d,
int e,
long f,
float g,
double h,
IntPtr i, // java.lang.Object
IntPtr j, // java.lang.String
IntPtr k, // java.util.ArrayList<String>
IntPtr l, // java.lang.String
IntPtr m, // java.lang.Object
double n,
float o,
long p)
{
if (a != false)
return false;
if (b != (byte) 0xb)
return false;
if (c != 'c')
return false;
if (d != (short) 0xd)
return false;
if (e != 0xe)
return false;
if (f != 0xf)
return false;
if (g != 1.0f)
return false;
if (h != 2.0)
return false;
if (i == IntPtr.Zero)
return false;
if (j == IntPtr.Zero)
return false;
if (k == IntPtr.Zero)
return false;
if (l == IntPtr.Zero)
return false;
if (m == IntPtr.Zero)
return false;
if (n != 3.0)
return false;
if (o != 4.0f)
return false;
if (p != 0x70)
return false;
return true;
}
}

[JniValueMarshaler (typeof (MyColorValueMarshaler))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void AddExportMethods ()
var methods = CreateBuilder ()
.GetExportedMemberRegistrations (typeof (ExportTest))
.ToList ();
Assert.AreEqual (10, methods.Count);
Assert.AreEqual (11, methods.Count);

Assert.AreEqual ("action", methods [0].Name);
Assert.AreEqual ("()V", methods [0].Signature);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,62 @@ public static void testStaticMethods () {
public static native void staticActionInt32String (int i, String s);
public static native int staticFuncMyLegacyColorMyColor_MyColor (int color1, int color2);

public void testMethods () {
action ();

actionIJavaObject (this);

long j = funcInt64 ();
if (j != 42)
throw new Error ("funcInt64() should return 42!");
public static native boolean staticFuncThisMethodTakesLotsOfParameters (
boolean a,
byte b,
char c,
short d,
int e,
long f,
float g,
double h,
Object i,
String j,
ArrayList<String> k,
String l,
Object m,
double n,
float o,
long p);

Object o = funcIJavaObject ();
if (o != this)
throw new Error ("funcIJavaObject() should return `this`!");

staticActionInt (1);
staticActionFloat (2.0f);
public void testMethods () {
action ();

actionIJavaObject (this);

long j = funcInt64 ();
if (j != 42)
throw new Error ("funcInt64() should return 42!");

Object o = funcIJavaObject ();
if (o != this)
throw new Error ("funcIJavaObject() should return `this`!");

staticActionInt (1);
staticActionFloat (2.0f);

/*
boolean r = staticFuncThisMethodTakesLotsOfParameters (
false,
(byte) 0xb,
'c',
(short) 0xd,
0xe,
0xf,
1.0f,
2.0,
new Object (),
"j",
new ArrayList<String>(),
"l",
new Object (),
3.0,
4.0f,
0x70
);
if (r != true)
throw new Error ("staticFuncThisMethodTakesLotsOfParameters should return true!");
*/
}

public native void action ();
Expand Down
30 changes: 29 additions & 1 deletion tools/jnimarshalmethod-gen/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
using Mono.Collections.Generic;
using Java.Interop.Tools.Cecil;

#if _DUMP_REGISTER_NATIVE_MEMBERS
using Mono.Linq.Expressions;
#endif // _DUMP_REGISTER_NATIVE_MEMBERS

namespace Xamarin.Android.Tools.JniMarshalMethodGenerator {

class App : MarshalByRefObject
Expand Down Expand Up @@ -393,6 +397,13 @@ void CreateMarshalMethodAssembly (string path)
continue;
}

#if !_ALL_THE_ARGUMENTS
if (method.GetParameters ().Length > 14) {
Warning ($"Methods taking more than 14 parameters is not supported.");
continue;
}
#endif // !_ALL_THE_ARGUMENTS

if (dt == null)
dt = GetTypeBuilder (dm, type);

Expand Down Expand Up @@ -467,7 +478,20 @@ void CreateMarshalMethodAssembly (string path)

static Expression CreateRegistration (string method, string signature, LambdaExpression lambda, ParameterExpression targetType, string methodName)
{
var d = Expression.Call (Delegate_CreateDelegate, Expression.Constant (lambda.Type, typeof (Type)), targetType, Expression.Constant (methodName));
Expression registrationDelegateType = null;
if (lambda.Type.Assembly == typeof (object).Assembly ||
lambda.Type.Assembly == typeof (System.Linq.Enumerable).Assembly) {
registrationDelegateType = Expression.Constant (lambda.Type, typeof (Type));
}
else {
Func<string, bool, Type> getType = Type.GetType;
registrationDelegateType = Expression.Call (getType.GetMethodInfo (),
Expression.Constant (lambda.Type.FullName, typeof (string)),
Expression.Constant (true, typeof (bool)));
registrationDelegateType = Expression.Convert (registrationDelegateType, typeof (Type));
}

var d = Expression.Call (Delegate_CreateDelegate, registrationDelegateType, targetType, Expression.Constant (methodName));
return Expression.New (JniNativeMethodRegistration_ctor,
Expression.Constant (method),
Expression.Constant (signature),
Expand All @@ -488,6 +512,10 @@ static void AddRegisterNativeMembers (TypeBuilder dt, ParameterExpression target
var rb = dt.DefineMethod ("__RegisterNativeMembers",
System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static);
rb.SetCustomAttribute (new CustomAttributeBuilder (typeof (JniAddNativeMethodRegistrationAttribute).GetConstructor (Type.EmptyTypes), new object[0]));
#if _DUMP_REGISTER_NATIVE_MEMBERS
Console.WriteLine ($"## Dumping contents of `{dt.FullName}::__RegisterNativeMembers`: ");
Console.WriteLine (lambda.ToCSharpCode ());
#endif // _DUMP_REGISTER_NATIVE_MEMBERS
lambda.CompileToMethod (rb);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,23 @@
<PropertyGroup>
<OutputPath>$(UtilityOutputFullPath)</OutputPath>
</PropertyGroup>

<PropertyGroup Condition=" '$(_DumpRegisterNativeMembers)' == 'True' ">
<DefineConstants>_DUMP_REGISTER_NATIVE_MEMBERS;$(DefineConstants)</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition=" '$(_AllTheArguments)' == 'True' ">
<DefineConstants>_ALL_THE_ARGUMENTS;$(DefineConstants)</DefineConstants>
</PropertyGroup>

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

<ItemGroup Condition=" '$(_DumpRegisterNativeMembers)' == 'True' ">
<PackageReference Include="Mono.Linq.Expressions" Version="2.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Java.Interop.Export\Java.Interop.Export.csproj" />
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj" />
Expand Down