Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix dynamic object member access logic #1937

Merged
merged 1 commit into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion Jint.Tests.PublicInterface/InteropTests.Dynamic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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>();
Expand Down Expand Up @@ -148,4 +194,4 @@ private class Person
public string Name { get; set; }
public int Age { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
4 changes: 1 addition & 3 deletions Jint/Runtime/Interop/Reflection/DynamicObjectAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
}

Expand Down
9 changes: 5 additions & 4 deletions Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Jint/Runtime/Interop/TypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private ReflectionAccessor ResolvePropertyDescriptorFactory(

if (typeof(DynamicObject).IsAssignableFrom(type))
{
return new DynamicObjectAccessor(type);
return new DynamicObjectAccessor();
}

var typeResolverMemberNameComparer = MemberNameComparer;
Expand Down