Skip to content

Commit

Permalink
[Java.Interop.Export] Use JniEnvironment in marshal methods.
Browse files Browse the repository at this point in the history
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".
  • Loading branch information
jonpryor committed Jan 9, 2015
1 parent 53626b7 commit 06cfd83
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 33 deletions.
53 changes: 49 additions & 4 deletions src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (ExportAttr
var jnienv = Expression.Parameter (typeof (IntPtr), "__jnienv");
var context = Expression.Parameter (typeof (IntPtr), "__context");

var envp = Expression.Variable (typeof (JniEnvironment), "__envp");
var envpVars = new List<ParameterExpression> () {
envp,
};

var envpBody = new List<Expression> () {
Expression.Assign (envp, CreateJniEnvironment (jnienv)),
};

var jvm = Expression.Variable (typeof (JavaVM), "__jvm");
var variables = new List<ParameterExpression> () {
jvm,
Expand Down Expand Up @@ -152,12 +161,17 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (ExportAttr
ParameterExpression ret = null;
if (method.ReturnType == typeof (void)) {
marshalBody.Add (invoke);
envpBody.Add (
Expression.TryCatchFinally (
Expression.Block (variables, marshalBody),
CreateDisposeJniEnvironment (envp),
CreateMarshalException (envp, null)));
} else {
var jniRType = GetMarshalToJniReturnType (method.ReturnType);
var exit = Expression.Label (jniRType, "__exit");
ret = Expression.Variable (jniRType, "__jret");
var mret = Expression.Variable (method.ReturnType, "__mret");
variables.Add (ret);
envpVars.Add (ret);
variables.Add (mret);
marshalBody.Add (Expression.Assign (mret, invoke));
if (jniRType == method.ReturnType)
Expand All @@ -170,9 +184,15 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (ExportAttr
marshalBody.Add (Expression.Assign (ret, marshalExpr));
}
marshalBody.Add (Expression.Return (exit, ret));
marshalBody.Add (Expression.Label (exit, ret));
}

envpBody.Add (
Expression.TryCatchFinally (
Expression.Block (variables, marshalBody),
CreateDisposeJniEnvironment (envp),
CreateMarshalException (envp, exit)));

envpBody.Add (Expression.Label (exit, Expression.Default (jniRType)));
}

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

var bodyParams = new List<ParameterExpression> { jnienv, context };
bodyParams.AddRange (marshalParameters);
var body = Expression.Block (variables, marshalBody);
var body = Expression.Block (envpVars, envpBody);
return Expression.Lambda (marshalerType, body, bodyParams);
}

Expand Down Expand Up @@ -271,6 +291,31 @@ static Func<T, TRet> F<T, TRet> (Func<T, TRet> func)
return func;
}

static Expression CreateJniEnvironment (ParameterExpression jnienv)
{
return Expression.New (
typeof (JniEnvironment).GetConstructor (new []{typeof (IntPtr)}),
jnienv);
}

static CatchBlock CreateMarshalException (ParameterExpression envp, LabelTarget exit)
{
var spe = typeof (JniEnvironment).GetMethod ("SetPendingException");
var ex = Expression.Variable (typeof (Exception), "__e");
var body = new List<Expression> () {
Expression.Call (envp, spe, ex),
};
if (exit != null) {
body.Add (Expression.Return (exit, Expression.Default (exit.Type)));
}
return Expression.Catch (ex, Expression.Block (body));
}

static Expression CreateDisposeJniEnvironment (ParameterExpression envp)
{
return Expression.Call (envp, typeof (JniEnvironment).GetMethod ("Dispose"));
}

static Expression GetThis (Expression vm, Type targetType, Expression context)
{
return Expression.Call (
Expand Down
130 changes: 101 additions & 29 deletions src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,26 @@ public void CreateMarshalFromJniMethodExpression_InstanceAction ()
CheckCreateInvocationExpression (null, t, m, typeof (Action<IntPtr, IntPtr>),
@"void (IntPtr __jnienv, IntPtr __context)
{
JavaVM __jvm;
ExportTest __this;
JniEnvironment __envp;
__jvm = JniEnvironment.Current.JavaVM;
__this = __jvm.GetObject<ExportTest>(__context);
__this.InstanceAction();
__envp = new JniEnvironment(__jnienv);
try
{
JavaVM __jvm;
ExportTest __this;
__jvm = JniEnvironment.Current.JavaVM;
__this = __jvm.GetObject<ExportTest>(__context);
__this.InstanceAction();
}
catch (Exception __e)
{
__envp.SetPendingException(__e);
}
finally
{
__envp.Dispose();
}
}");
}

Expand Down Expand Up @@ -209,10 +223,24 @@ public void CreateMarshalFromJniMethodExpression_StaticAction ()
CheckCreateInvocationExpression (null, t, a.Method, typeof(Action<IntPtr, IntPtr>),
@"void (IntPtr __jnienv, IntPtr __context)
{
JavaVM __jvm;
JniEnvironment __envp;
__envp = new JniEnvironment(__jnienv);
try
{
JavaVM __jvm;
__jvm = JniEnvironment.Current.JavaVM;
ExportTest.StaticAction();
__jvm = JniEnvironment.Current.JavaVM;
ExportTest.StaticAction();
}
catch (Exception __e)
{
__envp.SetPendingException(__e);
}
finally
{
__envp.Dispose();
}
}");
}

Expand All @@ -227,12 +255,26 @@ public void CreateMarshalFromJniMethodExpression_StaticActionInt32String ()
CheckCreateInvocationExpression (e, t, m.Method, typeof (Action<IntPtr, IntPtr, int, IntPtr>),
@"void (IntPtr __jnienv, IntPtr __context, int i, IntPtr native_v)
{
JavaVM __jvm;
string v;
JniEnvironment __envp;
__envp = new JniEnvironment(__jnienv);
try
{
JavaVM __jvm;
string v;
__jvm = JniEnvironment.Current.JavaVM;
v = Strings.ToString(native_v);
ExportTest.StaticActionInt32String(i, v);
__jvm = JniEnvironment.Current.JavaVM;
v = Strings.ToString(native_v);
ExportTest.StaticActionInt32String(i, v);
}
catch (Exception __e)
{
__envp.SetPendingException(__e);
}
finally
{
__envp.Dispose();
}
}");
}

Expand All @@ -247,16 +289,31 @@ public void CreateMarshalFromJniMethodExpression_FuncInt64 ()
CheckCreateInvocationExpression (e, t, m, typeof (Func<IntPtr, IntPtr, long>),
@"long (IntPtr __jnienv, IntPtr __context)
{
JavaVM __jvm;
ExportTest __this;
JniEnvironment __envp;
long __jret;
long __mret;
__jvm = JniEnvironment.Current.JavaVM;
__this = __jvm.GetObject<ExportTest>(__context);
__mret = __this.FuncInt64();
__jret = __mret;
return __jret;
__envp = new JniEnvironment(__jnienv);
try
{
JavaVM __jvm;
ExportTest __this;
long __mret;
__jvm = JniEnvironment.Current.JavaVM;
__this = __jvm.GetObject<ExportTest>(__context);
__mret = __this.FuncInt64();
__jret = __mret;
return __jret;
}
catch (Exception __e)
{
__envp.SetPendingException(__e);
return default(long);
}
finally
{
__envp.Dispose();
}
}");
}

Expand All @@ -271,16 +328,31 @@ public void CreateMarshalFromJniMethodExpression_FuncIJavaObject ()
CheckCreateInvocationExpression (e, t, m, typeof (Func<IntPtr, IntPtr, IntPtr>),
@"IntPtr (IntPtr __jnienv, IntPtr __context)
{
JavaVM __jvm;
ExportTest __this;
JniEnvironment __envp;
IntPtr __jret;
JavaObject __mret;
__jvm = JniEnvironment.Current.JavaVM;
__this = __jvm.GetObject<ExportTest>(__context);
__mret = __this.FuncIJavaObject();
__jret = Handles.NewReturnToJniRef(__mret);
return __jret;
__envp = new JniEnvironment(__jnienv);
try
{
JavaVM __jvm;
ExportTest __this;
JavaObject __mret;
__jvm = JniEnvironment.Current.JavaVM;
__this = __jvm.GetObject<ExportTest>(__context);
__mret = __this.FuncIJavaObject();
__jret = Handles.NewReturnToJniRef(__mret);
return __jret;
}
catch (Exception __e)
{
__envp.SetPendingException(__e);
return default(IntPtr);
}
finally
{
__envp.Dispose();
}
}");
}
}
Expand Down

0 comments on commit 06cfd83

Please sign in to comment.