Skip to content

Commit ff4053c

Browse files
committed
[Java.Interop] Value Marshaling with Expressions
Fixes: dotnet#8 Extend the JniValueMarshaler abstraction from commit 77a6bf8 so that JniValueMarshaler can *also* marshal from/to Java via System.Linq.Expressions expression trees: partial class JniValueMarshaler { public virtual Type MarshalType {get;} public virtual Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType); public virtual Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize); public virtual Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue); } The members: * MarshalType: The JNI type that this marshaler marshals from/to. The default value is typeof(IntPtr), making it suitable for marshaling reference types. * CreateParameterToManagedExpression(): Java > Managed marshaler. Convert the Java parameter `sourceValue`, of type `MarshalType`, to the managed type `targetType`. * CreateParameterFromManagedExpression(): Managed > Java marshaler. Convert the managed parameter `sourceValue` to `MarshalType`. * CreateReturnValueFromManagedExpression(): Managed > Java marshaler, for return values (not parameters). Convert the managed parameter `sourceValue` to `MarshalType`, suitable for use when returning to Java. The default implementation of these methods uses the marshaling behavior provided by the abstract JniValueMarshaler.CreateValue(), JniValueMarshaler.CreateArgumentState(), and JniValueMarshaler.DestroyArgumentState() methods. CreateParameterToManagedExpression() uses CreateValue(), while CreateParameterFromManagedExpression() and CreateReturnValueFromManagedExpression() uses both CreateArgumentState() and DestroyArgumentState() to marshal values. The purpose of using expression trees is to permit optimizations: Java.Interop.ExportedMemberBuilder uses expression trees in order to generate the Java > Managed marshaling code, so by extending JniValueMarshaler to use expression trees it's possible to use a smarter marshaling algorithm for specific types. For example, the builtin types don't need to do *anything*, as they're binary compatible with the JNI types. Conseequently, they only need to return the source parameter, instead of actually using JniValueManager.CreateValue()/etc. Similarly, the IJavaPeerable value marshaler can directly use JniRuntime.JniValueManager.GetValue<T>() for Java > managed marshaling and IJavaPeerable.PeerReference for Managed > Java marshaling. Finally, this doesn't *fully* fix Issue dotnet#8, but it fixes "enough" to close the issue. What's missing, but can be done later, is: > Do in conjunction w/ Java.Interop.Dynamic While Java.Interop.Dynamic can (should!) use JniValueManager.CreateParameterFromManagedExpression(), it doesn't currently because it doesn't even perform the JNI invocation via expression trees; it uses expression trees to invoke the intermediate helper method JavaClassInfo.TryInvokeMember(). This can be fixed, but doesn't need to be done yet. > Need to permit use of custom attributes on return types, parameter > types, "inline" into marshal methods. This will be done in a separate commit for a JniValueManagerAttribute custom attribute. (Let's try to keep commits single purpose, right?) > Use Expression<TDelegate> to make it easier to write marshalers. This isn't needed for the currently supported marshalers.
1 parent 5ef4b66 commit ff4053c

File tree

9 files changed

+519
-166
lines changed

9 files changed

+519
-166
lines changed

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

Lines changed: 41 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using System.Reflection;
77
using System.Text;
88

9+
using Java.Interop.Expressions;
10+
911
namespace Java.Interop {
1012

1113
public class ExportedMemberBuilder : JniRuntime.JniExportedMemberBuilder
@@ -108,89 +110,73 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (JavaCallab
108110
CheckMarshalTypesMatch (method, export.Signature, methodParameters);
109111

110112
var jnienv = Expression.Parameter (typeof (IntPtr), "__jnienv");
111-
var context = Expression.Parameter (typeof (IntPtr), "__context");
113+
var context = Expression.Parameter (typeof (IntPtr), method.IsStatic ? "__class" : "__this");
112114

113115
var envp = Expression.Variable (typeof (JniTransition), "__envp");
116+
var jvm = Expression.Variable (typeof (JniRuntime), "__jvm");
114117
var envpVars = new List<ParameterExpression> () {
115118
envp,
119+
jvm,
116120
};
117121

118122
var envpBody = new List<Expression> () {
119123
Expression.Assign (envp, CreateJniTransition (jnienv)),
120124
};
121125

122-
var jvm = Expression.Variable (typeof (JniRuntime), "__jvm");
123-
var variables = new List<ParameterExpression> () {
124-
jvm,
125-
};
126-
127126
var marshalBody = new List<Expression> () {
128127
Expression.Assign (jvm, GetRuntime ()),
129128
};
130129

131-
ParameterExpression self = null;
130+
Expression self = null;
131+
var marshalerContext = new JniValueMarshalerContext (jvm);
132132
if (!method.IsStatic) {
133-
self = Expression.Variable (type, "__this");
134-
variables.Add (self);
135-
marshalBody.Add (Expression.Assign (self, GetThis (jvm, type, context)));
133+
var selfMarshaler = Runtime.ValueManager.GetValueMarshaler (type);
134+
self = selfMarshaler.CreateParameterToManagedExpression (marshalerContext, context, 0, type);
136135
}
137136

138137
var marshalParameters = new List<ParameterExpression> (methodParameters.Length);
139-
var invokeParameters = new List<ParameterExpression> (methodParameters.Length);
138+
var invokeParameters = new List<Expression> (methodParameters.Length);
140139
for (int i = 0; i < methodParameters.Length; ++i) {
141-
var jni = GetMarshalFromJniParameterType (methodParameters [i].ParameterType);
142-
if (jni == methodParameters [i].ParameterType) {
143-
var p = Expression.Parameter (jni, methodParameters [i].Name);
144-
marshalParameters.Add (p);
145-
invokeParameters.Add (p);
146-
}
147-
else {
148-
var np = Expression.Parameter (jni, "native_" + methodParameters [i].Name);
149-
var p = Expression.Variable (methodParameters [i].ParameterType, methodParameters [i].Name);
150-
var fromJni = GetMarshalFromJniExpression (jvm, p.Type, np);
151-
if (fromJni == null)
152-
throw new NotSupportedException (string.Format ("Cannot convert from '{0}' to '{1}'.", jni, methodParameters [i].ParameterType));
153-
variables.Add (p);
154-
marshalParameters.Add (np);
155-
invokeParameters.Add (p);
156-
marshalBody.Add (Expression.Assign (p, fromJni));
157-
}
140+
var marshaler = Runtime.ValueManager.GetValueMarshaler (methodParameters [i].ParameterType);
141+
var np = Expression.Parameter (marshaler.MarshalType, methodParameters [i].Name);
142+
var p = marshaler.CreateParameterToManagedExpression (marshalerContext, np, methodParameters [i].Attributes, methodParameters [i].ParameterType);
143+
marshalParameters.Add (np);
144+
invokeParameters.Add (p);
158145
}
159146

147+
marshalBody.AddRange (marshalerContext.CreationStatements);
148+
160149
Expression invoke = method.IsStatic
161150
? Expression.Call (method, invokeParameters)
162151
: Expression.Call (self, method, invokeParameters);
163-
ParameterExpression ret = null;
152+
Expression ret = null;
164153
if (method.ReturnType == typeof (void)) {
154+
envpVars.AddRange (marshalerContext.LocalVariables);
155+
165156
marshalBody.Add (invoke);
166157
envpBody.Add (
167158
Expression.TryCatchFinally (
168-
Expression.Block (variables, marshalBody),
169-
CreateDisposeJniEnvironment (envp),
159+
Expression.Block (marshalBody),
160+
CreateDisposeJniEnvironment (envp, marshalerContext.CleanupStatements),
170161
CreateMarshalException (envp, null)));
171162
} else {
172-
var jniRType = GetMarshalToJniReturnType (method.ReturnType);
163+
var rmarshaler = Runtime.ValueManager.GetValueMarshaler (method.ReturnType);
164+
var jniRType = rmarshaler.MarshalType;
173165
var exit = Expression.Label (jniRType, "__exit");
174-
ret = Expression.Variable (jniRType, "__jret");
175166
var mret = Expression.Variable (method.ReturnType, "__mret");
176-
envpVars.Add (ret);
177-
variables.Add (mret);
167+
envpVars.Add (mret);
178168
marshalBody.Add (Expression.Assign (mret, invoke));
179-
if (jniRType == method.ReturnType)
180-
marshalBody.Add (Expression.Assign (ret, mret));
181-
else {
182-
var marshalExpr = GetMarshalToJniExpression (method.ReturnType, mret);
183-
if (marshalExpr == null)
184-
throw new NotSupportedException (string.Format ("Don't know how to marshal '{0}' to '{1}'.",
185-
method.ReturnType, jniRType));
186-
marshalBody.Add (Expression.Assign (ret, marshalExpr));
187-
}
169+
marshalerContext.CreationStatements.Clear ();
170+
ret = rmarshaler.CreateReturnValueFromManagedExpression (marshalerContext, mret);
171+
marshalBody.AddRange (marshalerContext.CreationStatements);
188172
marshalBody.Add (Expression.Return (exit, ret));
189173

174+
envpVars.AddRange (marshalerContext.LocalVariables);
175+
190176
envpBody.Add (
191177
Expression.TryCatchFinally (
192-
Expression.Block (variables, marshalBody),
193-
CreateDisposeJniEnvironment (envp),
178+
Expression.Block (marshalBody),
179+
CreateDisposeJniEnvironment (envp, marshalerContext.CleanupStatements),
194180
CreateMarshalException (envp, exit)));
195181

196182
envpBody.Add (Expression.Label (exit, Expression.Default (jniRType)));
@@ -222,7 +208,8 @@ void CheckMarshalTypesMatch (MethodInfo method, string signature, ParameterInfo[
222208
var mptypes = JniSignature.GetMarshalParameterTypes (signature).ToList ();
223209
int len = Math.Min (methodParameters.Length, mptypes.Count);
224210
for (int i = 0; i < len; ++i) {
225-
var jni = GetMarshalFromJniParameterType (methodParameters [i].ParameterType);
211+
var vm = Runtime.ValueManager.GetValueMarshaler (methodParameters [i].ParameterType);
212+
var jni = vm.MarshalType;
226213
if (mptypes [i] != jni)
227214
throw new ArgumentException (
228215
string.Format ("JNI parameter type mismatch. Type '{0}' != '{1}.", jni, mptypes [i]),
@@ -236,63 +223,14 @@ void CheckMarshalTypesMatch (MethodInfo method, string signature, ParameterInfo[
236223
"signature");
237224

238225
var jrinfo = JniSignature.GetMarshalReturnType (signature);
239-
var mrinfo = GetMarshalToJniReturnType (method.ReturnType);
226+
var mrvm = Runtime.ValueManager.GetValueMarshaler (method.ReturnType);
227+
var mrinfo = mrvm.MarshalType;
240228
if (mrinfo != jrinfo)
241229
throw new ArgumentException (
242-
string.Format ("JNI return type mismatch. Type '{0}' != '{1}.", jrinfo, mrinfo),
230+
string.Format ("JNI return type mismatch. Type '{0}' != '{1}'.", jrinfo, mrinfo),
243231
"signature");
244232
}
245233

246-
protected virtual Type GetMarshalFromJniParameterType (Type type)
247-
{
248-
if (JniBuiltinTypes.Contains (type))
249-
return type;
250-
return typeof (IntPtr);
251-
}
252-
253-
protected virtual Type GetMarshalToJniReturnType (Type type)
254-
{
255-
if (JniBuiltinTypes.Contains (type))
256-
return type;
257-
return typeof (IntPtr);
258-
}
259-
260-
protected virtual Expression GetMarshalFromJniExpression (Expression jvm, Type targetType, Expression jniParameter)
261-
{
262-
MarshalInfo v;
263-
if (Marshalers.TryGetValue (targetType, out v))
264-
return v.FromJni (jvm, targetType, jniParameter);
265-
if (typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ()))
266-
return Marshalers [typeof (IJavaPeerable)].FromJni (jvm, targetType, jniParameter);
267-
return null;
268-
}
269-
270-
protected virtual Expression GetMarshalToJniExpression (Type sourceType, Expression managedParameter)
271-
{
272-
MarshalInfo v;
273-
if (Marshalers.TryGetValue (sourceType, out v))
274-
return v.ToJni (managedParameter);
275-
if (typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (sourceType.GetTypeInfo ()))
276-
return Marshalers [typeof (IJavaPeerable)].ToJni (managedParameter);
277-
return null;
278-
}
279-
280-
static readonly Dictionary<Type, MarshalInfo> Marshalers = new Dictionary<Type, MarshalInfo> () {
281-
{ typeof (string), new MarshalInfo {
282-
FromJni = (vm, t, p) => Expression.Call (F<IntPtr, string> (JniEnvironment.Strings.ToString).GetMethodInfo (), p),
283-
ToJni = p => Expression.Call (F<string, JniObjectReference> (JniEnvironment.Strings.NewString).GetMethodInfo (), p)
284-
} },
285-
{ typeof (IJavaPeerable), new MarshalInfo {
286-
FromJni = (vm, t, p) => GetThis (vm, t, p),
287-
ToJni = p => Expression.Call (F<IJavaPeerable, IntPtr> (JniEnvironment.References.NewReturnToJniRef).GetMethodInfo (), p)
288-
} },
289-
};
290-
291-
static Func<T, TRet> F<T, TRet> (Func<T, TRet> func)
292-
{
293-
return func;
294-
}
295-
296234
static Expression CreateJniTransition (ParameterExpression jnienv)
297235
{
298236
var ctor =
@@ -319,18 +257,11 @@ static CatchBlock CreateMarshalException (ParameterExpression envp, LabelTarget
319257
return Expression.Catch (ex, Expression.Block (body));
320258
}
321259

322-
static Expression CreateDisposeJniEnvironment (ParameterExpression envp)
260+
static Expression CreateDisposeJniEnvironment (ParameterExpression envp, IList<Expression> cleanup)
323261
{
324-
return Expression.Call (envp, typeof (JniTransition).GetTypeInfo ().GetDeclaredMethod ("Dispose"));
325-
}
326-
327-
static Expression GetThis (Expression vm, Type targetType, Expression context)
328-
{
329-
return Expression.Call (
330-
Expression.Property (vm, "ValueManager"),
331-
"GetValue",
332-
new[]{targetType},
333-
context);
262+
var disposeTransition = Expression.Call (envp, typeof(JniTransition).GetTypeInfo ().GetDeclaredMethod ("Dispose"));
263+
return Expression.Block (
264+
cleanup.Reverse ().Concat (new[]{ disposeTransition }));;
334265
}
335266

336267
static Expression GetRuntime ()
@@ -339,26 +270,6 @@ static Expression GetRuntime ()
339270
var runtime = Expression.Property (null, env, "Runtime");
340271
return runtime;
341272
}
342-
343-
static readonly ISet<Type> JniBuiltinTypes = new HashSet<Type> {
344-
typeof (IntPtr),
345-
typeof (void),
346-
typeof (bool),
347-
typeof (sbyte),
348-
typeof (char),
349-
typeof (short),
350-
typeof (int),
351-
typeof (long),
352-
typeof (float),
353-
typeof (double),
354-
};
355-
356-
}
357-
358-
class MarshalInfo {
359-
360-
public Func<Expression /* vm */, Type /* targetType */, Expression /* value */, Expression /* managed rep */> FromJni;
361-
public Func<Expression /* managed rep */, Expression /* jni rep */> ToJni;
362273
}
363274

364275
static class JniSignature {

0 commit comments

Comments
 (0)