diff --git a/src/Java.Interop.Dynamic/Java.Interop.Dynamic.csproj b/src/Java.Interop.Dynamic/Java.Interop.Dynamic.csproj index 75ac7aa55..325ee7b5e 100644 --- a/src/Java.Interop.Dynamic/Java.Interop.Dynamic.csproj +++ b/src/Java.Interop.Dynamic/Java.Interop.Dynamic.csproj @@ -41,5 +41,9 @@ {94BD81F7-B06F-4295-9636-F8A3B6BDC762} Java.Interop + + {0C001D50-4176-45AE-BDC8-BA626508B0CC} + Mono.Linq.Expressions + \ No newline at end of file diff --git a/src/Java.Interop.Dynamic/Java.Interop.Dynamic/DynamicJavaClass.cs b/src/Java.Interop.Dynamic/Java.Interop.Dynamic/DynamicJavaClass.cs index 7898222e8..4d0e2b318 100644 --- a/src/Java.Interop.Dynamic/Java.Interop.Dynamic/DynamicJavaClass.cs +++ b/src/Java.Interop.Dynamic/Java.Interop.Dynamic/DynamicJavaClass.cs @@ -9,6 +9,8 @@ using Java.Interop; +using Mono.Linq.Expressions; + namespace Java.Interop.Dynamic { public class DynamicJavaClass : IDynamicMetaObjectProvider @@ -75,32 +77,35 @@ static string GetEncodedJniSignature (InvokeMemberBinder binder, DynamicMetaObje sb.Append (typeInfo.ToString ()); } sb.Append (")"); - sb.Append (JniEnvironment.Current.JavaVM.GetJniTypeInfoForType (returnType).ToString ()); + sb.Append (JniEnvironment.Current.JavaVM.GetJniTypeInfoForType (returnType).JniTypeReference); return sb.ToString (); } - public object GetStaticFieldValue (string fieldName, Type fieldType) + internal bool TryGetStaticMemberValue (string fieldName, Type fieldType, out object value) { Debug.WriteLine ("# DynamicJavaClass({0}).field({1}) as {2}", JniClassName, fieldName, fieldType); var typeInfo = JniEnvironment.Current.JavaVM.GetJniTypeInfoForType (fieldType); - var encoded = fieldName + "\u0000" + typeInfo.ToString (); - return members.StaticFields.GetValue (encoded); + var encoded = fieldName + "\u0000" + typeInfo.JniTypeReference; + try { + value = members.StaticFields.GetValue (encoded); + return true; + } + catch (JavaException e) { + value = null; + e.Dispose (); + return false; + } } public void SetStaticFieldValue (string fieldName, Type fieldType, object value) { Debug.WriteLine ("# DynamicJavaClass({0}).field({1}) as {2} = {3}", JniClassName, fieldName, fieldType, value); var typeInfo = JniEnvironment.Current.JavaVM.GetJniTypeInfoForType (fieldType); - var encoded = fieldName + "\u0000" + typeInfo.ToString (); + var encoded = fieldName + "\u0000" + typeInfo.JniTypeReference; members.StaticFields.SetValue (encoded, value); } - internal StaticFieldAccess GetStaticFieldAccess (string fieldName) - { - return new StaticFieldAccess (this, fieldName); - } - #if false Type CreateManagedPeerType () { @@ -131,13 +136,15 @@ public Expression ExpressionAsT { } public DynamicMetaObject (Expression parameter, T value) - : base (parameter, BindingRestrictions.Empty, value) + : base (parameter, BindingRestrictions.GetInstanceRestriction (parameter, value), value) { } } class MetaStaticMemberAccessObject : DynamicMetaObject { + delegate bool TryGetStaticMemberValue (string fieldName, Type fieldType, out object value); + public MetaStaticMemberAccessObject (Expression parameter, DynamicJavaClass value) : base (parameter, value) { @@ -196,16 +203,15 @@ public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicM public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { // Console.WriteLine ("GetMember: Expression={0} [{1}]; property={2}", Expression.ToCSharpCode (), Expression.Type, binder.Name); - var expr = - Expression.Call ( - ExpressionAsT, - typeof (DynamicJavaClass).GetMethod ("GetStaticFieldAccess", BindingFlags.Instance | BindingFlags.NonPublic), - Expression.Constant (binder.Name)); - return new DynamicMetaObject ( - expr, - BindingRestrictions.GetTypeRestriction ( - expr, - typeof (StaticFieldAccess))); + TryGetStaticMemberValue m = Value.TryGetStaticMemberValue; + var access = new DeferredConvert { + Arguments = new[]{Expression.Constant (binder.Name)}, + Instance = Value, + FallbackCreator = binder.FallbackGetMember, + Method = m.Method, + }; + var accessE = Expression.Constant (access); + return new DynamicMetaObject (accessE, BindingRestrictions.GetInstanceRestriction (accessE, access)); } } @@ -257,45 +263,45 @@ public override DynamicMetaObject BindConvert (ConvertBinder binder) } } - class StaticFieldAccess : IDynamicMetaObjectProvider { + class DeferredConvert : IDynamicMetaObjectProvider { - public string FieldName {get; private set;} - public DynamicJavaClass JavaClass {get; private set;} - - public StaticFieldAccess (DynamicJavaClass klass, string fieldName) - { - JavaClass = klass; - FieldName = fieldName; - } + public T Instance; + public MethodInfo Method; + public Expression[] Arguments; + public Func FallbackCreator; - public DynamicMetaObject GetMetaObject(Expression parameter) + public DynamicMetaObject GetMetaObject (Expression parameter) { -// Console.WriteLine ("# FieldAccessInfo.GetMetaObject: parameter={0}", parameter); - return new MetaStaticFieldAccessObject(parameter, this); + return new DeferredConvertMetaObject (parameter, this); } } - class MetaStaticFieldAccessObject : DynamicMetaObject { + class DeferredConvertMetaObject : DynamicMetaObject> { - public MetaStaticFieldAccessObject (Expression e, StaticFieldAccess value) + public DeferredConvertMetaObject (Expression e, DeferredConvert value) : base (e, value) { -// Console.WriteLine ("MyHelperObject: e={0}", e.ToCSharpCode ()); + Debug.WriteLine ("DeferredConvertMetaObject<{0}>: e={1}", typeof (T).Name, e.ToCSharpCode ()); } public override DynamicMetaObject BindConvert (ConvertBinder binder) { -// Console.WriteLine ("MetaStaticFieldAccessObject.Convert: Expression={0} [{1}]", Expression.ToCSharpCode (), Expression.Type); - - var field = ExpressionAsT; - var instance = Expression.Property (field, "JavaClass"); - var fieldName = Expression.Property (field, "FieldName"); - var gsfv = typeof(DynamicJavaClass).GetMethod ("GetStaticFieldValue"); - var expr = Expression.Convert ( - Expression.Call (instance, gsfv, fieldName, Expression.Constant (binder.Type)), - binder.Type); + Debug.WriteLine ("DeferredConvertMetaObject<{0}>.BindConvert: Expression='{1}'; Expression.Type={2}", typeof (T).Name, Expression.ToCSharpCode (), Expression.Type); + + var instance = Expression.Constant (Value.Instance); + var instanceMO = new DynamicMetaObject (instance, BindingRestrictions.GetInstanceRestriction (instance, typeof (T))); + var value = Expression.Parameter (typeof (object), "value"); + Debug.WriteLine ("DeferredConvertMetaObject<{0}>.BindConvert: Fallback={1}", typeof (T).Name, Value.FallbackCreator (instanceMO).Expression.ToCSharpCode ()); + var call = Expression.Block ( + new[]{value}, + Expression.Condition ( + test: Expression.Call (instance, Value.Method, Value.Arguments.Concat (new Expression[]{Expression.Constant (binder.Type), value})), + ifTrue: Expression.Convert (value, binder.Type), + ifFalse: Expression.Convert (Value.FallbackCreator (instanceMO).Expression, binder.Type)) + ); + Debug.WriteLine ("MetaStaticFieldAccessObject.Convert: call={0}", call.ToCSharpCode ()); return new DynamicMetaObject ( - expr, + call, BindingRestrictions.GetInstanceRestriction (Expression, Value)); } } diff --git a/src/Java.Interop.Dynamic/Tests/Java.Interop.Dynamic/DynamicJavaClassTests.cs b/src/Java.Interop.Dynamic/Tests/Java.Interop.Dynamic/DynamicJavaClassTests.cs index f035dbdac..3dd6aa1b3 100644 --- a/src/Java.Interop.Dynamic/Tests/Java.Interop.Dynamic/DynamicJavaClassTests.cs +++ b/src/Java.Interop.Dynamic/Tests/Java.Interop.Dynamic/DynamicJavaClassTests.cs @@ -16,16 +16,27 @@ namespace Java.Interop.DynamicTests { [TestFixture] class DynamicJavaClassTests : Java.InteropTests.JavaVMFixture { + const string Arrays_class = "java/util/Arrays"; + const string Integer_class = "java/lang/Integer"; + [Test] public void Constructor () { Assert.Throws (() => new DynamicJavaClass (null)); } + [Test] + public void JniClassName () + { + dynamic Arrays = new DynamicJavaClass (Arrays_class); + string name = Arrays.JniClassName; + Assert.AreEqual (Arrays_class, name); + } + [Test] public void CallStaticMethod () { - dynamic Arrays = new DynamicJavaClass ("java/util/Arrays"); + dynamic Arrays = new DynamicJavaClass (Arrays_class); var array = new int[]{ 1, 2, 3, 4 }; int value = 3; int index = Arrays.binarySearch (array, value); @@ -35,7 +46,7 @@ public void CallStaticMethod () [Test] public void ReadStaticMember () { - dynamic Integer = new DynamicJavaClass ("java/lang/Integer"); + dynamic Integer = new DynamicJavaClass (Integer_class); int max = Integer.MAX_VALUE; Assert.AreEqual (int.MaxValue, max); } @@ -43,7 +54,7 @@ public void ReadStaticMember () [Test] public void WriteStaticMember () { - dynamic Integer = new DynamicJavaClass ("java/lang/Integer"); + dynamic Integer = new DynamicJavaClass (Integer_class); int cur = Integer.MAX_VALUE; Console.WriteLine ("# MAX_VALUE={0}", cur); Integer.MAX_VALUE = 42;