Revert "[builtins] port Promise.all to CSA"

This reverts commit 7ef1df858a.

Reason for revert: Breaks inspector/debugger/get-possible-breakpoints-restrict-to-function: https://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20nosnap%20-%20debug/builds/13191/steps/Check/logs/get-possible-breakpoi..

Original change's description:
> [builtins] port Promise.all to CSA
> 
> Introduces CodeStubAssembler helpers for common Iterator operations
> (GetIterator, IteratorStep, IteratorClose).
> 
> Moves the Promise.all resolveElement closure and it's caller to
> builtins-promise-gen.cc.
> 
> Instead of creating an internal array (and copying its elements into a result
> array), a single JSArray is allocated, and appended with BuildAppendJSArray(),
> falling back to %CreateDataProperty(), and elements are updated in the resolve
> closure the same way. This should always be unobservable.
> 
> This CL increases the size of snapshot_blob.bin on an x64.debug build by 11.44kb
> 
> BUG=v8:5343
> R=​cbruni@chromium.org, gsathysa@chromium.org, jgruber@chromium.org
> 
> Change-Id: Id69b7f76866b29caccd97f35870154c4be85f418
> Reviewed-on: https://chromium-review.googlesource.com/497974
> Commit-Queue: Caitlin Potter <caitp@igalia.com>
> Reviewed-by: Camillo Bruni <cbruni@chromium.org>
> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#45306}

TBR=adamk@chromium.org,cbruni@chromium.org,gsathya@chromium.org,caitp@igalia.com,jgruber@chromium.org,ishell@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:5343

Change-Id: I831738003643561fa628266af2bcebbb18000e55
Reviewed-on: https://chromium-review.googlesource.com/506014
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45313}
This commit is contained in:
Clemens Hammacher 2017-05-15 17:17:31 +00:00 committed by Commit Bot
parent b99a1ba0a6
commit ae4216160d
8 changed files with 65 additions and 553 deletions

View File

@ -2140,9 +2140,6 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
InstallSpeciesGetter(promise_fun);
SimpleInstallFunction(promise_fun, "all", Builtins::kPromiseAll, 1, true,
DONT_ENUM);
SimpleInstallFunction(promise_fun, "resolve", Builtins::kPromiseResolve, 1,
true, DONT_ENUM);
@ -2225,16 +2222,6 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
info->set_length(1);
native_context()->set_promise_reject_shared_fun(*info);
}
{
Handle<Code> code =
isolate->builtins()->PromiseAllResolveElementClosure();
Handle<SharedFunctionInfo> info =
factory->NewSharedFunctionInfo(factory->empty_string(), code, false);
info->set_internal_formal_parameter_count(1);
info->set_length(1);
native_context()->set_promise_all_resolve_element_shared_fun(*info);
}
}
{ // -- R e g E x p

View File

@ -718,7 +718,6 @@ namespace internal {
TFJ(PromiseResolveClosure, 1, kValue) \
/* ES #sec-promise-reject-functions */ \
TFJ(PromiseRejectClosure, 1, kValue) \
TFJ(PromiseAllResolveElementClosure, 1, kValue) \
/* ES #sec-promise.prototype.then */ \
TFJ(PromiseThen, 2, kOnFullfilled, kOnRejected) \
/* ES #sec-promise.prototype.catch */ \
@ -738,8 +737,6 @@ namespace internal {
TFJ(PromiseCatchFinally, 1, kReason) \
TFJ(PromiseValueThunkFinally, 0) \
TFJ(PromiseThrowerFinally, 0) \
/* ES #sec-promise.all */ \
TFJ(PromiseAll, 1, kIterable) \
\
/* Proxy */ \
CPP(ProxyConstructor) \
@ -1032,7 +1029,6 @@ namespace internal {
V(AsyncGeneratorAwaitCaught) \
V(AsyncGeneratorAwaitUncaught) \
V(PerformNativePromiseThen) \
V(PromiseAll) \
V(PromiseConstructor) \
V(PromiseHandle) \
V(PromiseResolve) \

View File

@ -1799,328 +1799,5 @@ TF_BUILTIN(PerformNativePromiseThen, PromiseBuiltinsAssembler) {
Return(result_promise);
}
Node* PromiseBuiltinsAssembler::MutableHeapNumberInc(Node* number) {
Node* const value = LoadHeapNumberValue(number);
Node* const result = Float64Add(value, Float64Constant(1.0));
StoreHeapNumberValue(number, result);
return result;
}
Node* PromiseBuiltinsAssembler::MutableHeapNumberDec(Node* number) {
Node* const value = LoadHeapNumberValue(number);
Node* const result = Float64Sub(value, Float64Constant(1.0));
StoreHeapNumberValue(number, result);
return result;
}
Node* PromiseBuiltinsAssembler::PerformPromiseAll(
Node* context, Node* constructor, Node* capability, Node* iterator,
Label* if_exception, Variable* var_exception) {
Label close_iterator(this, Label::kDeferred);
#define HANDLE_EXCEPTION(__node__, __close_iterator__) \
do { \
if (if_exception != nullptr) { \
Node* const node = (__node__); \
const bool should_close_iterator = (__close_iterator__); \
if (should_close_iterator) { \
GotoIfException(node, &close_iterator, var_exception); \
} else { \
GotoIfException(node, if_exception, var_exception); \
} \
} \
} while (0)
Node* const native_context = LoadNativeContext(context);
Node* const array_map = LoadContextElement(
native_context, Context::JS_ARRAY_FAST_ELEMENTS_MAP_INDEX);
Node* const fast_iterator_result_map =
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
Node* const values_array = AllocateJSArray(FAST_ELEMENTS, array_map,
IntPtrConstant(0), SmiConstant(0));
Node* const remaining_elements =
AllocateHeapNumberWithValue(Float64Constant(1.0), MUTABLE);
VARIABLE(var_index, MachineRepresentation::kTagged, SmiConstant(0));
Node* const instrumenting = IsDebugActive();
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
Label did_set_forwarding_handler(this);
GotoIfNot(instrumenting, &did_set_forwarding_handler);
CallRuntime(Runtime::kSetProperty, context,
LoadObjectField(capability, JSPromiseCapability::kRejectOffset),
HeapConstant(factory()->promise_forwarding_handler_symbol()),
TrueConstant(), SmiConstant(STRICT));
Goto(&did_set_forwarding_handler);
BIND(&did_set_forwarding_handler);
Label loop(this, &var_index), break_loop(this);
Goto(&loop);
BIND(&loop);
{
// Let next be IteratorStep(iteratorRecord.[[Iterator]]).
// If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
// ReturnIfAbrupt(next).
Node* const next =
IteratorStep(context, iterator, &break_loop, fast_iterator_result_map,
if_exception, var_exception);
// Let nextValue be IteratorValue(next).
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to
// true.
// ReturnIfAbrupt(nextValue).
Node* const next_value = IteratorValue(
context, next, fast_iterator_result_map, if_exception, var_exception);
// Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »).
Node* const promise_resolve =
GetProperty(context, constructor, factory()->resolve_string());
HANDLE_EXCEPTION(promise_resolve, true);
Node* const next_promise = CallJS(CodeFactory::Call(isolate()), context,
promise_resolve, constructor, next_value);
HANDLE_EXCEPTION(next_promise, true);
// Let resolveElement be a new built-in function object as defined in
// Promise.all Resolve Element Functions.
Node* const resolve_context = CreatePromiseContext(
native_context,
static_cast<int>(PromiseAllResolveElementContext::kLength));
StoreContextElementNoWriteBarrier(
resolve_context,
static_cast<int>(PromiseAllResolveElementContext::kAlreadyVisitedSlot),
SmiConstant(0));
StoreContextElementNoWriteBarrier(
resolve_context,
static_cast<int>(PromiseAllResolveElementContext::kIndexSlot),
var_index.value());
StoreContextElement(
resolve_context,
static_cast<int>(PromiseAllResolveElementContext::kCapabilitySlot),
capability);
StoreContextElement(
resolve_context,
static_cast<int>(
PromiseAllResolveElementContext::kRemainingElementsSlot),
remaining_elements);
StoreContextElement(
resolve_context,
static_cast<int>(PromiseAllResolveElementContext::kValuesArraySlot),
values_array);
Node* const map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
Node* const resolve_info = LoadContextElement(
native_context, Context::PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN);
Node* const resolve =
AllocateFunctionWithMapAndContext(map, resolve_info, resolve_context);
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] + 1.
MutableHeapNumberInc(remaining_elements);
// Perform ? Invoke(nextPromise, "then", « resolveElement,
// resultCapability.[[Reject]] »).
Node* const then =
GetProperty(context, next_promise, factory()->then_string());
HANDLE_EXCEPTION(then, true);
Node* const then_call = CallJS(
CodeFactory::Call(isolate()), context, then, next_promise, resolve,
LoadObjectField(capability, JSPromiseCapability::kRejectOffset));
HANDLE_EXCEPTION(then_call, true);
// For catch prediction, mark that rejections here are semantically
// handled by the combined Promise.
Label did_set_handled_by(this);
GotoIfNot(instrumenting, &did_set_handled_by);
GotoIf(TaggedIsSmi(then_call), &did_set_handled_by);
GotoIfNot(HasInstanceType(then_call, JS_PROMISE_TYPE), &did_set_handled_by);
CallRuntime(
Runtime::kSetProperty, context, then_call,
HeapConstant(factory()->promise_handled_by_symbol()),
LoadObjectField(capability, JSPromiseCapability::kPromiseOffset),
SmiConstant(STRICT));
Goto(&did_set_handled_by);
BIND(&did_set_handled_by);
// Set index to index + 1
var_index.Bind(NumberInc(var_index.value()));
Goto(&loop);
}
Label resolve_promise(this), return_promise(this);
BIND(&close_iterator);
{
Node* exception = var_exception->value();
CSA_ASSERT(this, WordNotEqual(exception, UndefinedConstant()));
IteratorClose(context, iterator, if_exception, var_exception);
Node* const reject =
LoadObjectField(capability, JSPromiseCapability::kRejectOffset);
CallJS(CodeFactory::Call(isolate()), context, reject, UndefinedConstant(),
exception);
Goto(&return_promise);
}
BIND(&break_loop);
// Update array length
Label did_set_length(this);
// If length is not a positive Smi, it will be updated by
// %CreateDataProperty() in PromiseAllResolveElementClosure
GotoIfNot(TaggedIsPositiveSmi(var_index.value()), &did_set_length);
StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset,
var_index.value());
Goto(&did_set_length);
BIND(&did_set_length);
// Set iteratorRecord.[[Done]] to true.
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
Node* const remaining = MutableHeapNumberDec(remaining_elements);
Branch(Float64Equal(remaining, Float64Constant(0.0)), &resolve_promise,
&return_promise);
// If remainingElementsCount.[[Value]] is 0, then
// Let valuesArray be CreateArrayFromList(values).
// Perform ? Call(resultCapability.[[Resolve]], undefined,
// « valuesArray »).
BIND(&resolve_promise);
Node* const resolve =
LoadObjectField(capability, JSPromiseCapability::kResolveOffset);
Node* const resolve_call = CallJS(CodeFactory::Call(isolate()), context,
resolve, UndefinedConstant(), values_array);
HANDLE_EXCEPTION(resolve_call, false);
Goto(&return_promise);
// Return resultCapability.[[Promise]].
BIND(&return_promise);
Node* const promise =
LoadObjectField(capability, JSPromiseCapability::kPromiseOffset);
return promise;
#undef HANDLE_EXCEPTION
}
// ES#sec-promise.all
// Promise.all ( iterable )
TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) {
// Let C be the this value.
// If Type(C) is not Object, throw a TypeError exception.
Node* const receiver = Parameter(Descriptor::kReceiver);
Node* const context = Parameter(Descriptor::kContext);
ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
"Promise.all");
// Let promiseCapability be ? NewPromiseCapability(C).
// Don't fire debugEvent so that forwarding the rejection through all does not
// trigger redundant ExceptionEvents
Node* const debug_event = FalseConstant();
Node* const capability = NewPromiseCapability(context, receiver, debug_event);
VARIABLE(var_exception, MachineRepresentation::kTagged, UndefinedConstant());
Label reject_promise(this, &var_exception, Label::kDeferred);
// Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability).
Node* const iterable = Parameter(Descriptor::kIterable);
Node* const iterator =
GetIterator(context, iterable, &reject_promise, &var_exception);
// Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
// If result is an abrupt completion, then
// If iteratorRecord.[[Done]] is false, let result be
// IteratorClose(iterator, result).
// IfAbruptRejectPromise(result, promiseCapability).
Node* const result = PerformPromiseAll(
context, receiver, capability, iterator, &reject_promise, &var_exception);
Return(result);
BIND(&reject_promise);
{
Node* const reject =
LoadObjectField(capability, JSPromiseCapability::kRejectOffset);
Callable callable = CodeFactory::Call(isolate());
CallJS(callable, context, reject, UndefinedConstant(),
var_exception.value());
Node* const promise =
LoadObjectField(capability, JSPromiseCapability::kPromiseOffset);
Return(promise);
}
}
TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) {
Node* const value = Parameter(Descriptor::kValue);
Node* const context = Parameter(Descriptor::kContext);
CSA_ASSERT(this, SmiEqual(LoadFixedArrayBaseLength(context),
SmiConstant(static_cast<int>(
PromiseAllResolveElementContext::kLength))));
Label already_called(this), resolve_promise(this);
GotoIf(
SmiEqual(LoadContextElement(
context,
static_cast<int>(
PromiseAllResolveElementContext::kAlreadyVisitedSlot)),
SmiConstant(1)),
&already_called);
StoreContextElementNoWriteBarrier(
context,
static_cast<int>(PromiseAllResolveElementContext::kAlreadyVisitedSlot),
SmiConstant(1));
Node* const index = LoadContextElement(
context, static_cast<int>(PromiseAllResolveElementContext::kIndexSlot));
Node* const values_array = LoadContextElement(
context,
static_cast<int>(PromiseAllResolveElementContext::kValuesArraySlot));
// Set element in FixedArray
Label runtime_set_element(this), did_set_element(this);
GotoIfNot(TaggedIsPositiveSmi(index), &runtime_set_element);
{
VARIABLE(var_elements, MachineRepresentation::kTagged,
LoadElements(values_array));
PossiblyGrowElementsCapacity(SMI_PARAMETERS, FAST_ELEMENTS, values_array,
index, &var_elements, SmiConstant(1),
&runtime_set_element);
StoreFixedArrayElement(var_elements.value(), index, value,
UPDATE_WRITE_BARRIER, 0, SMI_PARAMETERS);
}
Goto(&did_set_element);
BIND(&runtime_set_element);
// New-space filled up or index too large, set element via runtime
CallRuntime(Runtime::kCreateDataProperty, context, values_array, index,
value);
Goto(&did_set_element);
BIND(&did_set_element);
Node* const remaining_elements = LoadContextElement(
context, static_cast<int>(
PromiseAllResolveElementContext::kRemainingElementsSlot));
Node* const result = MutableHeapNumberDec(remaining_elements);
GotoIf(Float64Equal(result, Float64Constant(0)), &resolve_promise);
Return(UndefinedConstant());
BIND(&resolve_promise);
Node* const capability = LoadContextElement(
context,
static_cast<int>(PromiseAllResolveElementContext::kCapabilitySlot));
Node* const resolve =
LoadObjectField(capability, JSPromiseCapability::kResolveOffset);
CallJS(CodeFactory::Call(isolate()), context, resolve, UndefinedConstant(),
values_array);
Return(UndefinedConstant());
BIND(&already_called);
Return(UndefinedConstant());
}
} // namespace internal
} // namespace v8

View File

@ -28,25 +28,6 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
kPromiseContextLength,
};
enum class PromiseAllResolveElementContext {
// Whether the resolve callback was already called.
kAlreadyVisitedSlot = Context::MIN_CONTEXT_SLOTS,
// Index into the values array
kIndexSlot,
// Remaining elements count (mutable HeapNumber)
kRemainingElementsSlot,
// Promise capability from Promise.all
kCapabilitySlot,
// Values array from Promise.all
kValuesArraySlot,
kLength
};
enum FunctionContextSlot {
kCapabilitySlot = Context::MIN_CONTEXT_SLOTS,
@ -99,10 +80,6 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
Node* NewPromiseCapability(Node* context, Node* constructor,
Node* debug_event = nullptr);
// Load heap number value, increment/decrement, and return the value component
Node* MutableHeapNumberInc(Node* number);
Node* MutableHeapNumberDec(Node* number);
protected:
void PromiseInit(Node* promise);
@ -158,10 +135,6 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
Node* CreateThrowerFunctionContext(Node* reason, Node* native_context);
Node* CreateThrowerFunction(Node* reason, Node* native_context);
Node* PerformPromiseAll(Node* context, Node* constructor, Node* capability,
Node* iterator, Label* if_exception,
Variable* var_exception);
private:
Node* AllocateJSPromise(Node* context);
};

View File

@ -4642,161 +4642,6 @@ Node* CodeStubAssembler::ToInteger(Node* context, Node* input,
return var_arg.value();
}
#define HANDLE_EXCEPTION(__node__) \
do { \
if (if_exception != nullptr) { \
Node* const node = (__node__); \
GotoIfException(node, if_exception, exception); \
} \
} while (0)
Node* CodeStubAssembler::GetIterator(Node* context, Node* object,
Label* if_exception, Variable* exception) {
Node* method = GetProperty(context, object, factory()->iterator_symbol());
HANDLE_EXCEPTION(method);
Callable callable = CodeFactory::Call(isolate());
Node* iterator = CallJS(callable, context, method, object);
HANDLE_EXCEPTION(iterator);
Label done(this), if_notobject(this, Label::kDeferred);
Branch(IsJSReceiver(iterator), &done, &if_notobject);
BIND(&if_notobject);
{
Node* ret =
CallRuntime(Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kNotAnIterator), iterator);
HANDLE_EXCEPTION(ret);
Goto(&done);
}
BIND(&done);
return iterator;
}
Node* CodeStubAssembler::IteratorStep(Node* context, Node* iterator,
Label* if_done,
Node* fast_iterator_result_map,
Label* if_exception,
Variable* exception) {
DCHECK_NOT_NULL(if_done);
Node* next_method = GetProperty(context, iterator, factory()->next_string());
HANDLE_EXCEPTION(next_method);
Callable callable = CodeFactory::Call(isolate());
Node* result = CallJS(callable, context, next_method, iterator);
HANDLE_EXCEPTION(result);
Label if_notobject(this, Label::kDeferred), return_result(this);
GotoIf(TaggedIsSmi(result), &if_notobject);
GotoIfNot(IsJSReceiver(result), &if_notobject);
auto IfFastIteratorResult = [=]() {
Node* done = LoadObjectField(result, JSIteratorResult::kDoneOffset);
CSA_ASSERT(this, IsBoolean(done));
return done;
};
auto IfGenericIteratorResult = [=]() {
Node* done = GetProperty(context, result, factory()->done_string());
HANDLE_EXCEPTION(done);
VARIABLE(var_done, MachineRepresentation::kTagged, done);
Label to_boolean(this, Label::kDeferred), return_result(this);
GotoIf(TaggedIsSmi(done), &to_boolean);
Branch(IsBoolean(done), &return_result, &to_boolean);
BIND(&to_boolean);
var_done.Bind(CallStub(CodeFactory::ToBoolean(isolate()), context, done));
Goto(&return_result);
BIND(&return_result);
return var_done.value();
};
Node* done;
if (fast_iterator_result_map != nullptr) {
Node* map = LoadMap(result);
done =
Select(WordEqual(map, fast_iterator_result_map), IfFastIteratorResult,
IfGenericIteratorResult, MachineRepresentation::kTagged);
} else {
done = IfGenericIteratorResult();
}
Goto(&return_result);
BIND(&if_notobject);
{
Node* ret =
CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context, result);
HANDLE_EXCEPTION(ret);
Goto(if_done);
}
BIND(&return_result);
GotoIf(IsTrue(done), if_done);
return result;
}
Node* CodeStubAssembler::IteratorValue(Node* context, Node* result,
Node* fast_iterator_result_map,
Label* if_exception,
Variable* exception) {
CSA_ASSERT(this, IsJSReceiver(result));
auto IfFastIteratorResult = [=]() {
return LoadObjectField(result, JSIteratorResult::kValueOffset);
};
auto IfGenericIteratorResult = [=]() {
Node* value = GetProperty(context, result, factory()->value_string());
HANDLE_EXCEPTION(value);
return value;
};
if (fast_iterator_result_map != nullptr) {
Node* map = LoadMap(result);
return Select(WordEqual(map, fast_iterator_result_map),
IfFastIteratorResult, IfGenericIteratorResult,
MachineRepresentation::kTagged);
} else {
return IfGenericIteratorResult();
}
}
void CodeStubAssembler::IteratorClose(Node* context, Node* iterator,
Label* if_exception,
Variable* exception) {
CSA_ASSERT(this, IsJSReceiver(iterator));
Node* method = GetProperty(context, iterator, factory()->return_string());
HANDLE_EXCEPTION(method);
Label done(this);
GotoIf(IsNull(method), &done);
GotoIf(IsUndefined(method), &done);
Node* inner_result =
CallJS(CodeFactory::Call(isolate()), context, method, iterator);
HANDLE_EXCEPTION(inner_result);
Label if_notobject(this, Label::kDeferred);
GotoIf(TaggedIsSmi(inner_result), &if_notobject);
Branch(IsJSReceiver(inner_result), &done, &if_notobject);
BIND(&if_notobject);
{
Node* ret = CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context,
inner_result);
HANDLE_EXCEPTION(ret);
Goto(&done);
}
BIND(&done);
}
#undef HANDLE_EXCEPTION
Node* CodeStubAssembler::DecodeWord32(Node* word32, uint32_t shift,
uint32_t mask) {
return Word32Shr(Word32And(word32, Int32Constant(mask)),

View File

@ -879,35 +879,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* ToInteger(Node* context, Node* input,
ToIntegerTruncationMode mode = kNoTruncation);
// https://tc39.github.io/ecma262/#sec-getiterator --- never used for
// @@asyncIterator.
Node* GetIterator(Node* context, Node* object, Label* if_exception = nullptr,
Variable* exception = nullptr);
// https://tc39.github.io/ecma262/#sec-iteratorstep
// Returns `false` if the iterator is done, otherwise returns an
// iterator result.
// `fast_iterator_result_map` refers to the map for the JSIteratorResult
// object, loaded from the native context.
Node* IteratorStep(Node* context, Node* iterator, Label* if_done,
Node* fast_iterator_result_map = nullptr,
Label* if_exception = nullptr,
Variable* exception = nullptr);
// https://tc39.github.io/ecma262/#sec-iteratorvalue
// Return the `value` field from an iterator.
// `fast_iterator_result_map` refers to the map for the JSIteratorResult
// object, loaded from the native context.
Node* IteratorValue(Node* context, Node* result,
Node* fast_iterator_result_map = nullptr,
Label* if_exception = nullptr,
Variable* exception = nullptr);
// https://tc39.github.io/ecma262/#sec-iteratorclose
void IteratorClose(Node* context, Node* iterator,
Label* if_exception = nullptr,
Variable* exception = nullptr);
// Returns a node that contains a decoded (unsigned!) value of a bit
// field |T| in |word32|. Returns result as an uint32 node.
template <typename T>

View File

@ -320,8 +320,6 @@ enum ContextLookupFlags {
promise_value_thunk_finally_shared_fun) \
V(PROMISE_THROWER_FINALLY_SHARED_FUN, SharedFunctionInfo, \
promise_thrower_finally_shared_fun) \
V(PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN, SharedFunctionInfo, \
promise_all_resolve_element_shared_fun) \
V(PROMISE_PROTOTYPE_MAP_INDEX, Map, promise_prototype_map) \
V(REGEXP_EXEC_FUNCTION_INDEX, JSFunction, regexp_exec_function) \
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \

View File

@ -23,6 +23,70 @@ var GlobalPromise = global.Promise;
// Combinators.
// ES#sec-promise.all
// Promise.all ( iterable )
function PromiseAll(iterable) {
if (!IS_RECEIVER(this)) {
throw %make_type_error(kCalledOnNonObject, "Promise.all");
}
// false debugEvent so that forwarding the rejection through all does not
// trigger redundant ExceptionEvents
var deferred = %new_promise_capability(this, false);
var resolutions = new InternalArray();
var count;
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
var instrumenting = DEBUG_IS_ACTIVE;
if (instrumenting) {
SET_PRIVATE(deferred.reject, promiseForwardingHandlerSymbol, true);
}
function CreateResolveElementFunction(index, values, promiseCapability) {
var alreadyCalled = false;
return (x) => {
if (alreadyCalled === true) return;
alreadyCalled = true;
values[index] = x;
if (--count === 0) {
var valuesArray = [];
%MoveArrayContents(values, valuesArray);
%_Call(promiseCapability.resolve, UNDEFINED, valuesArray);
}
};
}
try {
var i = 0;
count = 1;
for (var value of iterable) {
var nextPromise = this.resolve(value);
++count;
var throwawayPromise = nextPromise.then(
CreateResolveElementFunction(i, resolutions, deferred),
deferred.reject);
// For catch prediction, mark that rejections here are semantically
// handled by the combined Promise.
if (instrumenting && %is_promise(throwawayPromise)) {
SET_PRIVATE(throwawayPromise, promiseHandledBySymbol, deferred.promise);
}
++i;
}
// 6.d
if (--count === 0) {
var valuesArray = [];
%MoveArrayContents(resolutions, valuesArray);
%_Call(deferred.resolve, UNDEFINED, valuesArray);
}
} catch (e) {
%_Call(deferred.reject, UNDEFINED, e);
}
return deferred.promise;
}
// ES#sec-promise.race
// Promise.race ( iterable )
function PromiseRace(iterable) {
@ -61,6 +125,7 @@ function PromiseRace(iterable) {
// Install exported functions.
utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
"all", PromiseAll,
"race", PromiseRace,
]);