Skip to content

Commit 0ffda10

Browse files
authored
Fix dynamic object member access logic (#1937)
1 parent e2b5c3b commit 0ffda10

File tree

5 files changed

+55
-10
lines changed

5 files changed

+55
-10
lines changed

Jint.Tests.PublicInterface/InteropTests.Dynamic.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,52 @@ public void CanAccessDynamicObject()
114114
Assert.False(engine.Evaluate("test.ContainsKey('c')").AsBoolean());
115115
}
116116

117+
[Fact]
118+
public void ShouldAccessCustomDynamicObjectProperties()
119+
{
120+
var t = new DynamicType
121+
{
122+
["MemberKey"] = new MemberType
123+
{
124+
Field = 4
125+
}
126+
};
127+
var e = new Engine().SetValue("dynamicObj", t);
128+
Assert.Equal(4, ((dynamic) t).MemberKey.Field);
129+
Assert.Equal(4, e.Evaluate("dynamicObj.MemberKey.Field"));
130+
}
131+
132+
private class MemberType
133+
{
134+
public int Field;
135+
}
136+
137+
private class DynamicType : DynamicObject
138+
{
139+
private readonly Dictionary<string, object> _data = new();
140+
141+
public override bool TryGetMember(GetMemberBinder binder, out object result)
142+
{
143+
if (_data.ContainsKey(binder.Name))
144+
{
145+
result = this[binder.Name];
146+
return true;
147+
}
148+
149+
return base.TryGetMember(binder, out result);
150+
}
151+
152+
public object this[string key]
153+
{
154+
get
155+
{
156+
_data.TryGetValue(key, out var value);
157+
return value;
158+
}
159+
set => _data[key] = value;
160+
}
161+
}
162+
117163
private class DynamicClass : DynamicObject
118164
{
119165
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
@@ -148,4 +194,4 @@ private class Person
148194
public string Name { get; set; }
149195
public int Age { get; set; }
150196
}
151-
}
197+
}

Jint/Runtime/Descriptors/Specialized/ReflectionDescriptor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ protected internal override JsValue? CustomValue
6565
private JsValue DoGet(JsValue? thisObj)
6666
{
6767
var value = _reflectionAccessor.GetValue(_engine, _target, _propertyName);
68-
var type = _reflectionAccessor.MemberType;
68+
var type = _reflectionAccessor.MemberType ?? value?.GetType();
6969
return JsValue.FromObjectWithType(_engine, value, type);
7070
}
7171

Jint/Runtime/Interop/Reflection/DynamicObjectAccessor.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ internal sealed class DynamicObjectAccessor : ReflectionAccessor
1111
private JintSetMemberBinder? _setter;
1212
private JintGetMemberBinder? _getter;
1313

14-
public DynamicObjectAccessor(
15-
Type memberType,
16-
PropertyInfo? indexer = null) : base(memberType, indexer)
14+
public DynamicObjectAccessor() : base(memberType: null)
1715
{
1816
}
1917

Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ namespace Jint.Runtime.Interop.Reflection;
1717
/// </summary>
1818
internal abstract class ReflectionAccessor
1919
{
20-
private readonly Type _memberType;
20+
private readonly Type? _memberType;
2121
private readonly PropertyInfo? _indexer;
2222

23-
public Type MemberType => _memberType;
23+
public Type? MemberType => _memberType;
2424

2525
protected ReflectionAccessor(
26-
Type memberType,
26+
Type? memberType,
2727
PropertyInfo? indexer = null)
2828
{
2929
_memberType = memberType;
@@ -114,7 +114,8 @@ public void SetValue(Engine engine, object target, string memberName, JsValue va
114114

115115
protected virtual object? ConvertValueToSet(Engine engine, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] object value)
116116
{
117-
return engine.TypeConverter.Convert(value, _memberType, CultureInfo.InvariantCulture);
117+
var memberType = _memberType ?? value.GetType();
118+
return engine.TypeConverter.Convert(value, memberType, CultureInfo.InvariantCulture);
118119
}
119120

120121
public virtual PropertyDescriptor CreatePropertyDescriptor(Engine engine, object target, string memberName, bool enumerable = true)

Jint/Runtime/Interop/TypeResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ private ReflectionAccessor ResolvePropertyDescriptorFactory(
129129

130130
if (typeof(DynamicObject).IsAssignableFrom(type))
131131
{
132-
return new DynamicObjectAccessor(type);
132+
return new DynamicObjectAccessor();
133133
}
134134

135135
var typeResolverMemberNameComparer = MemberNameComparer;

0 commit comments

Comments
 (0)