diff --git a/Jint.Tests/Runtime/InteropTests.cs b/Jint.Tests/Runtime/InteropTests.cs index 8f383e985..3655bc3d0 100644 --- a/Jint.Tests/Runtime/InteropTests.cs +++ b/Jint.Tests/Runtime/InteropTests.cs @@ -3624,4 +3624,45 @@ public void StringifyShouldIncludeInheritedFieldsAndProperties() engine.SetValue("c", new Circle(12.34)); engine.Evaluate("JSON.stringify(c)").ToString().Should().Be("{\"Radius\":12.34,\"Color\":0,\"Id\":123}"); } + + public class Animal + { + public virtual string name { get; set; } = "animal"; + } + + public class Elephant : Animal + { + public override string name { get; set; } = "elephant"; + public int earSize = 5; + } + + public class Lion : Animal + { + public override string name { get; set; } = "lion"; + public int maneLength = 10; + } + + public class Zoo + { + public Animal king { get => (new Animal[] { new Lion() })[0]; } + public Animal[] animals { get => new Animal[] { new Lion(), new Elephant() }; } + } + + [Fact] + public void CanFindDerivedPropertiesFail() // Fails in 4.01 but success in 2.11 + { + var engine = new Engine(); + engine.SetValue("zoo", new Zoo()); + var kingManeLength = engine.Evaluate("zoo.King.maneLength"); + Assert.Equal(10, kingManeLength.AsNumber()); + } + + [Fact] + public void CanFindDerivedPropertiesSucceed() // Similar case that continues to succeed + { + var engine = new Engine(); + engine.SetValue("zoo", new Zoo()); + var lionManeLength = engine.Evaluate("zoo.animals[0].maneLength"); + Assert.Equal(10, lionManeLength.AsNumber()); + } } diff --git a/Jint/Runtime/Interop/ObjectWrapper.cs b/Jint/Runtime/Interop/ObjectWrapper.cs index 6a53056ff..adb0c50ab 100644 --- a/Jint/Runtime/Interop/ObjectWrapper.cs +++ b/Jint/Runtime/Interop/ObjectWrapper.cs @@ -364,6 +364,10 @@ private PropertyDescriptor GetOwnProperty(JsValue property, bool mustBeReadable, } var accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, ClrType, member, mustBeReadable, mustBeWritable); + if (accessor == ConstantValueAccessor.NullAccessor && ClrType != Target.GetType()) + { + accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, Target.GetType(), member, mustBeReadable, mustBeWritable); + } var descriptor = accessor.CreatePropertyDescriptor(_engine, Target, member, enumerable: !isDictionary); if (!isDictionary && !ReferenceEquals(descriptor, PropertyDescriptor.Undefined)