зеркало из https://github.com/mozilla/rhino.git
Implement ES2021 Promise.any
This commit is contained in:
Родитель
5c8707faa0
Коммит
ffdf2313f1
|
@ -7,6 +7,8 @@
|
|||
package org.mozilla.javascript;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The class of error objects
|
||||
|
@ -55,12 +57,7 @@ final class NativeError extends IdScriptableObject {
|
|||
}
|
||||
if (arglen >= 2) {
|
||||
if (args[1] instanceof NativeObject) {
|
||||
NativeObject options = (NativeObject) args[1];
|
||||
Object cause = ScriptableObject.getProperty(options, "cause");
|
||||
if (cause != NOT_FOUND) {
|
||||
ScriptableObject.putProperty(obj, "cause", cause);
|
||||
obj.setAttributes("cause", DONTENUM);
|
||||
}
|
||||
installCause((NativeObject) args[1], obj);
|
||||
} else {
|
||||
ScriptableObject.putProperty(obj, "fileName", ScriptRuntime.toString(args[1]));
|
||||
if (arglen >= 3) {
|
||||
|
@ -73,6 +70,58 @@ final class NativeError extends IdScriptableObject {
|
|||
return obj;
|
||||
}
|
||||
|
||||
static NativeError makeAggregate(
|
||||
Context cx, Scriptable scope, IdFunctionObject ctorObj, Object[] args) {
|
||||
Scriptable proto = (Scriptable) ctorObj.get("prototype", ctorObj);
|
||||
|
||||
NativeError obj = new NativeError();
|
||||
obj.setPrototype(proto);
|
||||
obj.setParentScope(scope);
|
||||
|
||||
int arglen = args.length;
|
||||
if (arglen >= 1) {
|
||||
if (arglen >= 2) {
|
||||
if (!Undefined.isUndefined(args[1])) {
|
||||
ScriptableObject.putProperty(obj, "message", ScriptRuntime.toString(args[1]));
|
||||
obj.setAttributes("message", DONTENUM);
|
||||
}
|
||||
|
||||
if (arglen >= 3) {
|
||||
if (args[2] instanceof NativeObject) {
|
||||
installCause((NativeObject) args[2], obj);
|
||||
} else {
|
||||
ScriptableObject.putProperty(
|
||||
obj, "fileName", ScriptRuntime.toString(args[2]));
|
||||
if (arglen >= 4) {
|
||||
ScriptableObject.putProperty(
|
||||
obj, "lineNumber", ScriptRuntime.toInt32(args[3]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Object iterator = ScriptRuntime.callIterator(args[0], cx, scope);
|
||||
try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, iterator)) {
|
||||
List<Object> errors = new ArrayList<>();
|
||||
for (Object o : it) {
|
||||
errors.add(o);
|
||||
}
|
||||
|
||||
Scriptable newArray = cx.newArray(scope, errors.toArray());
|
||||
obj.defineProperty("errors", newArray, DONTENUM);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void installCause(NativeObject options, NativeError obj) {
|
||||
Object cause = ScriptableObject.getProperty(options, "cause");
|
||||
if (cause != NOT_FOUND) {
|
||||
ScriptableObject.putProperty(obj, "cause", cause);
|
||||
obj.setAttributes("cause", DONTENUM);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillConstructorProperties(IdFunctionObject ctor) {
|
||||
addIdFunctionProperty(
|
||||
|
|
|
@ -113,8 +113,12 @@ public class NativeGlobal implements Serializable, IdFunctionCall {
|
|||
cx, scope, TopLevel.Builtins.Error, ScriptRuntime.emptyArgs);
|
||||
errorProto.defineProperty("name", name, DONTENUM);
|
||||
errorProto.defineProperty("message", "", DONTENUM);
|
||||
IdFunctionObject ctor =
|
||||
new IdFunctionObject(obj, FTAG, Id_new_CommonError, name, 1, scope);
|
||||
IdFunctionObject ctor;
|
||||
if (error == TopLevel.NativeErrors.AggregateError) {
|
||||
ctor = new IdFunctionObject(obj, FTAG, Id_new_AggregateError, name, 2, scope);
|
||||
} else {
|
||||
ctor = new IdFunctionObject(obj, FTAG, Id_new_CommonError, name, 1, scope);
|
||||
}
|
||||
ctor.markAsConstructor(errorProto);
|
||||
ctor.setPrototype(nativeError);
|
||||
errorProto.put("constructor", errorProto, ctor);
|
||||
|
@ -203,6 +207,9 @@ public class NativeGlobal implements Serializable, IdFunctionCall {
|
|||
// The implementation of all the ECMA error constructors
|
||||
// (SyntaxError, TypeError, etc.)
|
||||
return NativeError.make(cx, scope, f, args);
|
||||
|
||||
case Id_new_AggregateError:
|
||||
return NativeError.makeAggregate(cx, scope, f, args);
|
||||
}
|
||||
}
|
||||
throw f.unknown();
|
||||
|
@ -760,5 +767,6 @@ public class NativeGlobal implements Serializable, IdFunctionCall {
|
|||
Id_unescape = 12,
|
||||
Id_uneval = 13,
|
||||
LAST_SCOPE_FUNCTION_ID = 13,
|
||||
Id_new_CommonError = 14;
|
||||
Id_new_CommonError = 14,
|
||||
Id_new_AggregateError = 15;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ public class NativePromise extends ScriptableObject {
|
|||
scope, "allSettled", 1, NativePromise::allSettled, DONTENUM, DONTENUM | READONLY);
|
||||
constructor.defineConstructorMethod(
|
||||
scope, "race", 1, NativePromise::race, DONTENUM, DONTENUM | READONLY);
|
||||
constructor.defineConstructorMethod(
|
||||
scope, "any", 1, NativePromise::any, DONTENUM, DONTENUM | READONLY);
|
||||
|
||||
ScriptRuntimeES6.addSymbolSpecies(cx, scope, constructor);
|
||||
|
||||
|
@ -280,6 +282,43 @@ public class NativePromise extends ScriptableObject {
|
|||
}
|
||||
}
|
||||
|
||||
private static Object any(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
|
||||
Capability cap = new Capability(cx, scope, thisObj);
|
||||
Object arg = (args.length > 0 ? args[0] : Undefined.instance);
|
||||
|
||||
IteratorLikeIterable iterable;
|
||||
try {
|
||||
Object maybeIterable = ScriptRuntime.callIterator(arg, cx, scope);
|
||||
iterable = new IteratorLikeIterable(cx, scope, maybeIterable);
|
||||
} catch (RhinoException re) {
|
||||
cap.reject.call(
|
||||
cx,
|
||||
scope,
|
||||
Undefined.SCRIPTABLE_UNDEFINED,
|
||||
new Object[] {getErrorObject(cx, scope, re)});
|
||||
return cap.promise;
|
||||
}
|
||||
|
||||
IteratorLikeIterable.Itr iterator = iterable.iterator();
|
||||
try {
|
||||
PromiseAnyRejector rejector = new PromiseAnyRejector(iterator, thisObj, cap);
|
||||
try {
|
||||
return rejector.reject(cx, scope);
|
||||
} finally {
|
||||
if (!iterator.isDone()) {
|
||||
iterable.close();
|
||||
}
|
||||
}
|
||||
} catch (RhinoException re) {
|
||||
cap.reject.call(
|
||||
cx,
|
||||
scope,
|
||||
Undefined.SCRIPTABLE_UNDEFINED,
|
||||
new Object[] {getErrorObject(cx, scope, re)});
|
||||
return cap.promise;
|
||||
}
|
||||
}
|
||||
|
||||
// Promise.prototype.then
|
||||
private Object then(
|
||||
Context cx, Scriptable scope, LambdaConstructor defaultConstructor, Object[] args) {
|
||||
|
@ -811,7 +850,109 @@ public class NativePromise extends ScriptableObject {
|
|||
}
|
||||
}
|
||||
|
||||
// This object keeps track of the state necessary to execute Promise.any
|
||||
private static class PromiseAnyRejector {
|
||||
// Limit the number of promises in Promise.any the same as it is in V8.
|
||||
private static final int MAX_PROMISES = 1 << 21;
|
||||
|
||||
final ArrayList<Object> errors = new ArrayList<>();
|
||||
int remainingElements = 1;
|
||||
|
||||
IteratorLikeIterable.Itr iterator;
|
||||
Scriptable thisObj;
|
||||
Capability capability;
|
||||
|
||||
PromiseAnyRejector(IteratorLikeIterable.Itr iter, Scriptable thisObj, Capability cap) {
|
||||
this.iterator = iter;
|
||||
this.thisObj = thisObj;
|
||||
this.capability = cap;
|
||||
}
|
||||
|
||||
Object reject(Context topCx, Scriptable topScope) {
|
||||
int index = 0;
|
||||
// Do this first because we should catch any exception before
|
||||
// invoking the iterator.
|
||||
Callable resolve =
|
||||
ScriptRuntime.getPropFunctionAndThis(thisObj, "resolve", topCx, topScope);
|
||||
Scriptable storedThis = ScriptRuntime.lastStoredScriptable(topCx);
|
||||
|
||||
// Iterate manually because we need to catch exceptions in a special way.
|
||||
while (true) {
|
||||
if (index == MAX_PROMISES) {
|
||||
throw ScriptRuntime.rangeErrorById("msg.promise.any.toobig");
|
||||
}
|
||||
boolean hasNext;
|
||||
Object nextVal = Undefined.instance;
|
||||
boolean nextOk = false;
|
||||
try {
|
||||
hasNext = iterator.hasNext();
|
||||
if (hasNext) {
|
||||
nextVal = iterator.next();
|
||||
}
|
||||
nextOk = true;
|
||||
} finally {
|
||||
if (!nextOk) {
|
||||
iterator.setDone(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasNext) {
|
||||
if (--remainingElements == 0) {
|
||||
Scriptable newArray = topCx.newArray(topScope, errors.toArray());
|
||||
NativeError error =
|
||||
(NativeError)
|
||||
topCx.newObject(
|
||||
topScope,
|
||||
"AggregateError",
|
||||
new Object[] {newArray});
|
||||
throw new JavaScriptException(error, null, 0);
|
||||
}
|
||||
return capability.promise;
|
||||
}
|
||||
|
||||
errors.add(Undefined.instance);
|
||||
|
||||
// Call "resolve" to get the next promise in the chain
|
||||
Object nextPromise =
|
||||
resolve.call(topCx, topScope, storedThis, new Object[] {nextVal});
|
||||
|
||||
// Create a resolution func that will stash its result in the right place
|
||||
PromiseElementResolver eltResolver = new PromiseElementResolver(index);
|
||||
LambdaFunction rejectFunc =
|
||||
new LambdaFunction(
|
||||
topScope,
|
||||
1,
|
||||
(Context cx,
|
||||
Scriptable scope,
|
||||
Scriptable thisObj,
|
||||
Object[] args) -> {
|
||||
Object value = (args.length > 0 ? args[0] : Undefined.instance);
|
||||
return eltResolver.reject(cx, scope, value, this);
|
||||
});
|
||||
remainingElements++;
|
||||
|
||||
// Call "then" on the promise with the resolution func
|
||||
Callable thenFunc =
|
||||
ScriptRuntime.getPropFunctionAndThis(nextPromise, "then", topCx, topScope);
|
||||
thenFunc.call(
|
||||
topCx,
|
||||
topScope,
|
||||
ScriptRuntime.lastStoredScriptable(topCx),
|
||||
new Object[] {capability.resolve, rejectFunc});
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
void finalRejection(Context cx, Scriptable scope) {
|
||||
Scriptable newArray = cx.newArray(scope, errors.toArray());
|
||||
NativeError error =
|
||||
(NativeError) cx.newObject(scope, "AggregateError", new Object[] {newArray});
|
||||
capability.reject.call(cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {error});
|
||||
}
|
||||
}
|
||||
|
||||
// This object keeps track of the state necessary to resolve one element in Promise.all
|
||||
// and Promise.any
|
||||
private static class PromiseElementResolver {
|
||||
|
||||
private boolean alreadyCalled = false;
|
||||
|
@ -832,5 +973,17 @@ public class NativePromise extends ScriptableObject {
|
|||
}
|
||||
return Undefined.instance;
|
||||
}
|
||||
|
||||
Object reject(Context cx, Scriptable scope, Object result, PromiseAnyRejector rejector) {
|
||||
if (alreadyCalled) {
|
||||
return Undefined.instance;
|
||||
}
|
||||
alreadyCalled = true;
|
||||
rejector.errors.set(index, result);
|
||||
if (--rejector.remainingElements == 0) {
|
||||
rejector.finalRejection(cx, scope);
|
||||
}
|
||||
return Undefined.instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ public class TopLevel extends IdScriptableObject {
|
|||
|
||||
/** An enumeration of built-in native errors. [ECMAScript 5 - 15.11.6] */
|
||||
enum NativeErrors {
|
||||
/** The AggregateError */
|
||||
AggregateError,
|
||||
/** Basic Error */
|
||||
Error,
|
||||
/** The native EvalError. */
|
||||
|
|
|
@ -967,5 +967,8 @@ msg.promise.capability.state =\
|
|||
msg.promise.all.toobig =\
|
||||
Too many inputs to Promise.all
|
||||
|
||||
msg.promise.any.toobig =\
|
||||
Too many inputs to Promise.any
|
||||
|
||||
msg.typed.array.ctor.incompatible = \
|
||||
Method %TypedArray%.prototype.{0} called on incompatible receiver
|
||||
|
|
|
@ -1050,9 +1050,14 @@ built-ins/Math 51/326 (15.64%)
|
|||
|
||||
built-ins/NaN 0/6 (0.0%)
|
||||
|
||||
built-ins/NativeErrors 43/117 (36.75%)
|
||||
AggregateError/prototype 6/6 (100.0%)
|
||||
AggregateError 19/19 (100.0%)
|
||||
built-ins/NativeErrors 25/117 (21.37%)
|
||||
AggregateError/errors-iterabletolist-failures.js
|
||||
AggregateError/is-a-constructor.js {unsupported: [Reflect.construct]}
|
||||
AggregateError/message-tostring-abrupt.js
|
||||
AggregateError/message-tostring-abrupt-symbol.js
|
||||
AggregateError/newtarget-proto-custom.js {unsupported: [Reflect.construct]}
|
||||
AggregateError/newtarget-proto-fallback.js
|
||||
AggregateError/proto-from-ctor-realm.js {unsupported: [Reflect]}
|
||||
EvalError/prototype/not-error-object.js
|
||||
EvalError/is-a-constructor.js {unsupported: [Reflect.construct]}
|
||||
EvalError/proto-from-ctor-realm.js {unsupported: [Reflect]}
|
||||
|
@ -1098,7 +1103,7 @@ built-ins/Number 24/335 (7.16%)
|
|||
S9.3.1_A3_T1_U180E.js {unsupported: [u180e]}
|
||||
S9.3.1_A3_T2_U180E.js {unsupported: [u180e]}
|
||||
|
||||
built-ins/Object 218/3403 (6.41%)
|
||||
built-ins/Object 217/3403 (6.38%)
|
||||
assign/assignment-to-readonly-property-of-target-must-throw-a-typeerror-exception.js
|
||||
assign/not-a-constructor.js {unsupported: [Reflect.construct]}
|
||||
assign/source-own-prop-desc-missing.js {unsupported: [Proxy]}
|
||||
|
@ -1282,7 +1287,6 @@ built-ins/Object 218/3403 (6.41%)
|
|||
seal/not-a-constructor.js {unsupported: [Reflect.construct]}
|
||||
seal/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy, Reflect]}
|
||||
seal/proxy-with-defineProperty-handler.js {unsupported: [Proxy, Reflect]}
|
||||
seal/seal-aggregateerror.js
|
||||
seal/seal-asyncarrowfunction.js
|
||||
seal/seal-asyncfunction.js
|
||||
seal/seal-asyncgeneratorfunction.js
|
||||
|
@ -1304,7 +1308,7 @@ built-ins/Object 218/3403 (6.41%)
|
|||
proto-from-ctor-realm.js {unsupported: [Reflect]}
|
||||
subclass-object-arg.js {unsupported: [Reflect.construct, Reflect, class]}
|
||||
|
||||
built-ins/Promise 429/631 (67.99%)
|
||||
built-ins/Promise 406/631 (64.34%)
|
||||
allSettled/capability-resolve-throws-reject.js {unsupported: [async]}
|
||||
allSettled/ctx-ctor.js {unsupported: [class]}
|
||||
allSettled/does-not-invoke-array-setters.js {unsupported: [async]}
|
||||
|
@ -1426,16 +1430,10 @@ built-ins/Promise 429/631 (67.99%)
|
|||
all/S25.4.4.1_A8.1_T1.js {unsupported: [async]}
|
||||
all/S25.4.4.1_A8.2_T1.js {unsupported: [async]}
|
||||
all/S25.4.4.1_A8.2_T2.js {unsupported: [async]}
|
||||
any/call-reject-element-after-return.js
|
||||
any/call-reject-element-items.js
|
||||
any/capability-executor-called-twice.js
|
||||
any/capability-executor-not-callable.js
|
||||
any/capability-reject-throws-no-close.js {unsupported: [async]}
|
||||
any/capability-resolve-throws-no-close.js {unsupported: [async]}
|
||||
any/capability-resolve-throws-reject.js {unsupported: [async]}
|
||||
any/ctx-ctor.js {unsupported: [class]}
|
||||
any/ctx-ctor-throws.js
|
||||
any/ctx-non-ctor.js
|
||||
any/invoke-resolve.js {unsupported: [async]}
|
||||
any/invoke-resolve-error-close.js {unsupported: [async]}
|
||||
any/invoke-resolve-error-reject.js {unsupported: [async]}
|
||||
|
@ -1447,14 +1445,12 @@ built-ins/Promise 429/631 (67.99%)
|
|||
any/invoke-resolve-on-promises-every-iteration-of-promise.js {unsupported: [async]}
|
||||
any/invoke-resolve-on-values-every-iteration-of-custom.js {unsupported: [class, async]}
|
||||
any/invoke-resolve-on-values-every-iteration-of-promise.js {unsupported: [async]}
|
||||
any/invoke-resolve-return.js
|
||||
any/invoke-then.js {unsupported: [async]}
|
||||
any/invoke-then-error-close.js {unsupported: [async]}
|
||||
any/invoke-then-error-reject.js {unsupported: [async]}
|
||||
any/invoke-then-get-error-close.js {unsupported: [async]}
|
||||
any/invoke-then-get-error-reject.js {unsupported: [async]}
|
||||
any/invoke-then-on-promises-every-iteration.js {unsupported: [async]}
|
||||
any/is-function.js
|
||||
any/iter-arg-is-empty-iterable-reject.js {unsupported: [async]}
|
||||
any/iter-arg-is-empty-string-reject.js {unsupported: [async]}
|
||||
any/iter-arg-is-error-object-reject.js {unsupported: [async]}
|
||||
|
@ -1484,28 +1480,15 @@ built-ins/Promise 429/631 (67.99%)
|
|||
any/iter-returns-undefined-reject.js {unsupported: [async]}
|
||||
any/iter-step-err-no-close.js {unsupported: [async]}
|
||||
any/iter-step-err-reject.js {unsupported: [async]}
|
||||
any/length.js
|
||||
any/name.js
|
||||
any/new-reject-function.js
|
||||
any/not-a-constructor.js {unsupported: [Reflect.construct]}
|
||||
any/prop-desc.js
|
||||
any/reject-all-mixed.js {unsupported: [async]}
|
||||
any/reject-deferred.js {unsupported: [async]}
|
||||
any/reject-element-function-extensible.js
|
||||
any/reject-element-function-length.js
|
||||
any/reject-element-function-name.js
|
||||
any/reject-element-function-nonconstructor.js
|
||||
any/reject-element-function-property-order.js
|
||||
any/reject-element-function-prototype.js
|
||||
any/reject-from-same-thenable.js
|
||||
any/reject-ignored-deferred.js {unsupported: [async]}
|
||||
any/reject-ignored-immed.js {unsupported: [async]}
|
||||
any/reject-immed.js {unsupported: [async]}
|
||||
any/resolve-before-loop-exit.js
|
||||
any/resolve-before-loop-exit-from-same.js
|
||||
any/resolve-from-reject-catch.js {unsupported: [async]}
|
||||
any/resolve-from-resolve-reject-catch.js {unsupported: [async]}
|
||||
any/resolve-from-same-thenable.js
|
||||
any/resolve-ignores-late-rejection.js {unsupported: [async]}
|
||||
any/resolve-ignores-late-rejection-deferred.js {unsupported: [async]}
|
||||
any/resolve-non-callable.js {unsupported: [async]}
|
||||
|
@ -1515,8 +1498,6 @@ built-ins/Promise 429/631 (67.99%)
|
|||
any/resolved-sequence-extra-ticks.js {unsupported: [async]}
|
||||
any/resolved-sequence-mixed.js {unsupported: [async]}
|
||||
any/resolved-sequence-with-rejections.js {unsupported: [async]}
|
||||
any/returns-promise.js
|
||||
any/species-get-error.js
|
||||
prototype/catch/not-a-constructor.js {unsupported: [Reflect.construct]}
|
||||
prototype/catch/S25.4.5.1_A3.1_T1.js {unsupported: [async]}
|
||||
prototype/catch/S25.4.5.1_A3.1_T2.js {unsupported: [async]}
|
||||
|
|
Загрузка…
Ссылка в новой задаче