Skip to content

Commit

Permalink
Fix inheriting from TypeReference backed interop type (#1168)
Browse files Browse the repository at this point in the history
* remove old checks for can write property and use proper logic
* make OrdinaryCreateFromConstructor generic
* cleanup some build warnings
  • Loading branch information
lahma committed May 15, 2022
1 parent 8c89089 commit e48c6f5
Show file tree
Hide file tree
Showing 21 changed files with 140 additions and 106 deletions.
1 change: 1 addition & 0 deletions Jint.Tests.Test262/Jint.Tests.Test262.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<AssemblyOriginatorKeyFile>..\Jint\Jint.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<IsPackable>false</IsPackable>
<NoWarn>$(NoWarn);CS8002</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Jint\Jint.csproj" />
Expand Down
7 changes: 3 additions & 4 deletions Jint.Tests/Runtime/GenericMethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public void TestFancyGenericFail()
// we _may_ be able to address this by simply instantiating generic types using System.Object for the generic arguments
// This test currently generates the following error:
// No public methods with the specified arguments were found.
[Fact(Skip = "not supported yet")]
public void TestGenericClassDeriveFromGenericInterface()
{
var engine = new Engine(cfg => cfg.AllowClr(typeof(OpenGenericTest<>).Assembly));
Expand Down Expand Up @@ -206,17 +207,15 @@ public static bool SelectInvoked
private set;
}

TState _stateSubject;

public ReduxStore()
{
SelectInvoked = false;
}

public IObservable<TResult> Select<TResult>(ISelectorWithoutProps<TState, TResult> selector, string? optionsStr = null)
public IObservable<TResult> Select<TResult>(ISelectorWithoutProps<TState, TResult> selector, string optionsStr = null)
{
SelectInvoked = true;
return selector.Apply(_stateSubject);
return selector.Apply(null);
}
}

Expand Down
62 changes: 53 additions & 9 deletions Jint.Tests/Runtime/InteropTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -789,20 +789,64 @@ public void CanUseDelegateAsFunction()
[Fact]
public void JavaScriptClassCanExtendClrType()
{
var engine = new Engine();
engine.SetValue("TestClass", TypeReference.CreateTypeReference<TestClass>(engine));
_engine.SetValue("TestClass", TypeReference.CreateTypeReference<TestClass>(_engine));

_engine.Execute("class ExtendedType extends TestClass { constructor() { super(); this.a = 1; } get aProp() { return 'A'; } }");
_engine.Execute("class MyExtendedType extends ExtendedType { constructor() { super(); this.b = 2; } get bProp() { return 'B'; } }");
_engine.Evaluate("let obj = new MyExtendedType();");

engine.Execute("class ExtendedType extends TestClass { constructor() { super(); this.a = 1; } }");
engine.Execute("class MyExtendedType extends ExtendedType { constructor() { super(); this.b = 2; } }");
engine.Evaluate("let obj = new MyExtendedType();");
_engine.Evaluate("obj.setString('Hello World!');");

engine.Evaluate("obj.setString('Hello World!');");
Assert.Equal("Hello World!", _engine.Evaluate("obj.string"));
Assert.Equal(1, _engine.Evaluate("obj.a"));
Assert.Equal(2, _engine.Evaluate("obj.b"));

Assert.Equal("Hello World!", engine.Evaluate("obj.string"));
Assert.Equal(1, engine.Evaluate("obj.a"));
Assert.Equal(2, engine.Evaluate("obj.b"));
Assert.Equal("A", _engine.Evaluate("obj.aProp"));
Assert.Equal("B", _engine.Evaluate("obj.bProp"));

// TODO we should have a special prototype based on wrapped type so we could differentiate between own and type properties
// Assert.Equal("[\"a\"]", _engine.Evaluate("JSON.stringify(Object.getOwnPropertyNames(new ExtendedType()))"));
// Assert.Equal("[\"a\",\"b\"]", _engine.Evaluate("JSON.stringify(Object.getOwnPropertyNames(new MyExtendedType()))"));
}

[Fact]
public void ShouldAllowMethodsOnClrExtendedTypes()
{
_engine.SetValue("ClrBaseType", TypeReference.CreateTypeReference<TestClass>(_engine));
_engine.Evaluate(@"
class JsBaseType {}
class ExtendsFromJs extends JsBaseType {
constructor() {
super();
this.a = 1;
}
getA() {
return this.a;
}
}
class ExtendsFromClr extends ClrBaseType {
constructor() {
super();
this.a = 1;
}
getA() {
return this.a;
}
}
");

var extendsFromJs = _engine.Construct("ExtendsFromJs");
Assert.Equal(1, _engine.Evaluate("new ExtendsFromJs().getA();"));
Assert.NotEqual(JsValue.Undefined, extendsFromJs.Get("getA"));

var extendsFromClr = _engine.Construct("ExtendsFromClr");
Assert.Equal(1, _engine.Evaluate("new ExtendsFromClr().getA();"));
Assert.NotEqual(JsValue.Undefined, extendsFromClr.Get("getA"));
}

private struct TestStruct
{
public int Value;
Expand Down
1 change: 0 additions & 1 deletion Jint.Tests/Runtime/ModuleTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#if(NET6_0_OR_GREATER)
using System;
using System.IO;
using System.Reflection;
#endif
Expand Down
4 changes: 3 additions & 1 deletion Jint/Native/BigInt/BigIntConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
var o = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.BigInt.PrototypeObject,
static (engine, realm, state) => new BigIntInstance(engine, (JsBigInt) state), value);
static (engine, realm, state) => new BigIntInstance(engine, state),
value);

return o;
}

Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/BigInt/BigIntPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,4 @@ private JsBigInt ThisBigIntValue(JsValue value)
return JsBigInt.One;
}
}
}
}
2 changes: 1 addition & 1 deletion Jint/Native/DataView/DataViewConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
var o = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.DataView.PrototypeObject,
static (engine, realm, state) => new DataViewInstance(engine));
static (Engine engine, Realm _, object _) => new DataViewInstance(engine));

if (buffer.IsDetachedBuffer)
{
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Date/DateConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
var o = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.Date.PrototypeObject,
static (engine, realm, _) => new DateInstance(engine));
static (Engine engine, Realm _, object _) => new DateInstance(engine));
o.DateValue = dv;
return o;
}
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Error/ErrorConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
var o = OrdinaryCreateFromConstructor(
newTarget,
_intrinsicDefaultProto,
static (engine, realm, state) => new ErrorInstance(engine));
static (Engine engine, Realm _, object _) => new ErrorInstance(engine));

var jsValue = arguments.At(0);
if (!jsValue.IsUndefined())
Expand Down
6 changes: 3 additions & 3 deletions Jint/Native/Function/FunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,11 @@ internal void SetFunctionName(JsValue name, string prefix = null, bool force = f
/// In spec intrinsicDefaultProto is string pointing to intrinsic, but we do a selector.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal T OrdinaryCreateFromConstructor<T>(
internal T OrdinaryCreateFromConstructor<T, TState>(
JsValue constructor,
Func<Intrinsics, ObjectInstance> intrinsicDefaultProto,
Func<Engine, Realm, JsValue, T> objectCreator,
JsValue state = null) where T : ObjectInstance
Func<Engine, Realm, TState, T> objectCreator,
TState state = default) where T : ObjectInstance
{
var proto = GetPrototypeFromConstructor(constructor, intrinsicDefaultProto);

Expand Down
4 changes: 2 additions & 2 deletions Jint/Native/Function/ScriptFunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Jint.Native.Function
{
public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
{
internal bool _isClassConstructor;
private bool _isClassConstructor;

/// <summary>
/// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
Expand Down Expand Up @@ -121,7 +121,7 @@ ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
thisArgument = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.Object.PrototypeObject,
static (engine, realm, _) => new ObjectInstance(engine));
static (Engine engine, Realm _, object _) => new ObjectInstance(engine));
}

var calleeContext = PrepareForOrdinaryCall(newTarget);
Expand Down
3 changes: 2 additions & 1 deletion Jint/Native/Map/MapConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
var map = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.Map.PrototypeObject,
static (engine, realm, _) => new MapInstance(engine, realm));
static (Engine engine, Realm realm, object _) => new MapInstance(engine, realm));

if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
{
var adder = map.Get("set");
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Object/ObjectConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
return OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.Object.PrototypeObject,
(engine, realm, state) => new ObjectInstance(engine));
static (Engine engine, Realm _, object _) => new ObjectInstance(engine));
}

if (arguments.Length > 0)
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Promise/PromiseConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
var instance = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.Promise.PrototypeObject,
static(engine, realm, _) => new PromiseInstance(engine));
static (Engine engine, Realm _, object _) => new PromiseInstance(engine));

var (resolve, reject) = instance.CreateResolvingFunctions();
promiseExecutor.Call(Undefined, new JsValue[] {resolve, reject});
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/RegExp/RegExpConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private RegExpInstance RegExpAlloc(JsValue newTarget)
var r = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.RegExp.PrototypeObject,
static (engine, realm, _) => new RegExpInstance(engine));
static (Engine engine, Realm _, object _) => new RegExpInstance(engine));
return r;
}

Expand Down
3 changes: 2 additions & 1 deletion Jint/Native/Set/SetConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
var set = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.Set.PrototypeObject,
static (engine, realm, _) => new SetInstance(engine));
static (Engine engine, Realm _, object _) => new SetInstance(engine));

if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
{
var adderValue = set.Get("add");
Expand Down
1 change: 0 additions & 1 deletion Jint/Native/String/StringPrototype.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/WeakMap/WeakMapConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
var map = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.WeakMap.PrototypeObject,
static (engine, realm, _) => new WeakMapInstance(engine));
static (Engine engine, Realm _, object _) => new WeakMapInstance(engine));
if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
{
var adder = map.Get("set");
Expand Down
3 changes: 2 additions & 1 deletion Jint/Native/WeakSet/WeakSetConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
var set = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.WeakSet.PrototypeObject,
static (engine, realm, _) => new WeakSetInstance(engine));
static (Engine engine, Realm _, object _) => new WeakSetInstance(engine));

if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
{
var adder = set.Get("add") as ICallable;
Expand Down

0 comments on commit e48c6f5

Please sign in to comment.