diff --git a/lib/mono.linq.expressions b/lib/mono.linq.expressions index 3bfd63df3..32626c666 160000 --- a/lib/mono.linq.expressions +++ b/lib/mono.linq.expressions @@ -1 +1 @@ -Subproject commit 3bfd63df39154b5b3ead117d6b769bfc885ae9df +Subproject commit 32626c6665fe88f4543956195a8014d8dff314b1 diff --git a/src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs b/src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs index 9b55f75bd..3ef966bcd 100644 --- a/src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs +++ b/src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs @@ -6,6 +6,8 @@ using System.Reflection; using System.Text; +using Java.Interop.Expressions; + namespace Java.Interop { public class ExportedMemberBuilder : JniRuntime.JniExportedMemberBuilder @@ -108,89 +110,73 @@ public virtual LambdaExpression CreateMarshalFromJniMethodExpression (JavaCallab CheckMarshalTypesMatch (method, export.Signature, methodParameters); var jnienv = Expression.Parameter (typeof (IntPtr), "__jnienv"); - var context = Expression.Parameter (typeof (IntPtr), "__context"); + var context = Expression.Parameter (typeof (IntPtr), method.IsStatic ? "__class" : "__this"); var envp = Expression.Variable (typeof (JniTransition), "__envp"); + var jvm = Expression.Variable (typeof (JniRuntime), "__jvm"); var envpVars = new List () { envp, + jvm, }; var envpBody = new List () { Expression.Assign (envp, CreateJniTransition (jnienv)), }; - var jvm = Expression.Variable (typeof (JniRuntime), "__jvm"); - var variables = new List () { - jvm, - }; - var marshalBody = new List () { Expression.Assign (jvm, GetRuntime ()), }; - ParameterExpression self = null; + Expression self = null; + var marshalerContext = new JniValueMarshalerContext (jvm); if (!method.IsStatic) { - self = Expression.Variable (type, "__this"); - variables.Add (self); - marshalBody.Add (Expression.Assign (self, GetThis (jvm, type, context))); + var selfMarshaler = Runtime.ValueManager.GetValueMarshaler (type); + self = selfMarshaler.CreateParameterToManagedExpression (marshalerContext, context, 0, type); } var marshalParameters = new List (methodParameters.Length); - var invokeParameters = new List (methodParameters.Length); + var invokeParameters = new List (methodParameters.Length); for (int i = 0; i < methodParameters.Length; ++i) { - var jni = GetMarshalFromJniParameterType (methodParameters [i].ParameterType); - if (jni == methodParameters [i].ParameterType) { - var p = Expression.Parameter (jni, methodParameters [i].Name); - marshalParameters.Add (p); - invokeParameters.Add (p); - } - else { - var np = Expression.Parameter (jni, "native_" + methodParameters [i].Name); - var p = Expression.Variable (methodParameters [i].ParameterType, methodParameters [i].Name); - var fromJni = GetMarshalFromJniExpression (jvm, p.Type, np); - if (fromJni == null) - throw new NotSupportedException (string.Format ("Cannot convert from '{0}' to '{1}'.", jni, methodParameters [i].ParameterType)); - variables.Add (p); - marshalParameters.Add (np); - invokeParameters.Add (p); - marshalBody.Add (Expression.Assign (p, fromJni)); - } + var marshaler = Runtime.ValueManager.GetValueMarshaler (methodParameters [i].ParameterType); + var np = Expression.Parameter (marshaler.MarshalType, methodParameters [i].Name); + var p = marshaler.CreateParameterToManagedExpression (marshalerContext, np, methodParameters [i].Attributes, methodParameters [i].ParameterType); + marshalParameters.Add (np); + invokeParameters.Add (p); } + marshalBody.AddRange (marshalerContext.CreationStatements); + Expression invoke = method.IsStatic ? Expression.Call (method, invokeParameters) : Expression.Call (self, method, invokeParameters); - ParameterExpression ret = null; + Expression ret = null; if (method.ReturnType == typeof (void)) { + envpVars.AddRange (marshalerContext.LocalVariables); + marshalBody.Add (invoke); envpBody.Add ( Expression.TryCatchFinally ( - Expression.Block (variables, marshalBody), - CreateDisposeJniEnvironment (envp), + Expression.Block (marshalBody), + CreateDisposeJniEnvironment (envp, marshalerContext.CleanupStatements), CreateMarshalException (envp, null))); } else { - var jniRType = GetMarshalToJniReturnType (method.ReturnType); + var rmarshaler = Runtime.ValueManager.GetValueMarshaler (method.ReturnType); + var jniRType = rmarshaler.MarshalType; var exit = Expression.Label (jniRType, "__exit"); - ret = Expression.Variable (jniRType, "__jret"); var mret = Expression.Variable (method.ReturnType, "__mret"); - envpVars.Add (ret); - variables.Add (mret); + envpVars.Add (mret); marshalBody.Add (Expression.Assign (mret, invoke)); - if (jniRType == method.ReturnType) - marshalBody.Add (Expression.Assign (ret, mret)); - else { - var marshalExpr = GetMarshalToJniExpression (method.ReturnType, mret); - if (marshalExpr == null) - throw new NotSupportedException (string.Format ("Don't know how to marshal '{0}' to '{1}'.", - method.ReturnType, jniRType)); - marshalBody.Add (Expression.Assign (ret, marshalExpr)); - } + marshalerContext.CreationStatements.Clear (); + ret = rmarshaler.CreateReturnValueFromManagedExpression (marshalerContext, mret); + marshalBody.AddRange (marshalerContext.CreationStatements); marshalBody.Add (Expression.Return (exit, ret)); + envpVars.AddRange (marshalerContext.LocalVariables); + envpBody.Add ( Expression.TryCatchFinally ( - Expression.Block (variables, marshalBody), - CreateDisposeJniEnvironment (envp), + Expression.Block (marshalBody), + CreateDisposeJniEnvironment (envp, marshalerContext.CleanupStatements), CreateMarshalException (envp, exit))); envpBody.Add (Expression.Label (exit, Expression.Default (jniRType))); @@ -222,7 +208,8 @@ void CheckMarshalTypesMatch (MethodInfo method, string signature, ParameterInfo[ var mptypes = JniSignature.GetMarshalParameterTypes (signature).ToList (); int len = Math.Min (methodParameters.Length, mptypes.Count); for (int i = 0; i < len; ++i) { - var jni = GetMarshalFromJniParameterType (methodParameters [i].ParameterType); + var vm = Runtime.ValueManager.GetValueMarshaler (methodParameters [i].ParameterType); + var jni = vm.MarshalType; if (mptypes [i] != jni) throw new ArgumentException ( string.Format ("JNI parameter type mismatch. Type '{0}' != '{1}.", jni, mptypes [i]), @@ -236,63 +223,14 @@ void CheckMarshalTypesMatch (MethodInfo method, string signature, ParameterInfo[ "signature"); var jrinfo = JniSignature.GetMarshalReturnType (signature); - var mrinfo = GetMarshalToJniReturnType (method.ReturnType); + var mrvm = Runtime.ValueManager.GetValueMarshaler (method.ReturnType); + var mrinfo = mrvm.MarshalType; if (mrinfo != jrinfo) throw new ArgumentException ( - string.Format ("JNI return type mismatch. Type '{0}' != '{1}.", jrinfo, mrinfo), + string.Format ("JNI return type mismatch. Type '{0}' != '{1}'.", jrinfo, mrinfo), "signature"); } - protected virtual Type GetMarshalFromJniParameterType (Type type) - { - if (JniBuiltinTypes.Contains (type)) - return type; - return typeof (IntPtr); - } - - protected virtual Type GetMarshalToJniReturnType (Type type) - { - if (JniBuiltinTypes.Contains (type)) - return type; - return typeof (IntPtr); - } - - protected virtual Expression GetMarshalFromJniExpression (Expression jvm, Type targetType, Expression jniParameter) - { - MarshalInfo v; - if (Marshalers.TryGetValue (targetType, out v)) - return v.FromJni (jvm, targetType, jniParameter); - if (typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ())) - return Marshalers [typeof (IJavaPeerable)].FromJni (jvm, targetType, jniParameter); - return null; - } - - protected virtual Expression GetMarshalToJniExpression (Type sourceType, Expression managedParameter) - { - MarshalInfo v; - if (Marshalers.TryGetValue (sourceType, out v)) - return v.ToJni (managedParameter); - if (typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (sourceType.GetTypeInfo ())) - return Marshalers [typeof (IJavaPeerable)].ToJni (managedParameter); - return null; - } - - static readonly Dictionary Marshalers = new Dictionary () { - { typeof (string), new MarshalInfo { - FromJni = (vm, t, p) => Expression.Call (F (JniEnvironment.Strings.ToString).GetMethodInfo (), p), - ToJni = p => Expression.Call (F (JniEnvironment.Strings.NewString).GetMethodInfo (), p) - } }, - { typeof (IJavaPeerable), new MarshalInfo { - FromJni = (vm, t, p) => GetThis (vm, t, p), - ToJni = p => Expression.Call (F (JniEnvironment.References.NewReturnToJniRef).GetMethodInfo (), p) - } }, - }; - - static Func F (Func func) - { - return func; - } - static Expression CreateJniTransition (ParameterExpression jnienv) { var ctor = @@ -319,18 +257,11 @@ static CatchBlock CreateMarshalException (ParameterExpression envp, LabelTarget return Expression.Catch (ex, Expression.Block (body)); } - static Expression CreateDisposeJniEnvironment (ParameterExpression envp) + static Expression CreateDisposeJniEnvironment (ParameterExpression envp, IList cleanup) { - return Expression.Call (envp, typeof (JniTransition).GetTypeInfo ().GetDeclaredMethod ("Dispose")); - } - - static Expression GetThis (Expression vm, Type targetType, Expression context) - { - return Expression.Call ( - Expression.Property (vm, "ValueManager"), - "GetValue", - new[]{targetType}, - context); + var disposeTransition = Expression.Call (envp, typeof(JniTransition).GetTypeInfo ().GetDeclaredMethod ("Dispose")); + return Expression.Block ( + cleanup.Reverse ().Concat (new[]{ disposeTransition }));; } static Expression GetRuntime () @@ -339,26 +270,6 @@ static Expression GetRuntime () var runtime = Expression.Property (null, env, "Runtime"); return runtime; } - - static readonly ISet JniBuiltinTypes = new HashSet { - typeof (IntPtr), - typeof (void), - typeof (bool), - typeof (sbyte), - typeof (char), - typeof (short), - typeof (int), - typeof (long), - typeof (float), - typeof (double), - }; - - } - - class MarshalInfo { - - public Func FromJni; - public Func ToJni; } static class JniSignature { diff --git a/src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs b/src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs index 36ef4265a..c4f6bb016 100644 --- a/src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs +++ b/src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs @@ -167,19 +167,18 @@ public void CreateMarshalFromJniMethodExpression_InstanceAction () var t = typeof (ExportTest); var m = t.GetMethod ("InstanceAction"); CheckCreateInvocationExpression (null, t, m, typeof (Action), - @"void (IntPtr __jnienv, IntPtr __context) + @"void (IntPtr __jnienv, IntPtr __this) { JniTransition __envp; + JniRuntime __jvm; + ExportTest __this_val; __envp = new JniTransition(__jnienv); try { - JniRuntime __jvm; - ExportTest __this; - __jvm = JniEnvironment.Runtime; - __this = __jvm.ValueManager.GetValue(__context); - __this.InstanceAction(); + __this_val = __jvm.ValueManager.GetValue(__this); + __this_val.InstanceAction(); } catch (Exception __e) { @@ -225,15 +224,14 @@ public void CreateMarshalFromJniMethodExpression_StaticAction () var t = typeof (ExportTest); Action a = ExportTest.StaticAction; CheckCreateInvocationExpression (null, t, a.Method, typeof(Action), - @"void (IntPtr __jnienv, IntPtr __context) + @"void (IntPtr __jnienv, IntPtr __class) { JniTransition __envp; + JniRuntime __jvm; __envp = new JniTransition(__jnienv); try { - JniRuntime __jvm; - __jvm = JniEnvironment.Runtime; ExportTest.StaticAction(); } @@ -257,19 +255,18 @@ public void CreateMarshalFromJniMethodExpression_StaticActionInt32String () Signature = "(ILjava/lang/String;)V", }; CheckCreateInvocationExpression (e, t, m.Method, typeof (Action), - @"void (IntPtr __jnienv, IntPtr __context, int i, IntPtr native_v) + @"void (IntPtr __jnienv, IntPtr __class, int i, IntPtr v) { JniTransition __envp; + JniRuntime __jvm; + string v_val; __envp = new JniTransition(__jnienv); try { - JniRuntime __jvm; - string v; - __jvm = JniEnvironment.Runtime; - v = Strings.ToString(native_v); - ExportTest.StaticActionInt32String(i, v); + v_val = Strings.ToString(v); + ExportTest.StaticActionInt32String(i, v_val); } catch (Exception __e) { @@ -291,23 +288,20 @@ public void CreateMarshalFromJniMethodExpression_FuncInt64 () Signature = "()J", }; CheckCreateInvocationExpression (e, t, m, typeof (Func), - @"long (IntPtr __jnienv, IntPtr __context) + @"long (IntPtr __jnienv, IntPtr __this) { JniTransition __envp; - long __jret; + JniRuntime __jvm; + long __mret; + ExportTest __this_val; __envp = new JniTransition(__jnienv); try { - JniRuntime __jvm; - ExportTest __this; - long __mret; - __jvm = JniEnvironment.Runtime; - __this = __jvm.ValueManager.GetValue(__context); - __mret = __this.FuncInt64(); - __jret = __mret; - return __jret; + __this_val = __jvm.ValueManager.GetValue(__this); + __mret = __this_val.FuncInt64(); + return __mret; } catch (Exception __e) { @@ -330,23 +324,33 @@ public void CreateMarshalFromJniMethodExpression_FuncIJavaObject () Signature = "()Ljava/lang/Object;", }; CheckCreateInvocationExpression (e, t, m, typeof (Func), - @"IntPtr (IntPtr __jnienv, IntPtr __context) + @"IntPtr (IntPtr __jnienv, IntPtr __this) { JniTransition __envp; - IntPtr __jret; + JniRuntime __jvm; + JavaObject __mret; + ExportTest __this_val; + JniObjectReference __mret_ref; + IntPtr __mret_handle; + IntPtr __mret_rtn; __envp = new JniTransition(__jnienv); try { - JniRuntime __jvm; - ExportTest __this; - JavaObject __mret; - __jvm = JniEnvironment.Runtime; - __this = __jvm.ValueManager.GetValue(__context); - __mret = __this.FuncIJavaObject(); - __jret = References.NewReturnToJniRef(__mret); - return __jret; + __this_val = __jvm.ValueManager.GetValue(__this); + __mret = __this_val.FuncIJavaObject(); + if (null == __mret) + { + return __mret_ref = new JniObjectReference(); + } + else + { + return __mret_ref = __mret.PeerReference; + } + __mret_handle = __mret_ref.Handle; + __mret_rtn = References.NewReturnToJniRef(__mret_ref); + return __mret_rtn; } catch (Exception __e) { @@ -355,6 +359,7 @@ public void CreateMarshalFromJniMethodExpression_FuncIJavaObject () } finally { + JniObjectReference.Dispose(__mret_ref); __envp.Dispose(); } }"); diff --git a/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs b/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs index 978dfe01f..4f77dba3d 100644 --- a/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs +++ b/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq.Expressions; using System.Reflection; +using Java.Interop.Expressions; + namespace Java.Interop { partial class JniRuntime { @@ -91,6 +94,10 @@ public override bool IsJniValueType { get {return true;} } + public override Type MarshalType { + get {return typeof (Boolean);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -130,6 +137,21 @@ public override void DestroyGenericArgumentState (Boolean value, ref JniValueMar JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullableBooleanValueMarshaler : JniValueMarshaler { @@ -199,6 +221,10 @@ public override bool IsJniValueType { get {return true;} } + public override Type MarshalType { + get {return typeof (SByte);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -238,6 +264,21 @@ public override void DestroyGenericArgumentState (SByte value, ref JniValueMarsh JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullableSByteValueMarshaler : JniValueMarshaler { @@ -307,6 +348,10 @@ public override bool IsJniValueType { get {return true;} } + public override Type MarshalType { + get {return typeof (Char);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -346,6 +391,21 @@ public override void DestroyGenericArgumentState (Char value, ref JniValueMarsha JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullableCharValueMarshaler : JniValueMarshaler { @@ -415,6 +475,10 @@ public override bool IsJniValueType { get {return true;} } + public override Type MarshalType { + get {return typeof (Int16);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -454,6 +518,21 @@ public override void DestroyGenericArgumentState (Int16 value, ref JniValueMarsh JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullableInt16ValueMarshaler : JniValueMarshaler { @@ -523,6 +602,10 @@ public override bool IsJniValueType { get {return true;} } + public override Type MarshalType { + get {return typeof (Int32);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -562,6 +645,21 @@ public override void DestroyGenericArgumentState (Int32 value, ref JniValueMarsh JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullableInt32ValueMarshaler : JniValueMarshaler { @@ -631,6 +729,10 @@ public override bool IsJniValueType { get {return true;} } + public override Type MarshalType { + get {return typeof (Int64);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -670,6 +772,21 @@ public override void DestroyGenericArgumentState (Int64 value, ref JniValueMarsh JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullableInt64ValueMarshaler : JniValueMarshaler { @@ -739,6 +856,10 @@ public override bool IsJniValueType { get {return true;} } + public override Type MarshalType { + get {return typeof (Single);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -778,6 +899,21 @@ public override void DestroyGenericArgumentState (Single value, ref JniValueMars JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullableSingleValueMarshaler : JniValueMarshaler { @@ -847,6 +983,10 @@ public override bool IsJniValueType { get {return true;} } + public override Type MarshalType { + get {return typeof (Double);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -886,6 +1026,21 @@ public override void DestroyGenericArgumentState (Double value, ref JniValueMars JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullableDoubleValueMarshaler : JniValueMarshaler { diff --git a/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt b/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt index 52f956211..38ba889a6 100644 --- a/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt +++ b/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt @@ -6,8 +6,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq.Expressions; using System.Reflection; +using Java.Interop.Expressions; + namespace Java.Interop { <# var types = new[]{ @@ -95,6 +98,10 @@ namespace Java.Interop { get {return true;} } + public override Type MarshalType { + get {return typeof (<#= type.Type #>);} + } + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { if (!reference.IsValid) @@ -134,6 +141,21 @@ namespace Java.Interop { JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + return sourceValue; + } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return sourceValue; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return sourceValue; + } } class JniNullable<#= type.Type #>ValueMarshaler : JniValueMarshaler<<#= type.Type #>?> { diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 7817e026c..e140eb416 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; +using Java.Interop.Expressions; + namespace Java.Interop { partial class JniRuntime @@ -523,6 +526,9 @@ public JniValueMarshaler GetValueMarshaler (Type type) if (typeof (IJavaPeerable) == type) return JavaPeerableValueMarshaler.Instance; + if (typeof (void) == type) + return VoidValueMarshaler.Instance; + foreach (var marshaler in JniBuiltinMarshalers) { if (marshaler.Key == type) return marshaler.Value; @@ -560,6 +566,30 @@ protected virtual JniValueMarshaler GetValueMarshalerCore (Type type) } } + class VoidValueMarshaler : JniValueMarshaler { + + internal static VoidValueMarshaler Instance = new VoidValueMarshaler (); + + public override Type MarshalType { + get {return typeof (void);} + } + + public override object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) + { + throw new NotSupportedException (); + } + + public override JniValueMarshalerState CreateObjectReferenceArgumentState (object value, ParameterAttributes synchronize) + { + throw new NotSupportedException (); + } + + public override void DestroyArgumentState (object value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + { + throw new NotSupportedException (); + } + } + class JavaPeerableValueMarshaler : JniValueMarshaler { internal static JavaPeerableValueMarshaler Instance = new JavaPeerableValueMarshaler (); @@ -587,6 +617,44 @@ public override void DestroyGenericArgumentState (IJavaPeerable value, ref JniVa JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + var r = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); + context.LocalVariables.Add (r); + context.CreationStatements.Add ( + Expression.IfThenElse ( + test: Expression.Equal (Expression.Constant (null), sourceValue), + ifTrue: Expression.Assign (r, Expression.New (typeof (JniObjectReference))), + ifFalse: Expression.Assign (r, Expression.Property (sourceValue, "PeerReference")))); + context.CleanupStatements.Add (DisposeObjectReference (r)); + + var h = Expression.Variable (typeof (IntPtr), sourceValue + "_handle"); + context.LocalVariables.Add (h); + context.CreationStatements.Add (Expression.Assign (h, Expression.Property (r, "Handle"))); + return h; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + CreateParameterFromManagedExpression (context, sourceValue, 0); + var r = context.LocalVariables [sourceValue + "_ref"]; + return ReturnObjectReferenceToJni (context, sourceValue.Name, r); + } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + var r = Expression.Variable (targetType, sourceValue + "_val"); + context.LocalVariables.Add (r); + context.CreationStatements.Add ( + Expression.Assign (r, + Expression.Call ( + Expression.Property (context.Runtime, "ValueManager"), + "GetValue", + new[]{targetType}, + sourceValue))); + return r; + } } class DelegatingValueMarshaler : JniValueMarshaler { diff --git a/src/Java.Interop/Java.Interop/JniStringValueMarshaler.cs b/src/Java.Interop/Java.Interop/JniStringValueMarshaler.cs index 4993e68c5..bc859d152 100644 --- a/src/Java.Interop/Java.Interop/JniStringValueMarshaler.cs +++ b/src/Java.Interop/Java.Interop/JniStringValueMarshaler.cs @@ -1,7 +1,10 @@ using System; +using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; +using Java.Interop.Expressions; + namespace Java.Interop { class JniStringValueMarshaler : JniValueMarshaler { @@ -25,5 +28,41 @@ public override void DestroyGenericArgumentState (string value, ref JniValueMars JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } + + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + Func m = JniEnvironment.Strings.NewString; + + var obj = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); + var hdl = Expression.Variable (MarshalType, sourceValue.Name + "_handle"); + context.LocalVariables.Add (obj); + context.LocalVariables.Add (hdl); + context.CreationStatements.Add (Expression.Assign (obj, Expression.Call (m.GetMethodInfo (), sourceValue))); + context.CreationStatements.Add (Expression.Assign (hdl, Expression.Property (obj, "Handle"))); + context.CleanupStatements.Add (DisposeObjectReference (obj)); + return hdl; + } + + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + Func m = JniEnvironment.Strings.NewString; + + var obj = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); + context.LocalVariables.Add (obj); + context.CreationStatements.Add (Expression.Assign (obj, Expression.Call (m.GetMethodInfo (), sourceValue))); + context.CleanupStatements.Add (DisposeObjectReference (obj)); + return ReturnObjectReferenceToJni (context, sourceValue.Name, obj); + } + + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType) + { + Func m = JniEnvironment.Strings.ToString; + + var value = Expression.Variable (targetType, sourceValue.Name + "_val"); + context.LocalVariables.Add (value); + context.CreationStatements.Add (Expression.Assign (value, Expression.Call (m.GetMethodInfo (), sourceValue))); + + return value; + } } } diff --git a/src/Java.Interop/Java.Interop/JniValueMarshaler.cs b/src/Java.Interop/Java.Interop/JniValueMarshaler.cs index 26cf12dfa..72f6d52e4 100644 --- a/src/Java.Interop/Java.Interop/JniValueMarshaler.cs +++ b/src/Java.Interop/Java.Interop/JniValueMarshaler.cs @@ -1,7 +1,37 @@ using System; +using System.Collections.ObjectModel; using System.Reflection; +using System.Linq.Expressions; using System.Runtime.CompilerServices; +using Java.Interop.Expressions; + +namespace Java.Interop.Expressions { + + class VariableCollection : KeyedCollection { + + protected override string GetKeyForItem (ParameterExpression item) + { + return item.Name; + } + } + + public sealed class JniValueMarshalerContext { + public Expression Runtime {get;} + + public KeyedCollection LocalVariables {get;} = new VariableCollection (); + public Collection CreationStatements {get;} = new Collection (); + public Collection CleanupStatements {get;} = new Collection (); + + public JniValueMarshalerContext (Expression runtime) + { + if (runtime == null) + throw new ArgumentNullException (nameof (runtime)); + Runtime = runtime; + } + } +} + namespace Java.Interop { public struct JniValueMarshalerState : IEquatable { @@ -80,6 +110,11 @@ public virtual bool IsJniValueType { get {return false;} } + static readonly Type IntPtr_type = typeof(IntPtr); + public virtual Type MarshalType { + get {return IntPtr_type;} + } + public abstract object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType = null); public virtual JniValueMarshalerState CreateArgumentState (object value, ParameterAttributes synchronize = 0) @@ -89,6 +124,80 @@ public virtual JniValueMarshalerState CreateArgumentState (object value, P public abstract JniValueMarshalerState CreateObjectReferenceArgumentState (object value, ParameterAttributes synchronize = 0); public abstract void DestroyArgumentState (object value, ref JniValueMarshalerState state, ParameterAttributes synchronize = 0); + + object CreateValue (IntPtr handle, Type targetType) + { + var r = new JniObjectReference (handle); + return CreateValue (ref r, JniObjectReferenceOptions.Copy, targetType); + } + + public virtual Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize = 0, Type targetType = null) + { + Func m = CreateValue; + + var self = CreateSelf (context, sourceValue); + + var call = Expression.Call (self, m.GetMethodInfo (), sourceValue, Expression.Constant (targetType, typeof (Type))); + return targetType == null + ? (Expression) call + : Expression.Convert (call, targetType); + } + + Expression CreateSelf (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + var self = Expression.Variable (GetType (), sourceValue.Name + "_marshaler"); + context.LocalVariables.Add (self); + context.CreationStatements.Add (Expression.Assign (self, Expression.New (GetType ()))); + return self; + } + + public virtual Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + CreateParameterFromManagedExpression (context, sourceValue, 0); + var s = context.LocalVariables [sourceValue + "_state"]; + return ReturnObjectReferenceToJni (context, sourceValue.Name, Expression.Property (s, "ReferenceValue")); + } + + protected Expression ReturnObjectReferenceToJni (JniValueMarshalerContext context, string namePrefix, Expression sourceValue) + { + Func m = JniEnvironment.References.NewReturnToJniRef; + var r = Expression.Variable (MarshalType, namePrefix + "_rtn"); + context.LocalVariables.Add (r); + context.CreationStatements.Add ( + Expression.Assign (r, + Expression.Call (m.GetMethodInfo (), sourceValue))); + return r; + } + + delegate void DestroyArgumentStateCb (object value, ref JniValueMarshalerState state, ParameterAttributes synchronize); + public virtual Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + Func c = CreateArgumentState; + DestroyArgumentStateCb d = DestroyArgumentState; + + var self = CreateSelf (context, sourceValue); + var state = Expression.Variable (typeof(JniValueMarshalerState), sourceValue.Name + "_state"); + var ret = Expression.Variable (MarshalType, sourceValue.Name + "_val"); + + context.LocalVariables.Add (state); + context.LocalVariables.Add (ret); + context.CreationStatements.Add (Expression.Assign (state, Expression.Call (self, c.GetMethodInfo (), sourceValue, Expression.Constant (synchronize, typeof (ParameterAttributes))))); + context.CreationStatements.Add ( + Expression.Assign (ret, + Expression.Property ( + Expression.Property (state, "ReferenceValue"), + "Handle"))); + context.CleanupStatements.Add (Expression.Call (self, d.GetMethodInfo (), sourceValue, state, Expression.Constant (synchronize))); + + return ret; + } + + delegate void DisposeObjRef (ref JniObjectReference r); + protected static Expression DisposeObjectReference (Expression sourceValue) + { + DisposeObjRef m = JniObjectReference.Dispose; + return Expression.Call (m.GetMethodInfo (), sourceValue); + } } public abstract class JniValueMarshaler : JniValueMarshaler { diff --git a/src/Java.Interop/Tests/Java.Interop/JniValueMarshalerContractTests.cs b/src/Java.Interop/Tests/Java.Interop/JniValueMarshalerContractTests.cs index c64fccb33..52fc35f21 100644 --- a/src/Java.Interop/Tests/Java.Interop/JniValueMarshalerContractTests.cs +++ b/src/Java.Interop/Tests/Java.Interop/JniValueMarshalerContractTests.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Reflection; using Java.Interop; +using Java.Interop.Expressions; + +using Mono.Linq.Expressions; using NUnit.Framework; @@ -413,6 +417,46 @@ public class JniValueMarshaler_IJavaPeerable_ContractTests : JniValueMarshalerCo readonly IJavaPeerable value = new JavaObject (); protected override IJavaPeerable Value {get {return value;}} + + [Test] + public void CreateReturnValueFromManagedExpression () + { + var runtime = Expression.Variable (typeof (JniRuntime), "__jvm"); + var value = Expression.Variable (typeof (IJavaPeerable), "__value"); + var context = new JniValueMarshalerContext (runtime) { + LocalVariables = { + runtime, + value, + }, + }; + marshaler.CreateReturnValueFromManagedExpression (context, value); + var expected = @"{ + JniRuntime __jvm; + IJavaPeerable __value; + JniObjectReference __value_ref; + IntPtr __value_handle; + IntPtr __value_rtn; + + if (null == __value) + { + return __value_ref = new JniObjectReference(); + } + else + { + return __value_ref = __value.PeerReference; + } + __value_handle = __value_ref.Handle; + __value_rtn = References.NewReturnToJniRef(__value_ref); + JniObjectReference.Dispose(__value_ref); +}"; + CheckExpression (context, expected); + } + + static void CheckExpression (JniValueMarshalerContext context, string expected) + { + var block = Expression.Block (context.LocalVariables, context.CreationStatements.Concat (context.CleanupStatements)); + Assert.AreEqual (expected, block.ToCSharpCode ()); + } } }