Browse Source

Backport fix dynamic object member access logic (#1940)

Marko Lahma 11 months ago
parent
commit
7e368a289f

+ 46 - 0
Jint.Tests.PublicInterface/InteropTests.Dynamic.cs

@@ -114,6 +114,52 @@ namespace Jint.Tests.PublicInterface
             Assert.False(engine.Evaluate("test.ContainsKey('c')").AsBoolean());
         }
 
+        [Fact]
+        public void ShouldAccessCustomDynamicObjectProperties()
+        {
+            var t = new DynamicType
+            {
+                ["MemberKey"] = new MemberType
+                {
+                    Field = 4
+                }
+            };
+            var e = new Engine().SetValue("dynamicObj", t);
+            Assert.Equal(4, ((dynamic) t).MemberKey.Field);
+            Assert.Equal(4, e.Evaluate("dynamicObj.MemberKey.Field"));
+        }
+
+        private class MemberType
+        {
+            public int Field;
+        }
+
+        private class DynamicType : DynamicObject
+        {
+            private readonly Dictionary<string, object> _data = new();
+
+            public override bool TryGetMember(GetMemberBinder binder, out object result)
+            {
+                if (_data.ContainsKey(binder.Name))
+                {
+                    result = this[binder.Name];
+                    return true;
+                }
+
+                return base.TryGetMember(binder, out result);
+            }
+
+            public object this[string key]
+            {
+                get
+                {
+                    _data.TryGetValue(key, out var value);
+                    return value;
+                }
+                set => _data[key] = value;
+            }
+        }
+
         private class DynamicClass : DynamicObject
         {
             private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();

+ 1 - 1
Jint/Runtime/Descriptors/Specialized/ReflectionDescriptor.cs

@@ -65,7 +65,7 @@ internal sealed class ReflectionDescriptor : PropertyDescriptor
     private JsValue DoGet(JsValue? thisObj)
     {
         var value = _reflectionAccessor.GetValue(_engine, _target, _propertyName);
-        var type = _reflectionAccessor.MemberType;
+        var type = _reflectionAccessor.MemberType ?? value?.GetType();
         return JsValue.FromObjectWithType(_engine, value, type);
     }
 

+ 1 - 3
Jint/Runtime/Interop/Reflection/DynamicObjectAccessor.cs

@@ -11,9 +11,7 @@ internal sealed class DynamicObjectAccessor : ReflectionAccessor
     private JintSetMemberBinder? _setter;
     private JintGetMemberBinder? _getter;
 
-    public DynamicObjectAccessor(
-        Type memberType,
-        PropertyInfo? indexer = null) : base(memberType, indexer)
+    public DynamicObjectAccessor() : base(memberType: null)
     {
     }
 

+ 5 - 4
Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs

@@ -17,13 +17,13 @@ namespace Jint.Runtime.Interop.Reflection;
 /// </summary>
 internal abstract class ReflectionAccessor
 {
-    private readonly Type _memberType;
+    private readonly Type? _memberType;
     private readonly PropertyInfo? _indexer;
 
-    public Type MemberType => _memberType;
+    public Type? MemberType => _memberType;
 
     protected ReflectionAccessor(
-        Type memberType,
+        Type? memberType,
         PropertyInfo? indexer = null)
     {
         _memberType = memberType;
@@ -114,7 +114,8 @@ internal abstract class ReflectionAccessor
 
     protected virtual object? ConvertValueToSet(Engine engine, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] object value)
     {
-        return engine.TypeConverter.Convert(value, _memberType, CultureInfo.InvariantCulture);
+        var memberType = _memberType ?? value.GetType();
+        return engine.TypeConverter.Convert(value, memberType, CultureInfo.InvariantCulture);
     }
 
     public virtual PropertyDescriptor CreatePropertyDescriptor(Engine engine, object target, string memberName, bool enumerable = true)

+ 1 - 1
Jint/Runtime/Interop/TypeResolver.cs

@@ -129,7 +129,7 @@ namespace Jint.Runtime.Interop
 
             if (typeof(DynamicObject).IsAssignableFrom(type))
             {
-                return new DynamicObjectAccessor(type);
+                return new DynamicObjectAccessor();
             }
 
             var typeResolverMemberNameComparer = MemberNameComparer;