Skip to content
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
27 changes: 27 additions & 0 deletions Jint.Tests/Runtime/InteropExplicitTypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ public class CI1 : Super, I1
string I1.Name { get; } = "CI1 as I1";
}

public class BaseValue
{
public string BaseOnly { get; } = "base";
}

public class DerivedValue : BaseValue
{
public int DerivedOnlyProperty { get; set; }
}

public class Indexer<T>
{
private readonly T t;
Expand Down Expand Up @@ -67,6 +77,11 @@ public InterfaceHolder()

}

public class BaseValueHolder
{
public BaseValue Value { get; } = new DerivedValue();
}

private readonly Engine _engine;
private readonly InterfaceHolder holder;

Expand Down Expand Up @@ -145,6 +160,18 @@ public void SuperClassFromIndexer()
Assert.Equal(holder.IndexerSuper[0].Name, _engine.Evaluate("holder.IndexerSuper[0].Name"));
}

[Fact]
public void DerivedRuntimePropertyFromBaseDeclaredProperty()
{
var engine = new Engine(options => options.Interop.ThrowOnUnresolvedMember = true);
var holder = new BaseValueHolder();
engine.SetValue("holder", holder);

Assert.Equal("base", engine.Evaluate("holder.Value.BaseOnly"));
engine.Execute("const obj = holder.Value; obj.DerivedOnlyProperty = 123;");
Assert.Equal(123, ((DerivedValue) holder.Value).DerivedOnlyProperty);
}

public struct NullabeStruct : I1
{
public NullabeStruct()
Expand Down
11 changes: 8 additions & 3 deletions Jint/Runtime/Interop/ObjectWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ public override bool Set(JsValue property, JsValue value, JsValue receiver)
if (_properties is null || !_properties.ContainsKey(member))
{
// can try utilize fast path
var accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, ClrType, member, mustBeReadable: false, mustBeWritable: true);
var accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, ClrType, member, mustBeReadable: false, mustBeWritable: true, throwOnError: false);
var actualType = Target.GetType();
if (ClrType != actualType)
{
Expand All @@ -224,20 +224,25 @@ public override bool Set(JsValue property, JsValue value, JsValue receiver)
// that should take precedence over the indexer
if (accessor is IndexerAccessor)
{
var runtimeAccessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, actualType, member, mustBeReadable: false, mustBeWritable: true);
var runtimeAccessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, actualType, member, mustBeReadable: false, mustBeWritable: true, throwOnError: false);
if (runtimeAccessor is not IndexerAccessor && runtimeAccessor != ConstantValueAccessor.NullAccessor)
{
accessor = runtimeAccessor;
}
}
else if (ReferenceEquals(accessor, ConstantValueAccessor.NullAccessor))
{
accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, actualType, member, mustBeReadable: false, mustBeWritable: true);
accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, actualType, member, mustBeReadable: false, mustBeWritable: true, throwOnError: false);
}
}

if (ReferenceEquals(accessor, ConstantValueAccessor.NullAccessor))
{
if (_engine.Options.Interop.ThrowOnUnresolvedMember)
{
throw new MissingMemberException($"Cannot access property '{member}' on type '{ClrType.FullName}");
}

// there's no such property, but we can allow extending by calling base
// which will add properties, this allows for example JS class to extend a CLR type
return base.Set(property, value, receiver);
Expand Down
Loading