From 1a6aa38f1e1c6fc059d2980e378c670719c4e20c Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Sat, 21 Oct 2023 11:50:04 +0300 Subject: [PATCH] Implement Promise.withResolvers and update test suite (#1650) --- .../ShadowRealmTests.cs | 1 - .../Test262Harness.settings.json | 3 +- Jint/Native/Promise/PromiseConstructor.cs | 31 +++++++++++++------ Jint/Native/Proxy/JsProxy.cs | 1 - Jint/Native/RegExp/JsRegExp.cs | 2 ++ Jint/Native/RegExp/RegExpExtensions.cs | 25 ++------------- Jint/Native/RegExp/RegExpPrototype.cs | 27 +++++----------- README.md | 1 + 8 files changed, 36 insertions(+), 55 deletions(-) diff --git a/Jint.Tests.PublicInterface/ShadowRealmTests.cs b/Jint.Tests.PublicInterface/ShadowRealmTests.cs index 1740def1c5..f0d062d506 100644 --- a/Jint.Tests.PublicInterface/ShadowRealmTests.cs +++ b/Jint.Tests.PublicInterface/ShadowRealmTests.cs @@ -1,4 +1,3 @@ -using Jint.Native; using Jint.Native.Object; namespace Jint.Tests.PublicInterface; diff --git a/Jint.Tests.Test262/Test262Harness.settings.json b/Jint.Tests.Test262/Test262Harness.settings.json index 88fa50bd24..f6ac59bf09 100644 --- a/Jint.Tests.Test262/Test262Harness.settings.json +++ b/Jint.Tests.Test262/Test262Harness.settings.json @@ -1,5 +1,5 @@ { - "SuiteGitSha": "9437cab774ab2f22c5cb971b11b8512eca705721", + "SuiteGitSha": "6396ebde0316639292530460d1ef961fd9bbe0d4", //"SuiteDirectory": "//mnt/c/work/test262", "TargetPath": "./Generated", "Namespace": "Jint.Tests.Test262", @@ -46,6 +46,7 @@ // RegExp handling problems "built-ins/RegExp/prototype/exec/S15.10.6.2_A1_T6.js", "language/literals/regexp/u-case-mapping.js", + "built-ins/RegExp/lookahead-quantifier-match-groups.js", // requires investigation how to process complex function name evaluation for property "built-ins/Function/prototype/toString/method-computed-property-name.js", diff --git a/Jint/Native/Promise/PromiseConstructor.cs b/Jint/Native/Promise/PromiseConstructor.cs index 63d7d3b568..56b227b4e6 100644 --- a/Jint/Native/Promise/PromiseConstructor.cs +++ b/Jint/Native/Promise/PromiseConstructor.cs @@ -21,8 +21,6 @@ internal sealed class PromiseConstructor : Constructor { private static readonly JsString _functionName = new JsString("Promise"); - internal PromisePrototype PrototypeObject { get; private set; } - internal PromiseConstructor( Engine engine, Realm realm, @@ -36,18 +34,21 @@ internal PromiseConstructor( _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden); } + internal PromisePrototype PrototypeObject { get; } + protected override void Initialize() { - const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable; - const PropertyFlag lengthFlags = PropertyFlag.Configurable; + const PropertyFlag PropertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable; + const PropertyFlag LengthFlags = PropertyFlag.Configurable; var properties = new PropertyDictionary(6, checkExistingKeys: false) { - ["resolve"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "resolve", Resolve, 1, lengthFlags), propertyFlags)), - ["reject"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "reject", Reject, 1, lengthFlags), propertyFlags)), - ["all"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "all", All, 1, lengthFlags), propertyFlags)), - ["allSettled"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "allSettled", AllSettled, 1, lengthFlags), propertyFlags)), - ["any"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "any", Any, 1, lengthFlags), propertyFlags)), - ["race"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "race", Race, 1, lengthFlags), propertyFlags)), + ["all"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "all", All, 1, LengthFlags), PropertyFlags)), + ["allSettled"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "allSettled", AllSettled, 1, LengthFlags), PropertyFlags)), + ["any"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "any", Any, 1, LengthFlags), PropertyFlags)), + ["race"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "race", Race, 1, LengthFlags), PropertyFlags)), + ["reject"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "reject", Reject, 1, LengthFlags), PropertyFlags)), + ["resolve"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "resolve", Resolve, 1, LengthFlags), PropertyFlags)), + ["withResolvers"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "withResolvers", WithResolvers , 0, LengthFlags), PropertyFlags)), }; SetProperties(properties); @@ -113,6 +114,16 @@ internal JsValue Resolve(JsValue thisObject, JsValue[] arguments) return PromiseResolve(thisObject, x); } + private JsValue WithResolvers(JsValue thisObject, JsValue[] arguments) + { + var promiseCapability = NewPromiseCapability(_engine, thisObject); + var obj = OrdinaryObjectCreate(_engine, _engine.Realm.Intrinsics.Object.PrototypeObject); + obj.CreateDataPropertyOrThrow("promise", promiseCapability.PromiseInstance); + obj.CreateDataPropertyOrThrow("resolve", promiseCapability.ResolveObj); + obj.CreateDataPropertyOrThrow("reject", promiseCapability.RejectObj); + return obj; + } + /// /// https://tc39.es/ecma262/#sec-promise-resolve /// diff --git a/Jint/Native/Proxy/JsProxy.cs b/Jint/Native/Proxy/JsProxy.cs index 1607632b32..386d70a695 100644 --- a/Jint/Native/Proxy/JsProxy.cs +++ b/Jint/Native/Proxy/JsProxy.cs @@ -2,7 +2,6 @@ using Jint.Native.Object; using Jint.Runtime; using Jint.Runtime.Descriptors; -using Jint.Runtime.Interop; namespace Jint.Native.Proxy { diff --git a/Jint/Native/RegExp/JsRegExp.cs b/Jint/Native/RegExp/JsRegExp.cs index 629fabae0f..62f5886cdd 100644 --- a/Jint/Native/RegExp/JsRegExp.cs +++ b/Jint/Native/RegExp/JsRegExp.cs @@ -74,6 +74,8 @@ public string Flags public bool FullUnicode { get; private set; } public bool UnicodeSets { get; private set; } + internal bool HasDefaultRegExpExec => Properties == null && Prototype is RegExpPrototype { HasDefaultExec: true }; + public override PropertyDescriptor GetOwnProperty(JsValue property) { if (property == PropertyLastIndex) diff --git a/Jint/Native/RegExp/RegExpExtensions.cs b/Jint/Native/RegExp/RegExpExtensions.cs index 927f8be297..aa27b6bb8e 100644 --- a/Jint/Native/RegExp/RegExpExtensions.cs +++ b/Jint/Native/RegExp/RegExpExtensions.cs @@ -1,26 +1,5 @@ -using System.Diagnostics.CodeAnalysis; -using Jint.Native.Object; +namespace Jint.Native.RegExp; -namespace Jint.Native.RegExp +internal static class RegExpExtensions { - internal static class RegExpExtensions - { - internal static bool TryGetDefaultRegExpExec(this ObjectInstance? o, [NotNullWhen(true)] out Func? exec) - { - if (o is RegExpPrototype prototype) - { - return prototype.TryGetDefaultExec(prototype, out exec); - } - - if (o is JsRegExp instance) - { - exec = default; - return instance.Properties == null - && TryGetDefaultRegExpExec(instance.Prototype, out exec); - } - - exec = default; - return false; - } - } } diff --git a/Jint/Native/RegExp/RegExpPrototype.cs b/Jint/Native/RegExp/RegExpPrototype.cs index 7912a64859..279f229a53 100644 --- a/Jint/Native/RegExp/RegExpPrototype.cs +++ b/Jint/Native/RegExp/RegExpPrototype.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using Jint.Collections; using Jint.Native.Number; using Jint.Native.Object; @@ -158,7 +157,7 @@ private JsValue Replace(JsValue thisObject, JsValue[] arguments) if (!fullUnicode && !mayHaveNamedCaptures && !TypeConverter.ToBoolean(rx.Get(PropertySticky)) - && rx is JsRegExp rei && rei.TryGetDefaultRegExpExec(out _)) + && rx is JsRegExp rei && rei.HasDefaultRegExpExec) { var count = global ? int.MaxValue : 1; @@ -464,7 +463,7 @@ private JsValue Split(JsValue thisObject, JsValue[] arguments) return a; } - if (!unicodeMatching && rx is JsRegExp R && R.TryGetDefaultRegExpExec(out _)) + if (!unicodeMatching && rx is JsRegExp R && R.HasDefaultRegExpExec) { // we can take faster path @@ -690,7 +689,7 @@ private JsValue Match(JsValue thisObject, JsValue[] arguments) if (!fullUnicode && rx is JsRegExp rei - && rei.TryGetDefaultRegExpExec(out _)) + && rei.HasDefaultRegExpExec) { // fast path var a = _realm.Intrinsics.Array.ArrayCreate(0); @@ -812,8 +811,9 @@ private static ulong AdvanceStringIndex(string s, ulong index, bool unicode) internal static JsValue RegExpExec(ObjectInstance r, string s) { - var exec = r.Get(PropertyExec); - if (exec is ICallable callable) + var ri = r as JsRegExp; + + if ((ri is null || !ri.HasDefaultRegExpExec) && r.Get(PropertyExec) is ICallable callable) { var result = callable.Call(r, new JsValue[] { s }); if (!result.IsNull() && !result.IsObject()) @@ -824,7 +824,6 @@ internal static JsValue RegExpExec(ObjectInstance r, string s) return result; } - var ri = r as JsRegExp; if (ri is null) { ExceptionHelper.ThrowTypeError(r.Engine.Realm); @@ -833,17 +832,7 @@ internal static JsValue RegExpExec(ObjectInstance r, string s) return RegExpBuiltinExec(ri, s); } - internal bool TryGetDefaultExec(ObjectInstance o, [NotNullWhen((true))] out Func? exec) - { - if (o.Get(PropertyExec) is ClrFunctionInstance functionInstance && functionInstance._func == _defaultExec) - { - exec = _defaultExec; - return true; - } - - exec = default; - return false; - } + internal bool HasDefaultExec => Get(PropertyExec) is ClrFunctionInstance functionInstance && functionInstance._func == _defaultExec; /// /// https://tc39.es/ecma262/#sec-regexpbuiltinexec diff --git a/README.md b/README.md index edaaf5a8df..93113e6778 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ Following features are supported in version 3.x. #### ECMAScript Stage 3 (no version yet) - ✔ Array Grouping - `Object.groupBy` and `Map.groupBy` +- ✔ Promise.withResolvers - ✔ ShadowRealm #### Other