Skip to content

Commit 06cfd83

Browse files
committed
[Java.Interop.Export] Use JniEnvironment in marshal methods.
As noted in commit 2f9fece, Java.Interop.Export.ExportedMemberBuilder doesn't need to use JniEnvironment within it's generated code, as JniType.RegisterNativeMethods() automagically wraps delegates with the required JniEnvironment usage code. Thus, ExportedMemberBuilder doesn't *need* to emit JniEnvironment use RIGHT NOW. The question is rather regarding the future: one of the performance issues that needs investigation on Xamarin.Android is to improve support for AOT, so that startup overhead can be reduced/minimized by pre-JITing as much as possible via AOT. The problem with providing AOT support is that Xamarin.Android -- just like Java.Interop (unsurprisingly) -- ALSO automagically wraps all methods registered with the JVM with System.Reflection.Emit-generated code at runtime. It is thus not possible to AOT *everything*. *Lots* of things can be AOT'd, just not everything, and this is something that we would like to address, meaning all required sources of runtime code generation need to be removable. With that in mind, long-term we don't want ExportedMemberBuilder to rely on JniType.RegisterNativeMethods() behavior. (In fact, we'd want to REMOVE the currently required wrapping behavior from JniType.RegisterNativeMethods()!) Instead, long-term it would be "interesting" if we could use ExportedMemberBuilder to process assemblies, generate the JNI method marshaling code for ~everything, store that into a NEW assembly (as part of the build process), and AOT the marshaling assembly! How cool would that be? :-D But to even get there, we need the marshal code to be self-contained, and not rely upon runtime code generation semantics from "elsewhere".
1 parent 53626b7 commit 06cfd83

File tree

2 files changed

+150
-33
lines changed

2 files changed

+150
-33
lines changed

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

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (ExportAttr
108108
var jnienv = Expression.Parameter (typeof (IntPtr), "__jnienv");
109109
var context = Expression.Parameter (typeof (IntPtr), "__context");
110110

111+
var envp = Expression.Variable (typeof (JniEnvironment), "__envp");
112+
var envpVars = new List<ParameterExpression> () {
113+
envp,
114+
};
115+
116+
var envpBody = new List<Expression> () {
117+
Expression.Assign (envp, CreateJniEnvironment (jnienv)),
118+
};
119+
111120
var jvm = Expression.Variable (typeof (JavaVM), "__jvm");
112121
var variables = new List<ParameterExpression> () {
113122
jvm,
@@ -152,12 +161,17 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (ExportAttr
152161
ParameterExpression ret = null;
153162
if (method.ReturnType == typeof (void)) {
154163
marshalBody.Add (invoke);
164+
envpBody.Add (
165+
Expression.TryCatchFinally (
166+
Expression.Block (variables, marshalBody),
167+
CreateDisposeJniEnvironment (envp),
168+
CreateMarshalException (envp, null)));
155169
} else {
156170
var jniRType = GetMarshalToJniReturnType (method.ReturnType);
157171
var exit = Expression.Label (jniRType, "__exit");
158172
ret = Expression.Variable (jniRType, "__jret");
159173
var mret = Expression.Variable (method.ReturnType, "__mret");
160-
variables.Add (ret);
174+
envpVars.Add (ret);
161175
variables.Add (mret);
162176
marshalBody.Add (Expression.Assign (mret, invoke));
163177
if (jniRType == method.ReturnType)
@@ -170,9 +184,15 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (ExportAttr
170184
marshalBody.Add (Expression.Assign (ret, marshalExpr));
171185
}
172186
marshalBody.Add (Expression.Return (exit, ret));
173-
marshalBody.Add (Expression.Label (exit, ret));
174-
}
175187

188+
envpBody.Add (
189+
Expression.TryCatchFinally (
190+
Expression.Block (variables, marshalBody),
191+
CreateDisposeJniEnvironment (envp),
192+
CreateMarshalException (envp, exit)));
193+
194+
envpBody.Add (Expression.Label (exit, Expression.Default (jniRType)));
195+
}
176196

177197
var funcTypeParams = new List<Type> () {
178198
typeof (IntPtr),
@@ -188,7 +208,7 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (ExportAttr
188208

189209
var bodyParams = new List<ParameterExpression> { jnienv, context };
190210
bodyParams.AddRange (marshalParameters);
191-
var body = Expression.Block (variables, marshalBody);
211+
var body = Expression.Block (envpVars, envpBody);
192212
return Expression.Lambda (marshalerType, body, bodyParams);
193213
}
194214

@@ -271,6 +291,31 @@ static Func<T, TRet> F<T, TRet> (Func<T, TRet> func)
271291
return func;
272292
}
273293

294+
static Expression CreateJniEnvironment (ParameterExpression jnienv)
295+
{
296+
return Expression.New (
297+
typeof (JniEnvironment).GetConstructor (new []{typeof (IntPtr)}),
298+
jnienv);
299+
}
300+
301+
static CatchBlock CreateMarshalException (ParameterExpression envp, LabelTarget exit)
302+
{
303+
var spe = typeof (JniEnvironment).GetMethod ("SetPendingException");
304+
var ex = Expression.Variable (typeof (Exception), "__e");
305+
var body = new List<Expression> () {
306+
Expression.Call (envp, spe, ex),
307+
};
308+
if (exit != null) {
309+
body.Add (Expression.Return (exit, Expression.Default (exit.Type)));
310+
}
311+
return Expression.Catch (ex, Expression.Block (body));
312+
}
313+
314+
static Expression CreateDisposeJniEnvironment (ParameterExpression envp)
315+
{
316+
return Expression.Call (envp, typeof (JniEnvironment).GetMethod ("Dispose"));
317+
}
318+
274319
static Expression GetThis (Expression vm, Type targetType, Expression context)
275320
{
276321
return Expression.Call (

src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs

Lines changed: 101 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,26 @@ public void CreateMarshalFromJniMethodExpression_InstanceAction ()
167167
CheckCreateInvocationExpression (null, t, m, typeof (Action<IntPtr, IntPtr>),
168168
@"void (IntPtr __jnienv, IntPtr __context)
169169
{
170-
JavaVM __jvm;
171-
ExportTest __this;
170+
JniEnvironment __envp;
172171
173-
__jvm = JniEnvironment.Current.JavaVM;
174-
__this = __jvm.GetObject<ExportTest>(__context);
175-
__this.InstanceAction();
172+
__envp = new JniEnvironment(__jnienv);
173+
try
174+
{
175+
JavaVM __jvm;
176+
ExportTest __this;
177+
178+
__jvm = JniEnvironment.Current.JavaVM;
179+
__this = __jvm.GetObject<ExportTest>(__context);
180+
__this.InstanceAction();
181+
}
182+
catch (Exception __e)
183+
{
184+
__envp.SetPendingException(__e);
185+
}
186+
finally
187+
{
188+
__envp.Dispose();
189+
}
176190
}");
177191
}
178192

@@ -209,10 +223,24 @@ public void CreateMarshalFromJniMethodExpression_StaticAction ()
209223
CheckCreateInvocationExpression (null, t, a.Method, typeof(Action<IntPtr, IntPtr>),
210224
@"void (IntPtr __jnienv, IntPtr __context)
211225
{
212-
JavaVM __jvm;
226+
JniEnvironment __envp;
227+
228+
__envp = new JniEnvironment(__jnienv);
229+
try
230+
{
231+
JavaVM __jvm;
213232
214-
__jvm = JniEnvironment.Current.JavaVM;
215-
ExportTest.StaticAction();
233+
__jvm = JniEnvironment.Current.JavaVM;
234+
ExportTest.StaticAction();
235+
}
236+
catch (Exception __e)
237+
{
238+
__envp.SetPendingException(__e);
239+
}
240+
finally
241+
{
242+
__envp.Dispose();
243+
}
216244
}");
217245
}
218246

@@ -227,12 +255,26 @@ public void CreateMarshalFromJniMethodExpression_StaticActionInt32String ()
227255
CheckCreateInvocationExpression (e, t, m.Method, typeof (Action<IntPtr, IntPtr, int, IntPtr>),
228256
@"void (IntPtr __jnienv, IntPtr __context, int i, IntPtr native_v)
229257
{
230-
JavaVM __jvm;
231-
string v;
258+
JniEnvironment __envp;
259+
260+
__envp = new JniEnvironment(__jnienv);
261+
try
262+
{
263+
JavaVM __jvm;
264+
string v;
232265
233-
__jvm = JniEnvironment.Current.JavaVM;
234-
v = Strings.ToString(native_v);
235-
ExportTest.StaticActionInt32String(i, v);
266+
__jvm = JniEnvironment.Current.JavaVM;
267+
v = Strings.ToString(native_v);
268+
ExportTest.StaticActionInt32String(i, v);
269+
}
270+
catch (Exception __e)
271+
{
272+
__envp.SetPendingException(__e);
273+
}
274+
finally
275+
{
276+
__envp.Dispose();
277+
}
236278
}");
237279
}
238280

@@ -247,16 +289,31 @@ public void CreateMarshalFromJniMethodExpression_FuncInt64 ()
247289
CheckCreateInvocationExpression (e, t, m, typeof (Func<IntPtr, IntPtr, long>),
248290
@"long (IntPtr __jnienv, IntPtr __context)
249291
{
250-
JavaVM __jvm;
251-
ExportTest __this;
292+
JniEnvironment __envp;
252293
long __jret;
253-
long __mret;
254294
255-
__jvm = JniEnvironment.Current.JavaVM;
256-
__this = __jvm.GetObject<ExportTest>(__context);
257-
__mret = __this.FuncInt64();
258-
__jret = __mret;
259-
return __jret;
295+
__envp = new JniEnvironment(__jnienv);
296+
try
297+
{
298+
JavaVM __jvm;
299+
ExportTest __this;
300+
long __mret;
301+
302+
__jvm = JniEnvironment.Current.JavaVM;
303+
__this = __jvm.GetObject<ExportTest>(__context);
304+
__mret = __this.FuncInt64();
305+
__jret = __mret;
306+
return __jret;
307+
}
308+
catch (Exception __e)
309+
{
310+
__envp.SetPendingException(__e);
311+
return default(long);
312+
}
313+
finally
314+
{
315+
__envp.Dispose();
316+
}
260317
}");
261318
}
262319

@@ -271,16 +328,31 @@ public void CreateMarshalFromJniMethodExpression_FuncIJavaObject ()
271328
CheckCreateInvocationExpression (e, t, m, typeof (Func<IntPtr, IntPtr, IntPtr>),
272329
@"IntPtr (IntPtr __jnienv, IntPtr __context)
273330
{
274-
JavaVM __jvm;
275-
ExportTest __this;
331+
JniEnvironment __envp;
276332
IntPtr __jret;
277-
JavaObject __mret;
278333
279-
__jvm = JniEnvironment.Current.JavaVM;
280-
__this = __jvm.GetObject<ExportTest>(__context);
281-
__mret = __this.FuncIJavaObject();
282-
__jret = Handles.NewReturnToJniRef(__mret);
283-
return __jret;
334+
__envp = new JniEnvironment(__jnienv);
335+
try
336+
{
337+
JavaVM __jvm;
338+
ExportTest __this;
339+
JavaObject __mret;
340+
341+
__jvm = JniEnvironment.Current.JavaVM;
342+
__this = __jvm.GetObject<ExportTest>(__context);
343+
__mret = __this.FuncIJavaObject();
344+
__jret = Handles.NewReturnToJniRef(__mret);
345+
return __jret;
346+
}
347+
catch (Exception __e)
348+
{
349+
__envp.SetPendingException(__e);
350+
return default(IntPtr);
351+
}
352+
finally
353+
{
354+
__envp.Dispose();
355+
}
284356
}");
285357
}
286358
}

0 commit comments

Comments
 (0)