소스 검색

Fix dynamic object member access logic (#1937)

Marko Lahma 11 달 전
부모
커밋
0ffda10a14

+ 47 - 1
Jint.Tests.PublicInterface/InteropTests.Dynamic.cs

@@ -114,6 +114,52 @@ public partial class InteropTests
         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>();
@@ -148,4 +194,4 @@ public partial class InteropTests
         public string Name { get; set; }
         public int Age { get; set; }
     }
-}
+}

+ 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 @@ public sealed class TypeResolver
 
         if (typeof(DynamicObject).IsAssignableFrom(type))
         {
-            return new DynamicObjectAccessor(type);
+            return new DynamicObjectAccessor();
         }
 
         var typeResolverMemberNameComparer = MemberNameComparer;