Skip to content

Commit f33ed5b

Browse files
committed
[Java.Interop] Cleanup: Kill JniMarshalMethod.
JniMarshalMethod contained System.Linq.Expressions-using code to wrap an ~arbitrary delegate with a new delegate which, when invoked, will create a JniTransition instance, a try/catch block, and invoke the "source" delegate within the try/catch block for exception marshaling. Which is ~identical to what Java.Interop.ExportedMemberBuilder does! Which means we (kinda/sorta) had two different ways to do the same thing...except where they weren't *identical*: 1. JniMarshalMethod was used from JniType.RegisterNativeMethods() 2. JniMarshalMethod supported "direct" methods. A "direct" method is a method which would skip the "normal" generated marshaling infrastructure and support passing *all* parameters, as-is, to the wrapped delegate. JavaProxyObject made use of this to register e.g. java.lang.Object.hashCode() and delegate the implementation to System.Object.GetHashCode(). ExportedMemberBuilder didn't support this. Additionally, (1) introduced an implementation discrepency between Xamarin.Android and Java.Interop: JniMarshalMethod was NOT used in Xamarin.Android (to avoid additional runtime code generation). Clean this up by improving ExportedMemberBuilder to support direct method invocations, and then remove JniMarshalMethod. Note: checking for "direct methods" is done via heuristic: if the method has *at least* two parameters, and the first two parameters are System.IntPtr, then it is assumed to be a direct method. Fix JavaProxyObject and ManagedPeer so that they manually manage the JniTransition paradigm (commit 3cd8310), avoiding the need for System.Linq.Expressions use within Java.Interop.dll. Review JniRuntime.JniExportedMemberBuilder API, "push down" core methods that tools/jnimarshalmethod-gen uses so that ExportedMemberBuilder doesn't need to be used directly, but instead jnimarshalmethod-gen uses JniRuntime.ExportedMemberBuilder instead.
1 parent da5d1b8 commit f33ed5b

File tree

13 files changed

+281
-318
lines changed

13 files changed

+281
-318
lines changed

src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs

Lines changed: 132 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ public ExportedMemberBuilder (JniRuntime runtime)
2424
OnSetRuntime (runtime);
2525
}
2626

27+
public override LambdaExpression CreateMarshalToManagedExpression (MethodInfo method)
28+
{
29+
if (method == null)
30+
throw new ArgumentNullException (nameof (method));
31+
32+
return CreateMarshalToManagedExpression (method, null, method.DeclaringType);
33+
}
34+
2735
public override IEnumerable<JniNativeMethodRegistration> GetExportedMemberRegistrations (Type declaringType)
2836
{
2937
if (declaringType == null)
@@ -38,33 +46,31 @@ IEnumerable<JniNativeMethodRegistration> CreateExportedMemberRegistrationIterato
3846
if (exports == null || exports.Length == 0)
3947
continue;
4048
var export = exports [0];
41-
yield return CreateMarshalFromJniMethodRegistration (export, declaringType, method);
49+
yield return CreateMarshalToManagedMethodRegistration (export, method, declaringType);
4250
}
4351
}
4452

45-
public JniNativeMethodRegistration CreateMarshalFromJniMethodRegistration (JavaCallableAttribute export, Type type, MethodInfo method)
53+
public JniNativeMethodRegistration CreateMarshalToManagedMethodRegistration (JavaCallableAttribute export, MethodInfo method, Type type = null)
4654
{
4755
if (export == null)
4856
throw new ArgumentNullException ("export");
49-
if (type == null)
50-
throw new ArgumentNullException ("type");
5157
if (method == null)
5258
throw new ArgumentNullException ("method");
5359

5460
string signature = GetJniMethodSignature (export, method);
5561
return new JniNativeMethodRegistration () {
5662
Name = GetJniMethodName (export, method),
5763
Signature = signature,
58-
Marshaler = CreateJniMethodMarshaler (export, type, method),
64+
Marshaler = CreateJniMethodMarshaler (method, export, type),
5965
};
6066
}
6167

62-
protected virtual string GetJniMethodName (JavaCallableAttribute export, MethodInfo method)
68+
string GetJniMethodName (JavaCallableAttribute export, MethodInfo method)
6369
{
6470
return export.Name ?? "n_" + method.Name;
6571
}
6672

67-
public virtual string GetJniMethodSignature (JavaCallableAttribute export, MethodInfo method)
73+
public string GetJniMethodSignature (JavaCallableAttribute export, MethodInfo method)
6874
{
6975
if (export == null)
7076
throw new ArgumentNullException ("export");
@@ -75,7 +81,8 @@ public virtual string GetJniMethodSignature (JavaCallableAttribute export, Metho
7581
return export.Signature;
7682

7783
var signature = new StringBuilder ().Append ("(");
78-
foreach (var p in method.GetParameters ()) {
84+
var methodParameters = method.GetParameters ();
85+
foreach (var p in IsDirectMethod (methodParameters) ? methodParameters.Skip (2) : methodParameters) {
7986
signature.Append (GetTypeSignature (p));
8087
}
8188
signature.Append (")");
@@ -97,28 +104,26 @@ string GetTypeSignature (ParameterInfo p)
97104
throw new NotSupportedException ("Don't know how to determine JNI signature for parameter type: " + p.ParameterType.FullName + ".");
98105
}
99106

100-
Delegate CreateJniMethodMarshaler (JavaCallableAttribute export, Type type, MethodInfo method)
107+
Delegate CreateJniMethodMarshaler (MethodInfo method, JavaCallableAttribute export, Type type)
101108
{
102-
var e = CreateMarshalFromJniMethodExpression (export, type, method);
109+
var e = CreateMarshalToManagedExpression (method, export, type);
103110
return e.Compile ();
104111
}
105112

106-
// TODO: make internal, and add [InternalsVisibleTo] for Java.Interop.Export-Tests
107-
public virtual LambdaExpression CreateMarshalFromJniMethodExpression (JavaCallableAttribute export, Type type, MethodInfo method)
113+
public LambdaExpression CreateMarshalToManagedExpression (MethodInfo method, JavaCallableAttribute callable, Type type = null)
108114
{
109-
if (export == null)
110-
throw new ArgumentNullException ("export");
111-
if (type == null)
112-
throw new ArgumentNullException ("type");
113115
if (method == null)
114116
throw new ArgumentNullException ("method");
117+
type = type ?? method.DeclaringType;
115118

116119
var methodParameters = method.GetParameters ();
117120

118-
CheckMarshalTypesMatch (method, export.Signature, methodParameters);
121+
CheckMarshalTypesMatch (method, callable?.Signature, methodParameters);
119122

120-
var jnienv = Expression.Parameter (typeof (IntPtr), "__jnienv");
121-
var context = Expression.Parameter (typeof (IntPtr), method.IsStatic ? "__class" : "__this");
123+
bool direct = IsDirectMethod (methodParameters);
124+
125+
var jnienv = Expression.Parameter (typeof (IntPtr), direct ? methodParameters [0].Name : "__jnienv");
126+
var context = Expression.Parameter (typeof (IntPtr), direct ? methodParameters [1].Name : (method.IsStatic ? "__class" : "__this"));
122127

123128
var envp = Expression.Variable (typeof (JniTransition), "__envp");
124129
var jvm = Expression.Variable (typeof (JniRuntime), "__jvm");
@@ -146,7 +151,17 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (JavaCallab
146151
var invokeParameters = new List<Expression> (methodParameters.Length);
147152
for (int i = 0; i < methodParameters.Length; ++i) {
148153
var marshaler = GetValueMarshaler (methodParameters [i]);
149-
var np = Expression.Parameter (marshaler.MarshalType, methodParameters [i].Name);
154+
ParameterExpression np;
155+
if (i > 1 || !direct)
156+
np = Expression.Parameter (marshaler.MarshalType, methodParameters [i].Name);
157+
else {
158+
if (i == 0)
159+
np = jnienv;
160+
else if (i == 1)
161+
np = context;
162+
else
163+
throw new InvalidOperationException ("Should not be reached.");
164+
}
150165
var p = marshaler.CreateParameterToManagedExpression (marshalerContext, np, methodParameters [i].Attributes, methodParameters [i].ParameterType);
151166
marshalParameters.Add (np);
152167
invokeParameters.Add (p);
@@ -190,10 +205,14 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (JavaCallab
190205
envpBody.Add (Expression.Label (exit, Expression.Default (jniRType)));
191206
}
192207

193-
var funcTypeParams = new List<Type> () {
194-
typeof (IntPtr),
195-
typeof (IntPtr),
196-
};
208+
var funcTypeParams = new List<Type> ();
209+
var bodyParams = new List<ParameterExpression> ();
210+
if (!direct) {
211+
funcTypeParams.Add (typeof (IntPtr));
212+
funcTypeParams.Add (typeof (IntPtr));
213+
bodyParams.Add (jnienv);
214+
bodyParams.Add (context);
215+
}
197216
foreach (var p in marshalParameters)
198217
funcTypeParams.Add (p.Type);
199218
if (ret != null)
@@ -202,14 +221,24 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (JavaCallab
202221
? Expression.GetActionType (funcTypeParams.ToArray ())
203222
: Expression.GetFuncType (funcTypeParams.ToArray ());
204223

205-
var bodyParams = new List<ParameterExpression> { jnienv, context };
206224
bodyParams.AddRange (marshalParameters);
207225
var body = Expression.Block (envpVars, envpBody);
208226
return Expression.Lambda (marshalerType, body, bodyParams);
209227
}
210228

229+
// Heuristic: if first two parameters are IntPtr, this is a "direct" wrapper.
230+
static bool IsDirectMethod (ParameterInfo[] methodParameters)
231+
{
232+
return methodParameters.Length >= 2 &&
233+
methodParameters [0].ParameterType == typeof (IntPtr) &&
234+
methodParameters [1].ParameterType == typeof (IntPtr);
235+
}
236+
211237
JniValueMarshaler GetValueMarshaler (ParameterInfo parameter)
212238
{
239+
if (parameter.ParameterType == typeof(IntPtr))
240+
return IntPtrValueMarshaler.Instance;
241+
213242
var attr = parameter.GetCustomAttribute<JniValueMarshalerAttribute> ();
214243
if (attr != null) {
215244
return (JniValueMarshaler) Activator.CreateInstance (attr.MarshalerType);
@@ -223,8 +252,14 @@ void CheckMarshalTypesMatch (MethodInfo method, string signature, ParameterInfo[
223252
return;
224253

225254
var mptypes = JniSignature.GetMarshalParameterTypes (signature).ToList ();
255+
int rpcount = methodParameters.Length;
226256
int len = Math.Min (methodParameters.Length, mptypes.Count);
227-
for (int i = 0; i < len; ++i) {
257+
int start = 0;
258+
if (IsDirectMethod (methodParameters)) {
259+
start += 2;
260+
rpcount -= 2;
261+
}
262+
for (int i = start; i < len; ++i) {
228263
var vm = GetValueMarshaler (methodParameters [i]);
229264
var jni = vm.MarshalType;
230265
if (mptypes [i] != jni)
@@ -233,31 +268,32 @@ void CheckMarshalTypesMatch (MethodInfo method, string signature, ParameterInfo[
233268
"signature");
234269
}
235270

236-
if (mptypes.Count != methodParameters.Length)
271+
if (mptypes.Count != rpcount)
237272
throw new ArgumentException (
238273
string.Format ("JNI parametr count mismatch: signature contains {0} parameters, method contains {1}.",
239274
mptypes.Count, methodParameters.Length),
240-
"signature");
275+
nameof (signature));
241276

242277
var jrinfo = JniSignature.GetMarshalReturnType (signature);
243278
var mrvm = GetValueMarshaler (method.ReturnParameter);
244279
var mrinfo = mrvm.MarshalType;
245280
if (mrinfo != jrinfo)
246281
throw new ArgumentException (
247282
string.Format ("JNI return type mismatch. Type '{0}' != '{1}'.", jrinfo, mrinfo),
248-
"signature");
283+
nameof (signature));
249284
}
250285

286+
static ConstructorInfo JniTransitionConstructor =
287+
(from c in typeof (JniTransition).GetTypeInfo ().DeclaredConstructors
288+
let p = c.GetParameters ()
289+
where p.Length == 1 && p [0].ParameterType == typeof (IntPtr)
290+
select c)
291+
.First ();
292+
251293
static Expression CreateJniTransition (ParameterExpression jnienv)
252294
{
253-
var ctor =
254-
(from c in typeof(JniTransition).GetTypeInfo ().DeclaredConstructors
255-
let p = c.GetParameters ()
256-
where p.Length == 1 && p [0].ParameterType == typeof (IntPtr)
257-
select c)
258-
.First ();
259295
return Expression.New (
260-
ctor,
296+
JniTransitionConstructor,
261297
jnienv);
262298
}
263299

@@ -364,5 +400,65 @@ static Type ExtractMarshalTypeFromSignature (string signature, ref int index)
364400
#endif
365401
}
366402
}
403+
404+
class IntPtrValueMarshaler : JniValueMarshaler<IntPtr> {
405+
internal static IntPtrValueMarshaler Instance = new IntPtrValueMarshaler ();
406+
407+
public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize)
408+
{
409+
return sourceValue;
410+
}
411+
412+
public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType)
413+
{
414+
return sourceValue;
415+
}
416+
417+
public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue)
418+
{
419+
return sourceValue;
420+
}
421+
422+
423+
public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType)
424+
{
425+
throw new NotImplementedException ();
426+
}
427+
428+
public override IntPtr CreateGenericValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType)
429+
{
430+
throw new NotImplementedException ();
431+
}
432+
433+
public override JniValueMarshalerState CreateArgumentState (object value, ParameterAttributes synchronize)
434+
{
435+
throw new NotSupportedException ();
436+
}
437+
438+
public override JniValueMarshalerState CreateGenericArgumentState (IntPtr value, ParameterAttributes synchronize)
439+
{
440+
throw new NotSupportedException ();
441+
}
442+
443+
public override JniValueMarshalerState CreateObjectReferenceArgumentState (object value, ParameterAttributes synchronize)
444+
{
445+
throw new NotImplementedException ();
446+
}
447+
448+
public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState (IntPtr value, ParameterAttributes synchronize)
449+
{
450+
throw new NotImplementedException ();
451+
}
452+
453+
public override void DestroyArgumentState (object value, ref JniValueMarshalerState state, ParameterAttributes synchronize)
454+
{
455+
throw new NotImplementedException ();
456+
}
457+
458+
public override void DestroyGenericArgumentState (IntPtr value, ref JniValueMarshalerState state, ParameterAttributes synchronize)
459+
{
460+
throw new NotImplementedException ();
461+
}
462+
}
367463
}
368464

0 commit comments

Comments
 (0)