From 5b4d551d138f4609542796184663906976a0f60f Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Fri, 7 Feb 2020 17:53:41 +0200 Subject: [PATCH] tweaks --- Jint.Tests.Test262/BuiltIns/PromiseTests.cs | 15 ++ Jint.Tests.Test262/test/skipped.json | 18 ++- Jint/IteratorExtensions.cs | 30 +++- Jint/Native/Global/GlobalObject.cs | 2 +- Jint/Native/JsValue.cs | 24 +++- Jint/Native/Promise/PromiseConstructor.cs | 150 +++++++++++++------- Jint/Native/Promise/PromiseInstance.cs | 62 +++----- Jint/Native/Promise/PromisePrototype.cs | 33 +++-- Jint/Native/Reflect/ReflectInstance.cs | 10 +- Jint/Runtime/PromiseRejectedException.cs | 5 +- 10 files changed, 212 insertions(+), 137 deletions(-) create mode 100644 Jint.Tests.Test262/BuiltIns/PromiseTests.cs diff --git a/Jint.Tests.Test262/BuiltIns/PromiseTests.cs b/Jint.Tests.Test262/BuiltIns/PromiseTests.cs new file mode 100644 index 0000000000..111aac6672 --- /dev/null +++ b/Jint.Tests.Test262/BuiltIns/PromiseTests.cs @@ -0,0 +1,15 @@ +using Xunit; + +namespace Jint.Tests.Test262.BuiltIns +{ + public class PromiseTests : Test262Test + { + [Theory(DisplayName = "built-ins\\Promise")] + [MemberData(nameof(SourceFiles), "built-ins\\Promise", false)] + [MemberData(nameof(SourceFiles), "built-ins\\Promise", true, Skip = "Skipped")] + protected void Promise(SourceFile sourceFile) + { + RunTestInternal(sourceFile); + } + } +} \ No newline at end of file diff --git a/Jint.Tests.Test262/test/skipped.json b/Jint.Tests.Test262/test/skipped.json index 4455bca207..8dbbc13498 100644 --- a/Jint.Tests.Test262/test/skipped.json +++ b/Jint.Tests.Test262/test/skipped.json @@ -1,4 +1,16 @@ [ + { + "source": "built-ins/Promise/prototype/finally/species-symbol.js", + "reason": "classes not implemented" + }, + { + "source": "built-ins/Promise/prototype/finally/subclass-species-constructor-reject-count.js", + "reason": "classes not implemented" + }, + { + "source": "built-ins/Promise/prototype/finally/subclass-species-constructor-resolve-count.js", + "reason": "classes not implemented" + }, { "source": "language/expressions/assignment/fn-name-lhs-cover.js", "reason": "Currently quite impossible to detect if assignment target is CoverParenthesizedExpression" @@ -61,13 +73,9 @@ "source": "built-ins/String/raw/special-characters.js", "reason": "Windows line ending differences" }, - { - "source": "built-ins/Symbol/species/builtin-getter-name.js", - "reason": "Promise not implemented" - }, { "source": "language/expressions/object/method-definition/object-method-returns-promise.js", - "reason": "Promise not implemented" + "reason": "async not implemented" }, { "source": "built-ins/Symbol/species/subclassing.js", diff --git a/Jint/IteratorExtensions.cs b/Jint/IteratorExtensions.cs index 973f5bc8e5..5eadb056f8 100644 --- a/Jint/IteratorExtensions.cs +++ b/Jint/IteratorExtensions.cs @@ -1,21 +1,39 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Jint.Native; using Jint.Native.Iterator; +using Jint.Runtime; namespace Jint { internal static class IteratorExtensions { - internal static List CopyToList(this IteratorInstance iterator) + internal static List CopyToList(this IIterator iterator) { var items = new List(); - var item = iterator.Next(); + iterator.TryIteratorStep(out var item); - while (item.GetProperty("done").Value.AsBoolean() == false) + int i = 0; + + while (!TypeConverter.ToBoolean(item.Get("done"))) { - items.Add(item.GetProperty("value").Value); - item = iterator.Next(); + try + { + var jsValue = item.Get("value"); + items.Add(jsValue); + } + catch + { + break; + } + + iterator.TryIteratorStep(out item); + + if (i++ > 1000) + { + throw new Exception("TODO this logic is still flawed"); + } } return items; diff --git a/Jint/Native/Global/GlobalObject.cs b/Jint/Native/Global/GlobalObject.cs index b33cbef538..e98cf37bab 100644 --- a/Jint/Native/Global/GlobalObject.cs +++ b/Jint/Native/Global/GlobalObject.cs @@ -28,7 +28,7 @@ public static GlobalObject CreateGlobalObject(Engine engine) } protected override void Initialize() - { + { const PropertyFlag lengthFlags = PropertyFlag.Configurable; const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable; var properties = new PropertyDictionary(40, checkExistingKeys: false) diff --git a/Jint/Native/JsValue.cs b/Jint/Native/JsValue.cs index fcef6cd65d..51f65f3273 100644 --- a/Jint/Native/JsValue.cs +++ b/Jint/Native/JsValue.cs @@ -70,6 +70,13 @@ public bool IsDate() return this is DateInstance; } + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsPromise() + { + return this is PromiseInstance; + } + [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsRegExp() @@ -352,15 +359,12 @@ public static JsValue FromObject(Engine engine, object value) // todo - custom task types eg ValueTask<>. Not sure these can be supported generically without writing the associated state machine code if (value is Task task) { - return new PromiseInstance(engine, task) - { - _prototype = engine.Promise.PrototypeObject - }; + return new PromiseInstance(engine, task); } // if no known type could be guessed, wrap it as an ObjectInstance var h = engine.Options._WrapObjectHandler; - ObjectInstance o = h != null ? h(value) : null; + ObjectInstance o = h?.Invoke(value); return o ?? new ObjectWrapper(engine, value); } @@ -658,5 +662,15 @@ internal static bool SameValue(JsValue x, JsValue y) return ReferenceEquals(x, y); } } + + internal static IConstructor AssertConstructor(Engine engine, JsValue c) + { + if (!(c is IConstructor constructor)) + { + return ExceptionHelper.ThrowTypeError(engine, c + " is not a constructor"); + } + + return constructor; + } } } diff --git a/Jint/Native/Promise/PromiseConstructor.cs b/Jint/Native/Promise/PromiseConstructor.cs index 488ccb4e3d..8d51d9c44f 100644 --- a/Jint/Native/Promise/PromiseConstructor.cs +++ b/Jint/Native/Promise/PromiseConstructor.cs @@ -2,11 +2,11 @@ using System.Threading.Tasks; using Jint.Collections; using Jint.Native.Function; -using Jint.Native.Iterator; using Jint.Native.Object; using Jint.Native.Symbol; using Jint.Runtime; using Jint.Runtime.Descriptors; +using Jint.Runtime.Descriptors.Specialized; using Jint.Runtime.Interop; namespace Jint.Native.Promise @@ -20,33 +20,33 @@ private PromiseConstructor(Engine engine) { } - public PromisePrototype PrototypeObject { get; private set; } - public static PromiseConstructor CreatePromiseConstructor(Engine engine) { - var obj = new PromiseConstructor(engine) - { - _prototype = engine.Function.PrototypeObject - }; - - // The value of the [[Prototype]] internal property of the Set constructor is the Function prototype object - obj.PrototypeObject = PromisePrototype.CreatePrototypeObject(engine, obj); - obj._length = new PropertyDescriptor(0, PropertyFlag.Configurable); - obj._prototype = obj.PrototypeObject; - + var obj = new PromiseConstructor(engine); + obj._prototype = PromisePrototype.CreatePrototypeObject(engine, obj); + obj._length = new PropertyDescriptor(1, PropertyFlag.Configurable); + obj._prototypeDescriptor = new PropertyDescriptor(obj._prototype, PropertyFlag.AllForbidden); return obj; } protected override void Initialize() { - var properties = new PropertyDictionary(2, checkExistingKeys: false) + const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable; + const PropertyFlag lengthFlags = PropertyFlag.Configurable; + var properties = new PropertyDictionary(5, checkExistingKeys: false) { - ["resolve"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "resolve", Resolve, 1), PropertyFlag.NonEnumerable)), - ["reject"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "reject", Reject, 1), PropertyFlag.NonEnumerable)), - ["all"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "all", All, 1), PropertyFlag.NonEnumerable)), - ["race"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "race", Race, 1), PropertyFlag.NonEnumerable)), + ["resolve"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "resolve", Resolve, 1, lengthFlags), propertyFlags)), + ["reject"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "reject", Reject, 1, lengthFlags), propertyFlags)), + ["all"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "all", All, 1, lengthFlags), propertyFlags)), + ["race"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "race", Race, 1, lengthFlags), propertyFlags)), }; SetProperties(properties); + + var symbols = new SymbolDictionary(1) + { + [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", (thisObj, _) => thisObj, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable) + }; + SetSymbols(symbols); } public override JsValue Call(JsValue thisObject, JsValue[] arguments) @@ -61,45 +61,82 @@ public override JsValue Call(JsValue thisObject, JsValue[] arguments) public ObjectInstance Construct(JsValue[] arguments, JsValue receiver) { - FunctionInstance promiseResolver = null; - - if (arguments.Length == 0 || (promiseResolver = arguments[0] as FunctionInstance) == null) + if (!(arguments.At(0) is ICallable promiseResolver)) { - ExceptionHelper.ThrowTypeError(_engine, $"Promise resolver {(arguments.Length >= 1 ? arguments[0].Type.ToString() : Undefined.ToString())} is not a function"); + return ExceptionHelper.ThrowTypeError( + _engine, + $"Promise resolver {(arguments.At(0))} is not a function"); } - var instance = new PromiseInstance(Engine, promiseResolver) - { - _prototype = PrototypeObject - }; + var instance = new PromiseInstance(Engine, promiseResolver); instance.InvokePromiseResolver(); return instance; } - public PromiseInstance Resolve(JsValue thisRef, JsValue[] args) => PromiseInstance.CreateResolved(Engine, args.Length >= 1 ? args[0] : Undefined); - public PromiseInstance Reject(JsValue thisRef, JsValue[] args) => PromiseInstance.CreateRejected(Engine, args.Length >= 1 ? args[0] : Undefined); + private JsValue Resolve(JsValue thisObj, JsValue[] arguments) + { + if (!thisObj.IsObject()) + { + ExceptionHelper.ThrowTypeError(_engine, "PromiseResolve called on non-object"); + } + + JsValue x = arguments.At(0); + if (x.IsPromise()) + { + var xConstructor = x.Get(CommonProperties.Constructor); + if (SameValue(xConstructor, thisObj)) + { + return x; + } + } + + var promiseCapability = NewPromiseCapability(thisObj); + promiseCapability.Resolve(Undefined, new[] { x }); + return promiseCapability; + } - public PromiseInstance All(JsValue thisRef, JsValue[] args) + private PromiseInstance Reject(JsValue thisObj, JsValue[] arguments) { - if (args.Length == 0 || !(args[0] is ObjectInstance iteratorObj) || iteratorObj.HasProperty(GlobalSymbolRegistry.Iterator) == false) + if (!thisObj.IsObject()) { - ExceptionHelper.ThrowTypeError(Engine, $"undefined is not iterable (cannot read property {GlobalSymbolRegistry.Iterator})"); - return null; + ExceptionHelper.ThrowTypeError(_engine, "PromiseReject called on non-object"); } - var iteratorCtor = iteratorObj.GetProperty(GlobalSymbolRegistry.Iterator).Value; - var iterator = iteratorCtor.Invoke(iteratorObj, new JsValue[0]) as IteratorInstance; + var r = arguments.At(0); + + var promiseCapability = NewPromiseCapability(thisObj); + promiseCapability.Reject(Undefined, new[] { r }); + return promiseCapability; + } + + private JsValue All(JsValue thisObj, JsValue[] arguments) + { + var c = thisObj; + if (!c.IsObject()) + { + ExceptionHelper.ThrowTypeError(_engine, "PromiseReject called on non-object"); + } + + var s = c.Get(GlobalSymbolRegistry.Species); + if (!s.IsNullOrUndefined()) + { + c = s; + } + + var promiseCapability = NewPromiseCapability(c); + + var iterable = arguments.At(0); + var iterator = iterable.GetIterator(_engine); var items = iterator.CopyToList(); if (items.Count == 0) - return Resolve(Undefined, new JsValue[] { Engine.Array.ConstructFast(0) }); - - var chainedPromise = new PromiseInstance(Engine) { - _prototype = Engine.Promise.PrototypeObject - }; + return promiseCapability.Resolve(Undefined, new JsValue[] { Engine.Array.ConstructFast(0) }); + } + + var chainedPromise = new PromiseInstance(Engine); var promises = items.OfType().ToArray(); @@ -150,22 +187,25 @@ public PromiseInstance All(JsValue thisRef, JsValue[] args) return chainedPromise; } - public PromiseInstance Race(JsValue thisRef, JsValue[] args) + private PromiseInstance Race(JsValue thisObj, JsValue[] arguments) { - if (args.Length == 0 || !(args[0] is ObjectInstance iteratorObj) || iteratorObj.HasProperty(GlobalSymbolRegistry.Iterator) == false) + var c = thisObj; + if (!c.IsObject()) { - ExceptionHelper.ThrowTypeError(Engine, $"undefined is not iterable (cannot read property {GlobalSymbolRegistry.Iterator})"); - return null; + ExceptionHelper.ThrowTypeError(_engine, "PromiseReject called on non-object"); } - var iteratorCtor = iteratorObj.GetProperty(GlobalSymbolRegistry.Iterator).Value; - var iterator = iteratorCtor.Invoke(iteratorObj, new JsValue[0]) as IteratorInstance; - var items = iterator.CopyToList(); - - var chainedPromise = new PromiseInstance(Engine) + var s = c.Get(GlobalSymbolRegistry.Species); + if (!s.IsNullOrUndefined()) { - _prototype = Engine.Promise.PrototypeObject - }; + c = s; + } + + var chainedPromise = NewPromiseCapability(c); + var iterable = arguments.At(0); + var iterator = iterable.GetIterator(_engine); + + var items = iterator.CopyToList(); // If no promises passed then the spec says to pend forever! if (items.Count == 0) @@ -224,5 +264,15 @@ public PromiseInstance Race(JsValue thisRef, JsValue[] args) return chainedPromise; } + + private PromiseInstance NewPromiseCapability(JsValue c) + { + var constructor = AssertConstructor(_engine, c); + + var executor = new PromiseInstance(_engine); + var test = Construct(constructor, new JsValue[] { executor }); + var promiseCapability = executor; + return promiseCapability; + } } } \ No newline at end of file diff --git a/Jint/Native/Promise/PromiseInstance.cs b/Jint/Native/Promise/PromiseInstance.cs index 752cf92bc8..f99154e42d 100644 --- a/Jint/Native/Promise/PromiseInstance.cs +++ b/Jint/Native/Promise/PromiseInstance.cs @@ -2,16 +2,16 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using Jint.Native.Function; using Jint.Native.Object; using Jint.Runtime; +using Jint.Runtime.Descriptors; using Jint.Runtime.Interop; namespace Jint.Native.Promise { public class PromiseInstance : ObjectInstance { - private readonly FunctionInstance _promiseResolver; + private readonly ICallable _promiseResolver; private readonly TaskCompletionSource _tcs = new TaskCompletionSource(); public Task Task => _tcs.Task; @@ -19,10 +19,10 @@ public class PromiseInstance : ObjectInstance internal PromiseInstance(Engine engine) : base(engine, ObjectClass.Promise) { - + _prototype = engine.Promise._prototype; } - - public PromiseInstance(Engine engine, FunctionInstance promiseResolver) + + public PromiseInstance(Engine engine, ICallable promiseResolver) : this(engine) { _promiseResolver = promiseResolver; @@ -52,43 +52,20 @@ public PromiseInstance(Engine engine, Task wrappedTask) }); } - public static PromiseInstance CreateResolved(Engine engine, JsValue result) - { - var resolved = new PromiseInstance(engine) - { - _prototype = engine.Promise.PrototypeObject - }; - - resolved._tcs.SetResult(result); - resolved.State = PromiseState.Resolved; - - return resolved; - } - - public static PromiseInstance CreateRejected(Engine engine, JsValue result) - { - var rejected = new PromiseInstance(engine) - { - _prototype = engine.Promise.PrototypeObject - }; - - rejected._tcs.SetException(new PromiseRejectedException(result)); - rejected.State = PromiseState.Rejected; - - return rejected; - } - internal void InvokePromiseResolver() { - _promiseResolver.Invoke(new ClrFunctionInstance(_engine, "", Resolve, 1), new ClrFunctionInstance(_engine, "", Reject, 1)); + _promiseResolver.Call( + this, + new JsValue[] + { + new ClrFunctionInstance(_engine, "", Resolve, 1, PropertyFlag.Configurable), + new ClrFunctionInstance(_engine, "", Reject, 1, PropertyFlag.Configurable) + }); } - internal JsValue Resolve(JsValue thisValue, JsValue[] args) + internal JsValue Resolve(JsValue thisObj, JsValue[] arguments) { - var result = Undefined; - - if (args.Length >= 1) - result = args[0]; + var result = arguments.At(0); // Only first resolve/reject is actioned. Further calls are invalid and ignored if (State == PromiseState.Resolving) @@ -97,15 +74,12 @@ internal JsValue Resolve(JsValue thisValue, JsValue[] args) State = PromiseState.Resolved; } - return Undefined; + return this; } - internal JsValue Reject(JsValue thisValue, JsValue[] args) + internal JsValue Reject(JsValue thisObj, JsValue[] arguments) { - var result = Undefined; - - if (args.Length >= 1) - result = args[0]; + var result = arguments.At(0); // Only first resolve/reject is actioned. Further calls are invalid and ignored if (State == PromiseState.Resolving) @@ -114,7 +88,7 @@ internal JsValue Reject(JsValue thisValue, JsValue[] args) State = PromiseState.Rejected; } - return Undefined; + return this; } } } diff --git a/Jint/Native/Promise/PromisePrototype.cs b/Jint/Native/Promise/PromisePrototype.cs index 6644a9f5b1..2cb2db3ddd 100644 --- a/Jint/Native/Promise/PromisePrototype.cs +++ b/Jint/Native/Promise/PromisePrototype.cs @@ -4,6 +4,7 @@ using Jint.Collections; using Jint.Native.Function; using Jint.Native.Object; +using Jint.Native.Symbol; using Jint.Runtime; using Jint.Runtime.Descriptors; using Jint.Runtime.Interop; @@ -31,17 +32,25 @@ public static PromisePrototype CreatePrototypeObject(Engine engine, PromiseConst protected override void Initialize() { - var properties = new PropertyDictionary(4, checkExistingKeys: false) + const PropertyFlag lengthFlags = PropertyFlag.Configurable; + const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable; + var properties = new PropertyDictionary(5, checkExistingKeys: false) { ["constructor"] = new PropertyDescriptor(_promiseConstructor, PropertyFlag.NonEnumerable), - ["then"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "then", Then, 1, PropertyFlag.Configurable), true, false, true), - ["catch"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "catch", Catch, 1, PropertyFlag.Configurable), true, false, true), - ["finally"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "finally", Finally, 0, PropertyFlag.Configurable), true, false, true) + ["then"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "then", Then, 1, lengthFlags), propertyFlags), + ["catch"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "catch", Catch, 1, lengthFlags), propertyFlags), + ["finally"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "finally", Finally, 1, lengthFlags), propertyFlags) }; SetProperties(properties); + + var symbols = new SymbolDictionary(1) + { + [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor(new JsString("Promise"), PropertyFlag.Configurable) + }; + SetSymbols(symbols); } - public JsValue Then(JsValue thisValue, JsValue[] args) + private JsValue Then(JsValue thisValue, JsValue[] args) { var promise = thisValue as PromiseInstance; @@ -51,10 +60,7 @@ public JsValue Then(JsValue thisValue, JsValue[] args) return null; } - var chainedPromise = new PromiseInstance(Engine) - { - _prototype = _promiseConstructor.PrototypeObject - }; + var chainedPromise = new PromiseInstance(Engine); var resolvedCallback = (args.Length >= 1 ? args[0] : null) as FunctionInstance ?? Undefined; var rejectedCallback = (args.Length >= 2 ? args[1] : null) as FunctionInstance ?? Undefined; @@ -160,9 +166,9 @@ void HandleNestedPromiseResult(JsValue nestedResult) return chainedPromise; } - public JsValue Catch(JsValue thisValue, JsValue[] args) => Then(thisValue, new[] { Undefined, args.Length >= 1 ? args[0] : Undefined }); + private JsValue Catch(JsValue thisValue, JsValue[] args) => Then(thisValue, new[] { Undefined, args.Length >= 1 ? args[0] : Undefined }); - public JsValue Finally(JsValue thisValue, JsValue[] args) + private JsValue Finally(JsValue thisValue, JsValue[] args) { var promise = thisValue as PromiseInstance; @@ -172,10 +178,7 @@ public JsValue Finally(JsValue thisValue, JsValue[] args) return null; } - var chainedPromise = new PromiseInstance(Engine) - { - _prototype = _promiseConstructor.PrototypeObject - }; + var chainedPromise = new PromiseInstance(Engine); var callback = (args.Length >= 1 ? args[0] : null) as FunctionInstance ?? Undefined; diff --git a/Jint/Native/Reflect/ReflectInstance.cs b/Jint/Native/Reflect/ReflectInstance.cs index df037fde06..ba49ee78a8 100644 --- a/Jint/Native/Reflect/ReflectInstance.cs +++ b/Jint/Native/Reflect/ReflectInstance.cs @@ -58,16 +58,10 @@ private JsValue Apply(JsValue thisObject, JsValue[] arguments) private JsValue Construct(JsValue thisObject, JsValue[] arguments) { var targetArgument = arguments.At(0); - if (!(targetArgument is IConstructor target)) - { - return ExceptionHelper.ThrowTypeError(_engine, targetArgument + " is not a constructor"); - } + var target = AssertConstructor(_engine, targetArgument); var newTargetArgument = arguments.At(2, arguments[0]); - if (!(newTargetArgument is IConstructor newTarget)) - { - return ExceptionHelper.ThrowTypeError(_engine, newTargetArgument + " is not a constructor"); - } + AssertConstructor(_engine, newTargetArgument); var args = _engine.Function.PrototypeObject.CreateListFromArrayLike(arguments.At(1)); diff --git a/Jint/Runtime/PromiseRejectedException.cs b/Jint/Runtime/PromiseRejectedException.cs index 7dbb48dafb..164382b0be 100644 --- a/Jint/Runtime/PromiseRejectedException.cs +++ b/Jint/Runtime/PromiseRejectedException.cs @@ -1,9 +1,8 @@ -using System; -using Jint.Native; +using Jint.Native; namespace Jint.Runtime { - public class PromiseRejectedException : Exception + public class PromiseRejectedException : JintException { public JsValue RejectedValue { get; }