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:
parent
b99a1ba0a6
commit
ae4216160d
@ -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
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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)),
|
||||
|
@ -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>
|
||||
|
@ -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) \
|
||||
|
@ -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,
|
||||
]);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user