Skip to content

Commit

Permalink
Support CLR object inheritance chain with prototypes (#1518)
Browse files Browse the repository at this point in the history
Co-authored-by: Marko Lahma <marko.lahma@gmail.com>
  • Loading branch information
mainlyer and lahma committed Mar 30, 2023
1 parent 54e1a47 commit 9984761
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 48 deletions.
32 changes: 28 additions & 4 deletions Jint.Tests/Runtime/InstanceOfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,28 @@ public class InstanceOfTests
[Fact]
public void ShouldSupportInheritanceChainUnderInterop()
{
var a = new A();
var b = new B();
var c = new C();

var engine = new Engine();

engine.SetValue("A", TypeReference.CreateTypeReference(engine, typeof(A)));
engine.SetValue("AToo", TypeReference.CreateTypeReference(engine, typeof(A)));
engine.SetValue("B", TypeReference.CreateTypeReference(engine, typeof(B)));
engine.SetValue("C", TypeReference.CreateTypeReference(engine, typeof(C)));

Assert.True(engine.Evaluate("A == A").AsBoolean());
Assert.True(engine.Evaluate("A === A").AsBoolean());
Assert.True(engine.Evaluate("A == AToo").AsBoolean());
Assert.True(engine.Evaluate("A === AToo").AsBoolean());

Assert.True(engine.Evaluate("A.prototype instanceof A").AsBoolean());
Assert.True(engine.Evaluate("B.prototype instanceof A").AsBoolean());
Assert.False(engine.Evaluate("A.prototype instanceof B").AsBoolean());
Assert.True(engine.Evaluate("C.prototype instanceof A").AsBoolean());
Assert.True(engine.Evaluate("C.prototype instanceof B").AsBoolean());

var a = new A();
var b = new B();
var c = new C();

engine.SetValue("a", a);
engine.SetValue("b", b);
engine.SetValue("c", c);
Expand All @@ -32,6 +44,18 @@ public void ShouldSupportInheritanceChainUnderInterop()
Assert.True(engine.Evaluate("c instanceof A").AsBoolean());
Assert.True(engine.Evaluate("c instanceof B").AsBoolean());
Assert.True(engine.Evaluate("c instanceof C").AsBoolean());

Assert.True(engine.Evaluate("new A() instanceof A").AsBoolean());
Assert.False(engine.Evaluate("new A() instanceof B").AsBoolean());
Assert.False(engine.Evaluate("new A() instanceof C").AsBoolean());

Assert.True(engine.Evaluate("new B() instanceof A").AsBoolean());
Assert.True(engine.Evaluate("new B() instanceof B").AsBoolean());
Assert.False(engine.Evaluate("new B() instanceof C").AsBoolean());

Assert.True(engine.Evaluate("new C() instanceof A").AsBoolean());
Assert.True(engine.Evaluate("new C() instanceof B").AsBoolean());
Assert.True(engine.Evaluate("new C() instanceof C").AsBoolean());
}

public class A { }
Expand Down
29 changes: 22 additions & 7 deletions Jint/Runtime/Interop/TypeReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ private TypeReference(Engine engine, Type type)
_prototype = engine.Realm.Intrinsics.Function.PrototypeObject;
_length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;

var proto = new JsObject(engine);
var proto = new TypeReferencePrototype(engine, this);
_prototypeDescriptor = new PropertyDescriptor(proto, PropertyFlag.AllForbidden);

PreventExtensions();
Expand Down Expand Up @@ -184,12 +184,21 @@ static ObjectInstance ObjectCreator(Engine engine, Realm realm, ObjectCreateStat
ObjectCreator,
new ObjectCreateState(this, arguments));


return thisArgument;
}

private readonly record struct ObjectCreateState(TypeReference TypeReference, JsValue[] Arguments);

public override bool Equals(JsValue? obj)
{
if (obj is TypeReference typeReference)
{
return this.ReferenceType == typeReference.ReferenceType;
}

return base.Equals(obj);
}

internal override bool OrdinaryHasInstance(JsValue v)
{
if (v is IObjectWrapper wrapper)
Expand Down Expand Up @@ -295,8 +304,8 @@ private static ReflectionAccessor ResolveMemberAccessor(Engine engine, Type type
return ConstantValueAccessor.NullAccessor;
}

const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
return typeResolver.TryFindMemberAccessor(engine, type, name, bindingFlags, indexerToTry: null, out var accessor)
const BindingFlags BindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
return typeResolver.TryFindMemberAccessor(engine, type, name, BindingFlags, indexerToTry: null, out var accessor)
? accessor
: ConstantValueAccessor.NullAccessor;
}
Expand All @@ -306,16 +315,22 @@ private static ReflectionAccessor ResolveMemberAccessor(Engine engine, Type type
private static JsValue HasInstance(JsValue thisObject, JsValue[] arguments)
{
var typeReference = thisObject as TypeReference;
var objectWrapper = arguments.At(0) as ObjectWrapper;
var other = arguments.At(0);

if (typeReference is null || objectWrapper is null)
if (typeReference is null)
{
return JsBoolean.False;
}

var derivedType = objectWrapper.Target?.GetType();
var baseType = typeReference.ReferenceType;

var derivedType = other switch
{
ObjectWrapper wrapper => wrapper.Target.GetType(),
TypeReferencePrototype otherTypeReference => otherTypeReference.TypeReference.ReferenceType,
_ => null
};

return derivedType != null && baseType != null && (derivedType == baseType || derivedType.IsSubclassOf(baseType));
}
}
Expand Down
48 changes: 11 additions & 37 deletions Jint/Runtime/Interop/TypeReferencePrototype.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,14 @@
//using Jint.Native;
//using Jint.Native.Object;
using Jint.Native.Object;

//namespace Jint.Runtime.Interop
//{
// public sealed class TypeReferencePrototype : ObjectInstance
// {
// private TypeReferencePrototype(Engine engine)
// : base(engine)
// {
// }
namespace Jint.Runtime.Interop;

// public static TypeReferencePrototype CreatePrototypeObject(Engine engine, TypeReference typeReferenceConstructor)
// {
// var obj = new TypeReferencePrototype(engine);
// obj.Prototype = engine.Object.PrototypeObject;
// obj.Extensible = false;
internal sealed class TypeReferencePrototype : ObjectInstance
{
public TypeReferencePrototype(Engine engine, TypeReference typeReference) : base(engine)
{
TypeReference = typeReference;
_prototype = engine.Realm.Intrinsics.Object.PrototypeObject;
}

// obj.FastAddProperty("constructor", typeReferenceConstructor, true, false, true);

// return obj;
// }

// public void Configure()
// {
// FastAddProperty("toString", new ClrFunctionInstance(Engine, ToTypeReferenceString), true, false, true);
// }

// private JsValue ToTypeReferenceString(JsValue thisObj, JsValue[] arguments)
// {
// var typeReference = thisObj.As<TypeReference>();
// if (typeReference == null)
// {
// ExceptionHelper.ThrowTypeError(Engine);
// }

// return typeReference.Type.FullName;
// }
// }
//}
public TypeReference TypeReference { get; }
}

0 comments on commit 9984761

Please sign in to comment.