From dbcc9f6f7cbb103606632f97f6bc1efac3232701 Mon Sep 17 00:00:00 2001 From: Robert Gaggl Date: Sun, 21 Nov 2021 14:49:31 +0100 Subject: [PATCH 1/3] implements Promise.allSettled() resolves mozilla/rhino#1061 --- src/org/mozilla/javascript/NativePromise.java | 58 +++++++++-- testsrc/test262.properties | 97 ------------------- 2 files changed, 49 insertions(+), 106 deletions(-) diff --git a/src/org/mozilla/javascript/NativePromise.java b/src/org/mozilla/javascript/NativePromise.java index 05d79efeeb..47329e58de 100644 --- a/src/org/mozilla/javascript/NativePromise.java +++ b/src/org/mozilla/javascript/NativePromise.java @@ -44,6 +44,8 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { scope, "reject", 1, NativePromise::reject, DONTENUM, DONTENUM | READONLY); constructor.defineConstructorMethod( scope, "all", 1, NativePromise::all, DONTENUM, DONTENUM | READONLY); + constructor.defineConstructorMethod( + scope, "allSettled", 1, NativePromise::allSettled, DONTENUM, DONTENUM | READONLY); constructor.defineConstructorMethod( scope, "race", 1, NativePromise::race, DONTENUM, DONTENUM | READONLY); @@ -160,8 +162,7 @@ private static Object reject(Context cx, Scriptable scope, Scriptable thisObj, O return cap.promise; } - // Promise.all - private static Object all(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Object doAll(Context cx, Scriptable scope, Scriptable thisObj, Object[] args, boolean failFast) { Capability cap = new Capability(cx, scope, thisObj); Object arg = (args.length > 0 ? args[0] : Undefined.instance); @@ -180,7 +181,7 @@ private static Object all(Context cx, Scriptable scope, Scriptable thisObj, Obje IteratorLikeIterable.Itr iterator = iterable.iterator(); try { - PromiseAllResolver resolver = new PromiseAllResolver(iterator, thisObj, cap); + PromiseAllResolver resolver = new PromiseAllResolver(iterator, thisObj, cap, failFast); try { return resolver.resolve(cx, scope); } finally { @@ -198,6 +199,16 @@ private static Object all(Context cx, Scriptable scope, Scriptable thisObj, Obje } } + // Promise.all + private static Object all(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + return doAll(cx, scope, thisObj, args, true); + } + + // Promise.allSettled + private static Object allSettled(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + return doAll(cx, scope, thisObj, args, false); + } + // Promise.race private static Object race(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { Capability cap = new Capability(cx, scope, thisObj); @@ -691,11 +702,13 @@ private static class PromiseAllResolver { IteratorLikeIterable.Itr iterator; Scriptable thisObj; Capability capability; + boolean failFast; - PromiseAllResolver(IteratorLikeIterable.Itr iter, Scriptable thisObj, Capability cap) { + PromiseAllResolver(IteratorLikeIterable.Itr iter, Scriptable thisObj, Capability cap, boolean failFast) { this.iterator = iter; this.thisObj = thisObj; this.capability = cap; + this.failFast = failFast; } Object resolve(Context topCx, Scriptable topScope) { @@ -745,13 +758,40 @@ Object resolve(Context topCx, Scriptable topScope) { new LambdaFunction( topScope, 1, - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> - eltResolver.resolve( + (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { + Object value = (args.length > 0 ? args[0] : Undefined.instance); + if (!failFast) { + Scriptable elementResult = cx.newObject(scope); + elementResult.put("status", elementResult, "fulfilled"); + elementResult.put("value", elementResult, value); + value = elementResult; + } + return eltResolver.resolve( cx, scope, - (args.length > 0 ? args[0] : Undefined.instance), - this)); + value, + this); + }); resolveFunc.setStandardPropertyAttributes(DONTENUM | READONLY); + + Callable rejectFunc = capability.reject; + if (!failFast) { + LambdaFunction resolveSettledRejection = new LambdaFunction( + topScope, + 1, + (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { + Scriptable result = cx.newObject(scope); + result.put("status", result, " rejected"); + result.put("reason", result, (args.length > 0 ? args[0] : Undefined.instance)); + return eltResolver.resolve( + cx, + scope, + result, + this); + }); + resolveSettledRejection.setStandardPropertyAttributes(DONTENUM | READONLY); + rejectFunc = resolveSettledRejection; + } remainingElements++; // Call "then" on the promise with the resolution func @@ -761,7 +801,7 @@ Object resolve(Context topCx, Scriptable topScope) { topCx, topScope, ScriptRuntime.lastStoredScriptable(topCx), - new Object[] {resolveFunc, capability.reject}); + new Object[] {resolveFunc, rejectFunc}); index++; } } diff --git a/testsrc/test262.properties b/testsrc/test262.properties index b5955c3272..df8b5bb44d 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -979,103 +979,6 @@ built-ins/parseInt 3/60 (5.0%) S15.1.2.2_A9.2.js built-ins/Promise 444/599 (74.12%) - allSettled/call-resolve-element.js - allSettled/call-resolve-element-after-return.js - allSettled/call-resolve-element-items.js - allSettled/capability-executor-called-twice.js - allSettled/capability-executor-not-callable.js - allSettled/capability-resolve-throws-no-close.js - allSettled/capability-resolve-throws-reject.js {unsupported: [async]} - allSettled/ctx-ctor.js {unsupported: [class]} - allSettled/ctx-ctor-throws.js - allSettled/does-not-invoke-array-setters.js {unsupported: [async]} - allSettled/invoke-resolve.js - allSettled/invoke-resolve-error-close.js - allSettled/invoke-resolve-error-reject.js {unsupported: [async]} - allSettled/invoke-resolve-get-error.js {unsupported: [async]} - allSettled/invoke-resolve-get-error-reject.js {unsupported: [async]} - allSettled/invoke-resolve-get-once-multiple-calls.js - allSettled/invoke-resolve-get-once-no-calls.js - allSettled/invoke-resolve-on-promises-every-iteration-of-custom.js {unsupported: [class, async]} - allSettled/invoke-resolve-on-promises-every-iteration-of-promise.js {unsupported: [async]} - allSettled/invoke-resolve-on-values-every-iteration-of-promise.js {unsupported: [async]} - allSettled/invoke-resolve-return.js - allSettled/invoke-then.js - allSettled/invoke-then-error-close.js - allSettled/invoke-then-error-reject.js {unsupported: [async]} - allSettled/invoke-then-get-error-close.js - allSettled/invoke-then-get-error-reject.js {unsupported: [async]} - allSettled/is-function.js - allSettled/iter-arg-is-false-reject.js {unsupported: [async]} - allSettled/iter-arg-is-null-reject.js {unsupported: [async]} - allSettled/iter-arg-is-number-reject.js {unsupported: [async]} - allSettled/iter-arg-is-poisoned.js {unsupported: [async]} - allSettled/iter-arg-is-string-resolve.js {unsupported: [async]} - allSettled/iter-arg-is-symbol-reject.js {unsupported: [async]} - allSettled/iter-arg-is-true-reject.js {unsupported: [async]} - allSettled/iter-arg-is-undefined-reject.js {unsupported: [async]} - allSettled/iter-assigned-false-reject.js {unsupported: [async]} - allSettled/iter-assigned-null-reject.js {unsupported: [async]} - allSettled/iter-assigned-number-reject.js {unsupported: [async]} - allSettled/iter-assigned-string-reject.js {unsupported: [async]} - allSettled/iter-assigned-symbol-reject.js {unsupported: [async]} - allSettled/iter-assigned-true-reject.js {unsupported: [async]} - allSettled/iter-assigned-undefined-reject.js {unsupported: [async]} - allSettled/iter-next-err-reject.js {unsupported: [async]} - allSettled/iter-next-val-err-no-close.js - allSettled/iter-next-val-err-reject.js {unsupported: [async]} - allSettled/iter-returns-false-reject.js {unsupported: [async]} - allSettled/iter-returns-null-reject.js {unsupported: [async]} - allSettled/iter-returns-number-reject.js {unsupported: [async]} - allSettled/iter-returns-string-reject.js {unsupported: [async]} - allSettled/iter-returns-symbol-reject.js {unsupported: [async]} - allSettled/iter-returns-true-reject.js {unsupported: [async]} - allSettled/iter-returns-undefined-reject.js {unsupported: [async]} - allSettled/iter-step-err-no-close.js - allSettled/iter-step-err-reject.js {unsupported: [async]} - allSettled/length.js - allSettled/name.js - allSettled/new-reject-function.js - allSettled/new-resolve-function.js - allSettled/prop-desc.js - allSettled/reject-deferred.js {unsupported: [async]} - allSettled/reject-element-function-extensible.js - allSettled/reject-element-function-length.js - allSettled/reject-element-function-multiple-calls.js - allSettled/reject-element-function-name.js - allSettled/reject-element-function-nonconstructor.js - allSettled/reject-element-function-prototype.js - allSettled/reject-ignored-deferred.js {unsupported: [async]} - allSettled/reject-ignored-immed.js {unsupported: [async]} - allSettled/reject-immed.js {unsupported: [async]} - allSettled/resolve-before-loop-exit.js - allSettled/resolve-before-loop-exit-from-same.js - allSettled/resolve-element-function-extensible.js - allSettled/resolve-element-function-length.js - allSettled/resolve-element-function-name.js - allSettled/resolve-element-function-nonconstructor.js - allSettled/resolve-element-function-prototype.js - allSettled/resolve-from-same-thenable.js - allSettled/resolve-ignores-late-rejection.js {unsupported: [async]} - allSettled/resolve-ignores-late-rejection-deferred.js {unsupported: [async]} - allSettled/resolve-non-callable.js {unsupported: [async]} - allSettled/resolve-non-thenable.js {unsupported: [async]} - allSettled/resolve-not-callable-reject-with-typeerror.js {unsupported: [async]} - allSettled/resolve-poisoned-then.js {unsupported: [async]} - allSettled/resolve-thenable.js {unsupported: [async]} - allSettled/resolved-all-fulfilled.js {unsupported: [async]} - allSettled/resolved-all-mixed.js {unsupported: [async]} - allSettled/resolved-all-rejected.js {unsupported: [async]} - allSettled/resolved-immed.js {unsupported: [async]} - allSettled/resolved-sequence.js {unsupported: [async]} - allSettled/resolved-sequence-extra-ticks.js {unsupported: [async]} - allSettled/resolved-sequence-mixed.js {unsupported: [async]} - allSettled/resolved-sequence-with-rejections.js {unsupported: [async]} - allSettled/resolved-then-catch-finally.js {unsupported: [async]} - allSettled/resolves-empty-array.js {unsupported: [async]} - allSettled/resolves-to-array.js {unsupported: [async]} - allSettled/returns-promise.js - allSettled/species-get-error.js {unsupported: [Symbol.species]} all/capability-resolve-throws-reject.js {unsupported: [async]} all/ctx-ctor.js {unsupported: [class]} all/does-not-invoke-array-setters.js {unsupported: [async]} From 2d6004e4dcfce5334837355b1c788e8efd541340 Mon Sep 17 00:00:00 2001 From: Robert Gaggl Date: Sun, 21 Nov 2021 17:49:13 +0100 Subject: [PATCH 2/3] re-generated test262.properties --- testsrc/test262.properties | 61 +++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/testsrc/test262.properties b/testsrc/test262.properties index df8b5bb44d..7906a84e5d 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -978,7 +978,66 @@ built-ins/parseInt 3/60 (5.0%) S15.1.2.2_A2_T10_U180E.js {unsupported: [u180e]} S15.1.2.2_A9.2.js -built-ins/Promise 444/599 (74.12%) +built-ins/Promise 406/599 (67.78%) + allSettled/capability-resolve-throws-reject.js {unsupported: [async]} + allSettled/ctx-ctor.js {unsupported: [class]} + allSettled/does-not-invoke-array-setters.js {unsupported: [async]} + allSettled/invoke-resolve-error-reject.js {unsupported: [async]} + allSettled/invoke-resolve-get-error.js {unsupported: [async]} + allSettled/invoke-resolve-get-error-reject.js {unsupported: [async]} + allSettled/invoke-resolve-on-promises-every-iteration-of-custom.js {unsupported: [class, async]} + allSettled/invoke-resolve-on-promises-every-iteration-of-promise.js {unsupported: [async]} + allSettled/invoke-resolve-on-values-every-iteration-of-promise.js {unsupported: [async]} + allSettled/invoke-then-error-reject.js {unsupported: [async]} + allSettled/invoke-then-get-error-reject.js {unsupported: [async]} + allSettled/iter-arg-is-false-reject.js {unsupported: [async]} + allSettled/iter-arg-is-null-reject.js {unsupported: [async]} + allSettled/iter-arg-is-number-reject.js {unsupported: [async]} + allSettled/iter-arg-is-poisoned.js {unsupported: [async]} + allSettled/iter-arg-is-string-resolve.js {unsupported: [async]} + allSettled/iter-arg-is-symbol-reject.js {unsupported: [async]} + allSettled/iter-arg-is-true-reject.js {unsupported: [async]} + allSettled/iter-arg-is-undefined-reject.js {unsupported: [async]} + allSettled/iter-assigned-false-reject.js {unsupported: [async]} + allSettled/iter-assigned-null-reject.js {unsupported: [async]} + allSettled/iter-assigned-number-reject.js {unsupported: [async]} + allSettled/iter-assigned-string-reject.js {unsupported: [async]} + allSettled/iter-assigned-symbol-reject.js {unsupported: [async]} + allSettled/iter-assigned-true-reject.js {unsupported: [async]} + allSettled/iter-assigned-undefined-reject.js {unsupported: [async]} + allSettled/iter-next-err-reject.js {unsupported: [async]} + allSettled/iter-next-val-err-reject.js {unsupported: [async]} + allSettled/iter-returns-false-reject.js {unsupported: [async]} + allSettled/iter-returns-null-reject.js {unsupported: [async]} + allSettled/iter-returns-number-reject.js {unsupported: [async]} + allSettled/iter-returns-string-reject.js {unsupported: [async]} + allSettled/iter-returns-symbol-reject.js {unsupported: [async]} + allSettled/iter-returns-true-reject.js {unsupported: [async]} + allSettled/iter-returns-undefined-reject.js {unsupported: [async]} + allSettled/iter-step-err-reject.js {unsupported: [async]} + allSettled/reject-deferred.js {unsupported: [async]} + allSettled/reject-ignored-deferred.js {unsupported: [async]} + allSettled/reject-ignored-immed.js {unsupported: [async]} + allSettled/reject-immed.js {unsupported: [async]} + allSettled/resolve-ignores-late-rejection.js {unsupported: [async]} + allSettled/resolve-ignores-late-rejection-deferred.js {unsupported: [async]} + allSettled/resolve-non-callable.js {unsupported: [async]} + allSettled/resolve-non-thenable.js {unsupported: [async]} + allSettled/resolve-not-callable-reject-with-typeerror.js {unsupported: [async]} + allSettled/resolve-poisoned-then.js {unsupported: [async]} + allSettled/resolve-thenable.js {unsupported: [async]} + allSettled/resolved-all-fulfilled.js {unsupported: [async]} + allSettled/resolved-all-mixed.js {unsupported: [async]} + allSettled/resolved-all-rejected.js {unsupported: [async]} + allSettled/resolved-immed.js {unsupported: [async]} + allSettled/resolved-sequence.js {unsupported: [async]} + allSettled/resolved-sequence-extra-ticks.js {unsupported: [async]} + allSettled/resolved-sequence-mixed.js {unsupported: [async]} + allSettled/resolved-sequence-with-rejections.js {unsupported: [async]} + allSettled/resolved-then-catch-finally.js {unsupported: [async]} + allSettled/resolves-empty-array.js {unsupported: [async]} + allSettled/resolves-to-array.js {unsupported: [async]} + allSettled/species-get-error.js {unsupported: [Symbol.species]} all/capability-resolve-throws-reject.js {unsupported: [async]} all/ctx-ctor.js {unsupported: [class]} all/does-not-invoke-array-setters.js {unsupported: [async]} From ce867574fe6c88e80f4b6e6a2b0840e47a860798 Mon Sep 17 00:00:00 2001 From: Robert Gaggl Date: Sat, 27 Nov 2021 13:29:57 +0100 Subject: [PATCH 3/3] spotless --- src/org/mozilla/javascript/NativePromise.java | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/org/mozilla/javascript/NativePromise.java b/src/org/mozilla/javascript/NativePromise.java index 47329e58de..e92260c8d6 100644 --- a/src/org/mozilla/javascript/NativePromise.java +++ b/src/org/mozilla/javascript/NativePromise.java @@ -162,7 +162,8 @@ private static Object reject(Context cx, Scriptable scope, Scriptable thisObj, O return cap.promise; } - private static Object doAll(Context cx, Scriptable scope, Scriptable thisObj, Object[] args, boolean failFast) { + private static Object doAll( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args, boolean failFast) { Capability cap = new Capability(cx, scope, thisObj); Object arg = (args.length > 0 ? args[0] : Undefined.instance); @@ -205,7 +206,8 @@ private static Object all(Context cx, Scriptable scope, Scriptable thisObj, Obje } // Promise.allSettled - private static Object allSettled(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + private static Object allSettled( + Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { return doAll(cx, scope, thisObj, args, false); } @@ -704,7 +706,11 @@ private static class PromiseAllResolver { Capability capability; boolean failFast; - PromiseAllResolver(IteratorLikeIterable.Itr iter, Scriptable thisObj, Capability cap, boolean failFast) { + PromiseAllResolver( + IteratorLikeIterable.Itr iter, + Scriptable thisObj, + Capability cap, + boolean failFast) { this.iterator = iter; this.thisObj = thisObj; this.capability = cap; @@ -758,37 +764,39 @@ Object resolve(Context topCx, Scriptable topScope) { new LambdaFunction( topScope, 1, - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { - Object value = (args.length > 0 ? args[0] : Undefined.instance); - if (!failFast) { - Scriptable elementResult = cx.newObject(scope); - elementResult.put("status", elementResult, "fulfilled"); - elementResult.put("value", elementResult, value); - value = elementResult; - } - return eltResolver.resolve( - cx, - scope, - value, - this); + (Context cx, + Scriptable scope, + Scriptable thisObj, + Object[] args) -> { + Object value = (args.length > 0 ? args[0] : Undefined.instance); + if (!failFast) { + Scriptable elementResult = cx.newObject(scope); + elementResult.put("status", elementResult, "fulfilled"); + elementResult.put("value", elementResult, value); + value = elementResult; + } + return eltResolver.resolve(cx, scope, value, this); }); resolveFunc.setStandardPropertyAttributes(DONTENUM | READONLY); Callable rejectFunc = capability.reject; if (!failFast) { - LambdaFunction resolveSettledRejection = new LambdaFunction( - topScope, - 1, - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { - Scriptable result = cx.newObject(scope); - result.put("status", result, " rejected"); - result.put("reason", result, (args.length > 0 ? args[0] : Undefined.instance)); - return eltResolver.resolve( - cx, - scope, - result, - this); - }); + LambdaFunction resolveSettledRejection = + new LambdaFunction( + topScope, + 1, + (Context cx, + Scriptable scope, + Scriptable thisObj, + Object[] args) -> { + Scriptable result = cx.newObject(scope); + result.put("status", result, " rejected"); + result.put( + "reason", + result, + (args.length > 0 ? args[0] : Undefined.instance)); + return eltResolver.resolve(cx, scope, result, this); + }); resolveSettledRejection.setStandardPropertyAttributes(DONTENUM | READONLY); rejectFunc = resolveSettledRejection; }