diff --git a/Jint.Tests.PublicInterface/InteropTests.Dynamic.cs b/Jint.Tests.PublicInterface/InteropTests.Dynamic.cs index 10e7cb573e..2ed6ad447f 100644 --- a/Jint.Tests.PublicInterface/InteropTests.Dynamic.cs +++ b/Jint.Tests.PublicInterface/InteropTests.Dynamic.cs @@ -114,6 +114,52 @@ public void CanAccessDynamicObject() 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 _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 _properties = new Dictionary(); diff --git a/Jint/Runtime/Descriptors/Specialized/ReflectionDescriptor.cs b/Jint/Runtime/Descriptors/Specialized/ReflectionDescriptor.cs index 473e2c5e24..b732360e8d 100644 --- a/Jint/Runtime/Descriptors/Specialized/ReflectionDescriptor.cs +++ b/Jint/Runtime/Descriptors/Specialized/ReflectionDescriptor.cs @@ -65,7 +65,7 @@ protected internal override JsValue? CustomValue 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); } diff --git a/Jint/Runtime/Interop/Reflection/DynamicObjectAccessor.cs b/Jint/Runtime/Interop/Reflection/DynamicObjectAccessor.cs index 2c445d7f16..8244acf441 100644 --- a/Jint/Runtime/Interop/Reflection/DynamicObjectAccessor.cs +++ b/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) { } diff --git a/Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs b/Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs index 3f6ea0b9c7..9bca7f4f48 100644 --- a/Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs +++ b/Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs @@ -17,13 +17,13 @@ namespace Jint.Runtime.Interop.Reflection; /// 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 @@ public void SetValue(Engine engine, object target, string memberName, JsValue va 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) diff --git a/Jint/Runtime/Interop/TypeResolver.cs b/Jint/Runtime/Interop/TypeResolver.cs index 2fed2aa082..1d4e58a848 100644 --- a/Jint/Runtime/Interop/TypeResolver.cs +++ b/Jint/Runtime/Interop/TypeResolver.cs @@ -129,7 +129,7 @@ private ReflectionAccessor ResolvePropertyDescriptorFactory( if (typeof(DynamicObject).IsAssignableFrom(type)) { - return new DynamicObjectAccessor(type); + return new DynamicObjectAccessor(); } var typeResolverMemberNameComparer = MemberNameComparer;