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

Simplifies the implementation of IteratorClose in IteratorBuiltinsAssembler, and makes clear that it is only invoked when an exception occurs. Adds exception handling support to GetIterator, IteratorStep, and IteratorCloseOnException.

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.release build
by 8.51kb

BUG=v8:5343
R=cbruni@chromium.org, gsathysa@chromium.org, jgruber@chromium.org, hpayer@chromium.org, tebbi@chromium.org

Change-Id: I29c4a529154ef49ad65555ce6ddc2c5b7c9de6b3
Reviewed-on: https://chromium-review.googlesource.com/508473
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: Hannes Payer <hpayer@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
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@{#45946}
This commit is contained in:
Caitlin Potter 2017-06-13 13:14:57 -04:00 committed by Commit Bot
parent b267efc706
commit 8ada753888
14 changed files with 522 additions and 125 deletions

View File

@ -2140,6 +2140,9 @@ 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);
@ -2222,6 +2225,16 @@ 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

@ -159,7 +159,7 @@ TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) {
Node* const fast_iterator_result_map =
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
VARIABLE(var_exception, MachineRepresentation::kTagged, UndefinedConstant());
VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
Label loop(this), if_notobject(this), if_exception(this);
Goto(&loop);
@ -199,7 +199,8 @@ TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) {
BIND(&if_exception);
{
iterator_assembler.IteratorClose(context, iterator, var_exception.value());
iterator_assembler.IteratorCloseOnException(context, iterator,
&var_exception);
}
BIND(&if_notcallable);
@ -285,7 +286,7 @@ TF_BUILTIN(SetConstructor, CollectionsBuiltinsAssembler) {
Node* const fast_iterator_result_map =
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
VARIABLE(var_exception, MachineRepresentation::kTagged, UndefinedConstant());
VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
Label loop(this), if_notobject(this), if_exception(this);
Goto(&loop);
@ -307,7 +308,8 @@ TF_BUILTIN(SetConstructor, CollectionsBuiltinsAssembler) {
BIND(&if_exception);
{
iterator_assembler.IteratorClose(context, iterator, var_exception.value());
iterator_assembler.IteratorCloseOnException(context, iterator,
&var_exception);
}
BIND(&if_notcallable);

View File

@ -772,6 +772,7 @@ 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 */ \
@ -791,6 +792,8 @@ namespace internal {
TFJ(PromiseCatchFinally, 1, kReason) \
TFJ(PromiseValueThunkFinally, 0) \
TFJ(PromiseThrowerFinally, 0) \
/* ES #sec-promise.all */ \
TFJ(PromiseAll, 1, kIterable) \
\
/* Proxy */ \
CPP(ProxyConstructor) \
@ -1098,6 +1101,7 @@ namespace internal {
V(AsyncGeneratorAwaitCaught) \
V(AsyncGeneratorAwaitUncaught) \
V(PerformNativePromiseThen) \
V(PromiseAll) \
V(PromiseConstructor) \
V(PromiseHandle) \
V(PromiseResolve) \

View File

@ -9,11 +9,15 @@ namespace internal {
using compiler::Node;
Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object) {
Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object,
Label* if_exception,
Variable* exception) {
Node* method = GetProperty(context, object, factory()->iterator_symbol());
GotoIfException(method, if_exception, exception);
Callable callable = CodeFactory::Call(isolate());
Node* iterator = CallJS(callable, context, method, object);
GotoIfException(iterator, if_exception, exception);
Label done(this), if_notobject(this, Label::kDeferred);
GotoIf(TaggedIsSmi(iterator), &if_notobject);
@ -21,8 +25,10 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object) {
BIND(&if_notobject);
{
Node* ret =
CallRuntime(Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kNotAnIterator), iterator);
GotoIfException(ret, if_exception, exception);
Unreachable();
}
@ -32,25 +38,31 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object) {
Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator,
Label* if_done,
Node* fast_iterator_result_map) {
Node* fast_iterator_result_map,
Label* if_exception,
Variable* exception) {
DCHECK_NOT_NULL(if_done);
// IteratorNext
Node* next_method = GetProperty(context, iterator, factory()->next_string());
GotoIfException(next_method, if_exception, exception);
// 1. a. Let result be ? Invoke(iterator, "next", « »).
Callable callable = CodeFactory::Call(isolate());
Node* result = CallJS(callable, context, next_method, iterator);
GotoIfException(result, if_exception, exception);
// 3. If Type(result) is not Object, throw a TypeError exception.
Label if_notobject(this, Label::kDeferred), return_result(this);
GotoIf(TaggedIsSmi(result), &if_notobject);
GotoIfNot(IsJSReceiver(result), &if_notobject);
Label if_generic(this);
VARIABLE(var_done, MachineRepresentation::kTagged);
if (fast_iterator_result_map != nullptr) {
// Fast iterator result case:
Label if_generic(this);
// 4. Return result.
Node* map = LoadMap(result);
GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
@ -61,15 +73,16 @@ Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator,
CSA_ASSERT(this, IsBoolean(done));
var_done.Bind(done);
Goto(&return_result);
} else {
Goto(&if_generic);
}
BIND(&if_generic);
}
// Generic iterator result case:
{
// IteratorComplete
// 2. Return ToBoolean(? Get(iterResult, "done")).
Node* done = GetProperty(context, result, factory()->done_string());
GotoIfException(done, if_exception, exception);
var_done.Bind(done);
Label to_boolean(this, Label::kDeferred);
@ -83,8 +96,10 @@ Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator,
BIND(&if_notobject);
{
Node* ret =
CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context, result);
Goto(if_done);
GotoIfException(ret, if_exception, exception);
Unreachable();
}
BIND(&return_result);
@ -93,23 +108,28 @@ Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator,
}
Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
Node* fast_iterator_result_map) {
Node* fast_iterator_result_map,
Label* if_exception,
Variable* exception) {
CSA_ASSERT(this, IsJSReceiver(result));
Label exit(this), if_generic(this);
Label exit(this);
VARIABLE(var_value, MachineRepresentation::kTagged);
if (fast_iterator_result_map != nullptr) {
// Fast iterator result case:
Label if_generic(this);
Node* map = LoadMap(result);
GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
var_value.Bind(LoadObjectField(result, JSIteratorResult::kValueOffset));
Goto(&exit);
} else {
Goto(&if_generic);
}
BIND(&if_generic);
}
// Generic iterator result case:
{
Node* value = GetProperty(context, result, factory()->value_string());
GotoIfException(value, if_exception, exception);
var_value.Bind(value);
Goto(&exit);
}
@ -118,47 +138,47 @@ Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
return var_value.value();
}
void IteratorBuiltinsAssembler::IteratorClose(Node* context, Node* iterator,
Node* exception) {
void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
Node* iterator,
Label* if_exception,
Variable* exception) {
// Perform ES #sec-iteratorclose when an exception occurs. This simpler
// algorithm does not include redundant steps which are never reachable from
// the spec IteratorClose algorithm.
DCHECK_NOT_NULL(if_exception);
DCHECK_NOT_NULL(exception);
CSA_ASSERT(this, IsNotTheHole(exception->value()));
CSA_ASSERT(this, IsJSReceiver(iterator));
VARIABLE(var_iter_exception, MachineRepresentation::kTagged,
UndefinedConstant());
Label rethrow_exception(this);
// Let return be ? GetMethod(iterator, "return").
Node* method = GetProperty(context, iterator, factory()->return_string());
GotoIf(Word32Or(IsUndefined(method), IsNull(method)), &rethrow_exception);
GotoIfException(method, if_exception, exception);
Label if_iter_exception(this), if_notobject(this);
// If return is undefined, return Completion(completion).
GotoIf(Word32Or(IsUndefined(method), IsNull(method)), if_exception);
{
// Let innerResult be Call(return, iterator, « »).
// If an exception occurs, the original exception remains bound
Node* inner_result =
CallJS(CodeFactory::Call(isolate()), context, method, iterator);
GotoIfException(inner_result, if_exception, nullptr);
GotoIfException(inner_result, &if_iter_exception, &var_iter_exception);
GotoIfNot(IsUndefined(exception), &rethrow_exception);
GotoIf(TaggedIsSmi(inner_result), &if_notobject);
Branch(IsJSReceiver(inner_result), &rethrow_exception, &if_notobject);
BIND(&if_notobject);
{
CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context,
inner_result);
Unreachable();
// (If completion.[[Type]] is throw) return Completion(completion).
Goto(if_exception);
}
}
BIND(&if_iter_exception);
{
GotoIfNot(IsUndefined(exception), &rethrow_exception);
CallRuntime(Runtime::kReThrow, context, var_iter_exception.value());
Unreachable();
}
void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
Node* iterator,
Variable* exception) {
Label rethrow(this, Label::kDeferred);
IteratorCloseOnException(context, iterator, &rethrow, exception);
BIND(&rethrow_exception);
{
CallRuntime(Runtime::kReThrow, context, exception);
BIND(&rethrow);
CallRuntime(Runtime::kReThrow, context, exception->value());
Unreachable();
}
}
} // namespace internal
} // namespace v8

View File

@ -16,7 +16,8 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
// https://tc39.github.io/ecma262/#sec-getiterator --- never used for
// @@asyncIterator.
Node* GetIterator(Node* context, Node* object);
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
@ -24,17 +25,24 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
// `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);
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);
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, Node* exception);
void IteratorCloseOnException(Node* context, Node* iterator,
Label* if_exception, Variable* exception);
void IteratorCloseOnException(Node* context, Node* iterator,
Variable* exception);
};
} // namespace internal

View File

@ -5,6 +5,7 @@
#include "src/builtins/builtins-promise-gen.h"
#include "src/builtins/builtins-constructor-gen.h"
#include "src/builtins/builtins-iterator-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
@ -208,7 +209,7 @@ Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context,
int slots) {
DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS);
Node* const context = Allocate(FixedArray::SizeFor(slots));
Node* const context = AllocateInNewSpace(FixedArray::SizeFor(slots));
StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex);
StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset,
SmiConstant(slots));
@ -1818,5 +1819,329 @@ TF_BUILTIN(PerformNativePromiseThen, PromiseBuiltinsAssembler) {
Return(result_promise);
}
Node* PromiseBuiltinsAssembler::PerformPromiseAll(
Node* context, Node* constructor, Node* capability, Node* iterator,
Label* if_exception, Variable* var_exception) {
IteratorBuiltinsAssembler iter_assembler(state());
Label close_iterator(this);
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);
}
Node* const native_context = LoadNativeContext(context);
Node* const array_map = LoadContextElement(
native_context, Context::JS_ARRAY_FAST_ELEMENTS_MAP_INDEX);
Node* const values_array = AllocateJSArray(FAST_ELEMENTS, array_map,
IntPtrConstant(0), SmiConstant(0));
Node* const remaining_elements = AllocateSmiCell(1);
VARIABLE(var_index, MachineRepresentation::kTagged, SmiConstant(0));
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 fast_iterator_result_map =
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
Node* const next = iter_assembler.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 = iter_assembler.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());
GotoIfException(promise_resolve, &close_iterator, var_exception);
Node* const next_promise = CallJS(CodeFactory::Call(isolate()), context,
promise_resolve, constructor, next_value);
GotoIfException(next_promise, &close_iterator, var_exception);
// Let resolveElement be a new built-in function object as defined in
// Promise.all Resolve Element Functions.
Node* const resolve_context =
CreatePromiseContext(native_context, kPromiseAllResolveElementLength);
StoreContextElementNoWriteBarrier(
resolve_context, kPromiseAllResolveElementAlreadyVisitedSlot,
SmiConstant(0));
StoreContextElementNoWriteBarrier(
resolve_context, kPromiseAllResolveElementIndexSlot, var_index.value());
StoreContextElementNoWriteBarrier(
resolve_context, kPromiseAllResolveElementRemainingElementsSlot,
remaining_elements);
StoreContextElementNoWriteBarrier(
resolve_context, kPromiseAllResolveElementCapabilitySlot, capability);
StoreContextElementNoWriteBarrier(resolve_context,
kPromiseAllResolveElementValuesArraySlot,
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.
{
Label if_outofrange(this, Label::kDeferred), done(this);
IncrementSmiCell(remaining_elements, &if_outofrange);
Goto(&done);
BIND(&if_outofrange);
{
// If the incremented value is out of Smi range, crash.
Abort(kOffsetOutOfRange);
}
BIND(&done);
}
// Perform ? Invoke(nextPromise, "then", « resolveElement,
// resultCapability.[[Reject]] »).
Node* const then =
GetProperty(context, next_promise, factory()->then_string());
GotoIfException(then, &close_iterator, var_exception);
Node* const then_call = CallJS(
CodeFactory::Call(isolate()), context, then, next_promise, resolve,
LoadObjectField(capability, JSPromiseCapability::kRejectOffset));
GotoIfException(then_call, &close_iterator, var_exception);
// 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);
}
BIND(&close_iterator);
{
// Exception must be bound to a JS value.
CSA_ASSERT(this, IsNotTheHole(var_exception->value()));
iter_assembler.IteratorCloseOnException(context, iterator, if_exception,
var_exception);
}
BIND(&break_loop);
{
Label resolve_promise(this), return_promise(this);
// Set iteratorRecord.[[Done]] to true.
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
Node* const remaining = DecrementSmiCell(remaining_elements);
Branch(SmiEqual(remaining, SmiConstant(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);
GotoIfException(resolve_call, if_exception, var_exception);
Goto(&return_promise);
// Return resultCapability.[[Promise]].
BIND(&return_promise);
}
Node* const promise =
LoadObjectField(capability, JSPromiseCapability::kPromiseOffset);
return promise;
}
Node* PromiseBuiltinsAssembler::IncrementSmiCell(Node* cell,
Label* if_overflow) {
CSA_SLOW_ASSERT(this, HasInstanceType(cell, CELL_TYPE));
Node* value = LoadCellValue(cell);
CSA_SLOW_ASSERT(this, TaggedIsSmi(value));
if (if_overflow != nullptr) {
GotoIf(SmiEqual(value, SmiConstant(Smi::kMaxValue)), if_overflow);
}
Node* result = SmiAdd(value, SmiConstant(1));
StoreCellValue(cell, result, SKIP_WRITE_BARRIER);
return result;
}
Node* PromiseBuiltinsAssembler::DecrementSmiCell(Node* cell) {
CSA_SLOW_ASSERT(this, HasInstanceType(cell, CELL_TYPE));
Node* value = LoadCellValue(cell);
CSA_SLOW_ASSERT(this, TaggedIsSmi(value));
Node* result = SmiSub(value, SmiConstant(1));
StoreCellValue(cell, result, SKIP_WRITE_BARRIER);
return result;
}
// ES#sec-promise.all
// Promise.all ( iterable )
TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) {
IteratorBuiltinsAssembler iter_assembler(state());
// 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, TheHoleConstant());
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 = iter_assembler.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);
{
// Exception must be bound to a JS value.
CSA_SLOW_ASSERT(this, IsNotTheHole(var_exception.value()));
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(kPromiseAllResolveElementLength)));
Label already_called(this), resolve_promise(this);
GotoIf(SmiEqual(LoadContextElement(
context, kPromiseAllResolveElementAlreadyVisitedSlot),
SmiConstant(1)),
&already_called);
StoreContextElementNoWriteBarrier(
context, kPromiseAllResolveElementAlreadyVisitedSlot, SmiConstant(1));
Node* const index =
LoadContextElement(context, kPromiseAllResolveElementIndexSlot);
Node* const values_array =
LoadContextElement(context, kPromiseAllResolveElementValuesArraySlot);
// 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);
// Update array length
Label did_set_length(this);
Node* const length = LoadJSArrayLength(values_array);
GotoIfNot(TaggedIsPositiveSmi(length), &did_set_length);
Node* const new_length = SmiAdd(index, SmiConstant(1));
GotoIfNot(SmiLessThan(length, new_length), &did_set_length);
StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset,
new_length);
// Assert that valuesArray.[[Length]] is less than or equal to the
// elements backing-store length.e
CSA_SLOW_ASSERT(
this, SmiAboveOrEqual(LoadFixedArrayBaseLength(var_elements.value()),
new_length));
Goto(&did_set_length);
BIND(&did_set_length);
}
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, kPromiseAllResolveElementRemainingElementsSlot);
Node* const result = DecrementSmiCell(remaining_elements);
GotoIf(SmiEqual(result, SmiConstant(0)), &resolve_promise);
Return(UndefinedConstant());
BIND(&resolve_promise);
Node* const capability =
LoadContextElement(context, kPromiseAllResolveElementCapabilitySlot);
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,6 +28,27 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
kPromiseContextLength,
};
protected:
enum PromiseAllResolveElementContextSlots {
// Whether the resolve callback was already called.
kPromiseAllResolveElementAlreadyVisitedSlot = Context::MIN_CONTEXT_SLOTS,
// Index into the values array
kPromiseAllResolveElementIndexSlot,
// Remaining elements count (mutable HeapNumber)
kPromiseAllResolveElementRemainingElementsSlot,
// Promise capability from Promise.all
kPromiseAllResolveElementCapabilitySlot,
// Values array from Promise.all
kPromiseAllResolveElementValuesArraySlot,
kPromiseAllResolveElementLength
};
public:
enum FunctionContextSlot {
kCapabilitySlot = Context::MIN_CONTEXT_SLOTS,
@ -135,6 +156,13 @@ 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);
Node* IncrementSmiCell(Node* cell, Label* if_overflow = nullptr);
Node* DecrementSmiCell(Node* cell);
private:
Node* AllocateJSPromise(Node* context);
};

View File

@ -156,6 +156,9 @@ HEAP_CONSTANT_LIST(HEAP_CONSTANT_ACCESSOR);
#define HEAP_CONSTANT_TEST(rootName, name) \
Node* CodeStubAssembler::Is##name(Node* value) { \
return WordEqual(value, name##Constant()); \
} \
Node* CodeStubAssembler::IsNot##name(Node* value) { \
return WordNotEqual(value, name##Constant()); \
}
HEAP_CONSTANT_LIST(HEAP_CONSTANT_TEST);
#undef HEAP_CONSTANT_TEST
@ -1704,6 +1707,31 @@ void CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* array,
StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length);
}
Node* CodeStubAssembler::AllocateCellWithValue(Node* value,
WriteBarrierMode mode) {
Node* result = Allocate(Cell::kSize, kNone);
StoreMapNoWriteBarrier(result, Heap::kCellMapRootIndex);
StoreCellValue(result, value, mode);
return result;
}
Node* CodeStubAssembler::LoadCellValue(Node* cell) {
CSA_SLOW_ASSERT(this, HasInstanceType(cell, CELL_TYPE));
return LoadObjectField(cell, Cell::kValueOffset);
}
Node* CodeStubAssembler::StoreCellValue(Node* cell, Node* value,
WriteBarrierMode mode) {
CSA_SLOW_ASSERT(this, HasInstanceType(cell, CELL_TYPE));
DCHECK(mode == SKIP_WRITE_BARRIER || mode == UPDATE_WRITE_BARRIER);
if (mode == UPDATE_WRITE_BARRIER) {
return StoreObjectField(cell, Cell::kValueOffset, value);
} else {
return StoreObjectFieldNoWriteBarrier(cell, Cell::kValueOffset, value);
}
}
Node* CodeStubAssembler::AllocateHeapNumber(MutableMode mode) {
Node* result = Allocate(HeapNumber::kSize, kNone);
Heap::RootListIndex heap_map_index =

View File

@ -152,7 +152,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
HEAP_CONSTANT_LIST(HEAP_CONSTANT_ACCESSOR)
#undef HEAP_CONSTANT_ACCESSOR
#define HEAP_CONSTANT_TEST(rootName, name) Node* Is##name(Node* value);
#define HEAP_CONSTANT_TEST(rootName, name) \
Node* Is##name(Node* value); \
Node* IsNot##name(Node* value);
HEAP_CONSTANT_LIST(HEAP_CONSTANT_TEST)
#undef HEAP_CONSTANT_TEST
@ -547,6 +549,17 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
void StoreFieldsNoWriteBarrier(Node* start_address, Node* end_address,
Node* value);
Node* AllocateCellWithValue(Node* value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
Node* AllocateSmiCell(int value = 0) {
return AllocateCellWithValue(SmiConstant(value), SKIP_WRITE_BARRIER);
}
Node* LoadCellValue(Node* cell);
Node* StoreCellValue(Node* cell, Node* value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
// Allocate a HeapNumber without initializing its value.
Node* AllocateHeapNumber(MutableMode mode = IMMUTABLE);
// Allocate a HeapNumber with a specific value.
@ -1452,6 +1465,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
UndefinedConstant(), SmiConstant(message), args...);
}
void Abort(BailoutReason reason) {
CallRuntime(Runtime::kAbort, NoContextConstant(), SmiConstant(reason));
Unreachable();
}
protected:
void DescriptorLookup(Node* unique_name, Node* descriptors, Node* bitfield3,
Label* if_found, Variable* var_name_index,

View File

@ -554,10 +554,16 @@ Node* CodeAssembler::Projection(int index, Node* value) {
void CodeAssembler::GotoIfException(Node* node, Label* if_exception,
Variable* exception_var) {
DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
if (if_exception == nullptr) {
// If no handler is supplied, don't add continuations
return;
}
Label success(this), exception(this, Label::kDeferred);
success.MergeVariables();
exception.MergeVariables();
DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
raw_assembler()->Continuations(node, success.label_, exception.label_);

View File

@ -321,6 +321,8 @@ 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

@ -40,6 +40,10 @@ void StaticNewSpaceVisitor<StaticVisitor>::Initialize() {
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
int>::Visit);
table_.Register(
kVisitCell,
&FixedBodyVisitor<StaticVisitor, Cell::BodyDescriptor, int>::Visit);
table_.Register(
kVisitSymbol,
&FixedBodyVisitor<StaticVisitor, Symbol::BodyDescriptor, int>::Visit);

View File

@ -32,6 +32,10 @@ class ScavengingVisitor : public StaticVisitorBase {
table_.Register(kVisitSeqOneByteString, &EvacuateSeqOneByteString);
table_.Register(kVisitSeqTwoByteString, &EvacuateSeqTwoByteString);
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
table_.Register(
kVisitCell,
&ObjectEvacuationStrategy<POINTER_OBJECT>::template VisitSpecialized<
Cell::kSize>);
table_.Register(kVisitThinString, &EvacuateThinString);
table_.Register(kVisitByteArray, &EvacuateByteArray);
table_.Register(kVisitFixedArray, &EvacuateFixedArray);

View File

@ -23,70 +23,6 @@ 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) {
@ -125,7 +61,6 @@ function PromiseRace(iterable) {
// Install exported functions.
utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
"all", PromiseAll,
"race", PromiseRace,
]);