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:
parent
b267efc706
commit
8ada753888
@ -2140,6 +2140,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
|||||||
|
|
||||||
InstallSpeciesGetter(promise_fun);
|
InstallSpeciesGetter(promise_fun);
|
||||||
|
|
||||||
|
SimpleInstallFunction(promise_fun, "all", Builtins::kPromiseAll, 1, true,
|
||||||
|
DONT_ENUM);
|
||||||
|
|
||||||
SimpleInstallFunction(promise_fun, "resolve", Builtins::kPromiseResolve, 1,
|
SimpleInstallFunction(promise_fun, "resolve", Builtins::kPromiseResolve, 1,
|
||||||
true, DONT_ENUM);
|
true, DONT_ENUM);
|
||||||
|
|
||||||
@ -2222,6 +2225,16 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
|||||||
info->set_length(1);
|
info->set_length(1);
|
||||||
native_context()->set_promise_reject_shared_fun(*info);
|
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
|
{ // -- R e g E x p
|
||||||
|
@ -159,7 +159,7 @@ TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) {
|
|||||||
Node* const fast_iterator_result_map =
|
Node* const fast_iterator_result_map =
|
||||||
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
|
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);
|
Label loop(this), if_notobject(this), if_exception(this);
|
||||||
Goto(&loop);
|
Goto(&loop);
|
||||||
@ -199,7 +199,8 @@ TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) {
|
|||||||
|
|
||||||
BIND(&if_exception);
|
BIND(&if_exception);
|
||||||
{
|
{
|
||||||
iterator_assembler.IteratorClose(context, iterator, var_exception.value());
|
iterator_assembler.IteratorCloseOnException(context, iterator,
|
||||||
|
&var_exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&if_notcallable);
|
BIND(&if_notcallable);
|
||||||
@ -285,7 +286,7 @@ TF_BUILTIN(SetConstructor, CollectionsBuiltinsAssembler) {
|
|||||||
Node* const fast_iterator_result_map =
|
Node* const fast_iterator_result_map =
|
||||||
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
|
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);
|
Label loop(this), if_notobject(this), if_exception(this);
|
||||||
Goto(&loop);
|
Goto(&loop);
|
||||||
@ -307,7 +308,8 @@ TF_BUILTIN(SetConstructor, CollectionsBuiltinsAssembler) {
|
|||||||
|
|
||||||
BIND(&if_exception);
|
BIND(&if_exception);
|
||||||
{
|
{
|
||||||
iterator_assembler.IteratorClose(context, iterator, var_exception.value());
|
iterator_assembler.IteratorCloseOnException(context, iterator,
|
||||||
|
&var_exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&if_notcallable);
|
BIND(&if_notcallable);
|
||||||
|
@ -772,6 +772,7 @@ namespace internal {
|
|||||||
TFJ(PromiseResolveClosure, 1, kValue) \
|
TFJ(PromiseResolveClosure, 1, kValue) \
|
||||||
/* ES #sec-promise-reject-functions */ \
|
/* ES #sec-promise-reject-functions */ \
|
||||||
TFJ(PromiseRejectClosure, 1, kValue) \
|
TFJ(PromiseRejectClosure, 1, kValue) \
|
||||||
|
TFJ(PromiseAllResolveElementClosure, 1, kValue) \
|
||||||
/* ES #sec-promise.prototype.then */ \
|
/* ES #sec-promise.prototype.then */ \
|
||||||
TFJ(PromiseThen, 2, kOnFullfilled, kOnRejected) \
|
TFJ(PromiseThen, 2, kOnFullfilled, kOnRejected) \
|
||||||
/* ES #sec-promise.prototype.catch */ \
|
/* ES #sec-promise.prototype.catch */ \
|
||||||
@ -791,6 +792,8 @@ namespace internal {
|
|||||||
TFJ(PromiseCatchFinally, 1, kReason) \
|
TFJ(PromiseCatchFinally, 1, kReason) \
|
||||||
TFJ(PromiseValueThunkFinally, 0) \
|
TFJ(PromiseValueThunkFinally, 0) \
|
||||||
TFJ(PromiseThrowerFinally, 0) \
|
TFJ(PromiseThrowerFinally, 0) \
|
||||||
|
/* ES #sec-promise.all */ \
|
||||||
|
TFJ(PromiseAll, 1, kIterable) \
|
||||||
\
|
\
|
||||||
/* Proxy */ \
|
/* Proxy */ \
|
||||||
CPP(ProxyConstructor) \
|
CPP(ProxyConstructor) \
|
||||||
@ -1098,6 +1101,7 @@ namespace internal {
|
|||||||
V(AsyncGeneratorAwaitCaught) \
|
V(AsyncGeneratorAwaitCaught) \
|
||||||
V(AsyncGeneratorAwaitUncaught) \
|
V(AsyncGeneratorAwaitUncaught) \
|
||||||
V(PerformNativePromiseThen) \
|
V(PerformNativePromiseThen) \
|
||||||
|
V(PromiseAll) \
|
||||||
V(PromiseConstructor) \
|
V(PromiseConstructor) \
|
||||||
V(PromiseHandle) \
|
V(PromiseHandle) \
|
||||||
V(PromiseResolve) \
|
V(PromiseResolve) \
|
||||||
|
@ -9,11 +9,15 @@ namespace internal {
|
|||||||
|
|
||||||
using compiler::Node;
|
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());
|
Node* method = GetProperty(context, object, factory()->iterator_symbol());
|
||||||
|
GotoIfException(method, if_exception, exception);
|
||||||
|
|
||||||
Callable callable = CodeFactory::Call(isolate());
|
Callable callable = CodeFactory::Call(isolate());
|
||||||
Node* iterator = CallJS(callable, context, method, object);
|
Node* iterator = CallJS(callable, context, method, object);
|
||||||
|
GotoIfException(iterator, if_exception, exception);
|
||||||
|
|
||||||
Label done(this), if_notobject(this, Label::kDeferred);
|
Label done(this), if_notobject(this, Label::kDeferred);
|
||||||
GotoIf(TaggedIsSmi(iterator), &if_notobject);
|
GotoIf(TaggedIsSmi(iterator), &if_notobject);
|
||||||
@ -21,8 +25,10 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object) {
|
|||||||
|
|
||||||
BIND(&if_notobject);
|
BIND(&if_notobject);
|
||||||
{
|
{
|
||||||
CallRuntime(Runtime::kThrowTypeError, context,
|
Node* ret =
|
||||||
SmiConstant(MessageTemplate::kNotAnIterator), iterator);
|
CallRuntime(Runtime::kThrowTypeError, context,
|
||||||
|
SmiConstant(MessageTemplate::kNotAnIterator), iterator);
|
||||||
|
GotoIfException(ret, if_exception, exception);
|
||||||
Unreachable();
|
Unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,25 +38,31 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object) {
|
|||||||
|
|
||||||
Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator,
|
Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator,
|
||||||
Label* if_done,
|
Label* if_done,
|
||||||
Node* fast_iterator_result_map) {
|
Node* fast_iterator_result_map,
|
||||||
|
Label* if_exception,
|
||||||
|
Variable* exception) {
|
||||||
DCHECK_NOT_NULL(if_done);
|
DCHECK_NOT_NULL(if_done);
|
||||||
|
|
||||||
// IteratorNext
|
// IteratorNext
|
||||||
Node* next_method = GetProperty(context, iterator, factory()->next_string());
|
Node* next_method = GetProperty(context, iterator, factory()->next_string());
|
||||||
|
GotoIfException(next_method, if_exception, exception);
|
||||||
|
|
||||||
// 1. a. Let result be ? Invoke(iterator, "next", « »).
|
// 1. a. Let result be ? Invoke(iterator, "next", « »).
|
||||||
Callable callable = CodeFactory::Call(isolate());
|
Callable callable = CodeFactory::Call(isolate());
|
||||||
Node* result = CallJS(callable, context, next_method, iterator);
|
Node* result = CallJS(callable, context, next_method, iterator);
|
||||||
|
GotoIfException(result, if_exception, exception);
|
||||||
|
|
||||||
// 3. If Type(result) is not Object, throw a TypeError exception.
|
// 3. If Type(result) is not Object, throw a TypeError exception.
|
||||||
Label if_notobject(this, Label::kDeferred), return_result(this);
|
Label if_notobject(this, Label::kDeferred), return_result(this);
|
||||||
GotoIf(TaggedIsSmi(result), &if_notobject);
|
GotoIf(TaggedIsSmi(result), &if_notobject);
|
||||||
GotoIfNot(IsJSReceiver(result), &if_notobject);
|
GotoIfNot(IsJSReceiver(result), &if_notobject);
|
||||||
|
|
||||||
Label if_generic(this);
|
|
||||||
VARIABLE(var_done, MachineRepresentation::kTagged);
|
VARIABLE(var_done, MachineRepresentation::kTagged);
|
||||||
|
|
||||||
if (fast_iterator_result_map != nullptr) {
|
if (fast_iterator_result_map != nullptr) {
|
||||||
|
// Fast iterator result case:
|
||||||
|
Label if_generic(this);
|
||||||
|
|
||||||
// 4. Return result.
|
// 4. Return result.
|
||||||
Node* map = LoadMap(result);
|
Node* map = LoadMap(result);
|
||||||
GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
|
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));
|
CSA_ASSERT(this, IsBoolean(done));
|
||||||
var_done.Bind(done);
|
var_done.Bind(done);
|
||||||
Goto(&return_result);
|
Goto(&return_result);
|
||||||
} else {
|
|
||||||
Goto(&if_generic);
|
BIND(&if_generic);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&if_generic);
|
// Generic iterator result case:
|
||||||
{
|
{
|
||||||
// IteratorComplete
|
// IteratorComplete
|
||||||
// 2. Return ToBoolean(? Get(iterResult, "done")).
|
// 2. Return ToBoolean(? Get(iterResult, "done")).
|
||||||
Node* done = GetProperty(context, result, factory()->done_string());
|
Node* done = GetProperty(context, result, factory()->done_string());
|
||||||
|
GotoIfException(done, if_exception, exception);
|
||||||
var_done.Bind(done);
|
var_done.Bind(done);
|
||||||
|
|
||||||
Label to_boolean(this, Label::kDeferred);
|
Label to_boolean(this, Label::kDeferred);
|
||||||
@ -83,8 +96,10 @@ Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator,
|
|||||||
|
|
||||||
BIND(&if_notobject);
|
BIND(&if_notobject);
|
||||||
{
|
{
|
||||||
CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context, result);
|
Node* ret =
|
||||||
Goto(if_done);
|
CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context, result);
|
||||||
|
GotoIfException(ret, if_exception, exception);
|
||||||
|
Unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&return_result);
|
BIND(&return_result);
|
||||||
@ -93,23 +108,28 @@ Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
|
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));
|
CSA_ASSERT(this, IsJSReceiver(result));
|
||||||
|
|
||||||
Label exit(this), if_generic(this);
|
Label exit(this);
|
||||||
VARIABLE(var_value, MachineRepresentation::kTagged);
|
VARIABLE(var_value, MachineRepresentation::kTagged);
|
||||||
if (fast_iterator_result_map != nullptr) {
|
if (fast_iterator_result_map != nullptr) {
|
||||||
|
// Fast iterator result case:
|
||||||
|
Label if_generic(this);
|
||||||
Node* map = LoadMap(result);
|
Node* map = LoadMap(result);
|
||||||
GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
|
GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
|
||||||
var_value.Bind(LoadObjectField(result, JSIteratorResult::kValueOffset));
|
var_value.Bind(LoadObjectField(result, JSIteratorResult::kValueOffset));
|
||||||
Goto(&exit);
|
Goto(&exit);
|
||||||
} else {
|
|
||||||
Goto(&if_generic);
|
BIND(&if_generic);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&if_generic);
|
// Generic iterator result case:
|
||||||
{
|
{
|
||||||
Node* value = GetProperty(context, result, factory()->value_string());
|
Node* value = GetProperty(context, result, factory()->value_string());
|
||||||
|
GotoIfException(value, if_exception, exception);
|
||||||
var_value.Bind(value);
|
var_value.Bind(value);
|
||||||
Goto(&exit);
|
Goto(&exit);
|
||||||
}
|
}
|
||||||
@ -118,46 +138,46 @@ Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
|
|||||||
return var_value.value();
|
return var_value.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IteratorBuiltinsAssembler::IteratorClose(Node* context, Node* iterator,
|
void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
|
||||||
Node* exception) {
|
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));
|
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());
|
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);
|
||||||
|
|
||||||
Node* inner_result =
|
|
||||||
CallJS(CodeFactory::Call(isolate()), context, method, iterator);
|
|
||||||
|
|
||||||
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,
|
// Let innerResult be Call(return, iterator, « »).
|
||||||
inner_result);
|
// If an exception occurs, the original exception remains bound
|
||||||
Unreachable();
|
Node* inner_result =
|
||||||
}
|
CallJS(CodeFactory::Call(isolate()), context, method, iterator);
|
||||||
|
GotoIfException(inner_result, if_exception, nullptr);
|
||||||
|
|
||||||
BIND(&if_iter_exception);
|
// (If completion.[[Type]] is throw) return Completion(completion).
|
||||||
{
|
Goto(if_exception);
|
||||||
GotoIfNot(IsUndefined(exception), &rethrow_exception);
|
|
||||||
CallRuntime(Runtime::kReThrow, context, var_iter_exception.value());
|
|
||||||
Unreachable();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BIND(&rethrow_exception);
|
void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
|
||||||
{
|
Node* iterator,
|
||||||
CallRuntime(Runtime::kReThrow, context, exception);
|
Variable* exception) {
|
||||||
Unreachable();
|
Label rethrow(this, Label::kDeferred);
|
||||||
}
|
IteratorCloseOnException(context, iterator, &rethrow, exception);
|
||||||
|
|
||||||
|
BIND(&rethrow);
|
||||||
|
CallRuntime(Runtime::kReThrow, context, exception->value());
|
||||||
|
Unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -16,7 +16,8 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
|
|
||||||
// https://tc39.github.io/ecma262/#sec-getiterator --- never used for
|
// https://tc39.github.io/ecma262/#sec-getiterator --- never used for
|
||||||
// @@asyncIterator.
|
// @@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
|
// https://tc39.github.io/ecma262/#sec-iteratorstep
|
||||||
// Returns `false` if the iterator is done, otherwise returns an
|
// 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
|
// `fast_iterator_result_map` refers to the map for the JSIteratorResult
|
||||||
// object, loaded from the native context.
|
// object, loaded from the native context.
|
||||||
Node* IteratorStep(Node* context, Node* iterator, Label* if_done,
|
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
|
// https://tc39.github.io/ecma262/#sec-iteratorvalue
|
||||||
// Return the `value` field from an iterator.
|
// Return the `value` field from an iterator.
|
||||||
// `fast_iterator_result_map` refers to the map for the JSIteratorResult
|
// `fast_iterator_result_map` refers to the map for the JSIteratorResult
|
||||||
// object, loaded from the native context.
|
// object, loaded from the native context.
|
||||||
Node* IteratorValue(Node* context, Node* result,
|
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
|
// 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
|
} // namespace internal
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "src/builtins/builtins-promise-gen.h"
|
#include "src/builtins/builtins-promise-gen.h"
|
||||||
|
|
||||||
#include "src/builtins/builtins-constructor-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-utils-gen.h"
|
||||||
#include "src/builtins/builtins.h"
|
#include "src/builtins/builtins.h"
|
||||||
#include "src/code-factory.h"
|
#include "src/code-factory.h"
|
||||||
@ -208,7 +209,7 @@ Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context,
|
|||||||
int slots) {
|
int slots) {
|
||||||
DCHECK_GE(slots, Context::MIN_CONTEXT_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);
|
StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex);
|
||||||
StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset,
|
StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset,
|
||||||
SmiConstant(slots));
|
SmiConstant(slots));
|
||||||
@ -1818,5 +1819,329 @@ TF_BUILTIN(PerformNativePromiseThen, PromiseBuiltinsAssembler) {
|
|||||||
Return(result_promise);
|
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 internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -28,6 +28,27 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
kPromiseContextLength,
|
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 {
|
enum FunctionContextSlot {
|
||||||
kCapabilitySlot = Context::MIN_CONTEXT_SLOTS,
|
kCapabilitySlot = Context::MIN_CONTEXT_SLOTS,
|
||||||
|
|
||||||
@ -135,6 +156,13 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
Node* CreateThrowerFunctionContext(Node* reason, Node* native_context);
|
Node* CreateThrowerFunctionContext(Node* reason, Node* native_context);
|
||||||
Node* CreateThrowerFunction(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:
|
private:
|
||||||
Node* AllocateJSPromise(Node* context);
|
Node* AllocateJSPromise(Node* context);
|
||||||
};
|
};
|
||||||
|
@ -153,9 +153,12 @@ Node* CodeStubAssembler::NoContextConstant() { return NumberConstant(0); }
|
|||||||
HEAP_CONSTANT_LIST(HEAP_CONSTANT_ACCESSOR);
|
HEAP_CONSTANT_LIST(HEAP_CONSTANT_ACCESSOR);
|
||||||
#undef HEAP_CONSTANT_ACCESSOR
|
#undef HEAP_CONSTANT_ACCESSOR
|
||||||
|
|
||||||
#define HEAP_CONSTANT_TEST(rootName, name) \
|
#define HEAP_CONSTANT_TEST(rootName, name) \
|
||||||
Node* CodeStubAssembler::Is##name(Node* value) { \
|
Node* CodeStubAssembler::Is##name(Node* value) { \
|
||||||
return WordEqual(value, name##Constant()); \
|
return WordEqual(value, name##Constant()); \
|
||||||
|
} \
|
||||||
|
Node* CodeStubAssembler::IsNot##name(Node* value) { \
|
||||||
|
return WordNotEqual(value, name##Constant()); \
|
||||||
}
|
}
|
||||||
HEAP_CONSTANT_LIST(HEAP_CONSTANT_TEST);
|
HEAP_CONSTANT_LIST(HEAP_CONSTANT_TEST);
|
||||||
#undef HEAP_CONSTANT_TEST
|
#undef HEAP_CONSTANT_TEST
|
||||||
@ -1704,6 +1707,31 @@ void CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* array,
|
|||||||
StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length);
|
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* CodeStubAssembler::AllocateHeapNumber(MutableMode mode) {
|
||||||
Node* result = Allocate(HeapNumber::kSize, kNone);
|
Node* result = Allocate(HeapNumber::kSize, kNone);
|
||||||
Heap::RootListIndex heap_map_index =
|
Heap::RootListIndex heap_map_index =
|
||||||
|
@ -152,7 +152,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
|||||||
HEAP_CONSTANT_LIST(HEAP_CONSTANT_ACCESSOR)
|
HEAP_CONSTANT_LIST(HEAP_CONSTANT_ACCESSOR)
|
||||||
#undef 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)
|
HEAP_CONSTANT_LIST(HEAP_CONSTANT_TEST)
|
||||||
#undef 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,
|
void StoreFieldsNoWriteBarrier(Node* start_address, Node* end_address,
|
||||||
Node* value);
|
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.
|
// Allocate a HeapNumber without initializing its value.
|
||||||
Node* AllocateHeapNumber(MutableMode mode = IMMUTABLE);
|
Node* AllocateHeapNumber(MutableMode mode = IMMUTABLE);
|
||||||
// Allocate a HeapNumber with a specific value.
|
// Allocate a HeapNumber with a specific value.
|
||||||
@ -1452,6 +1465,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
|||||||
UndefinedConstant(), SmiConstant(message), args...);
|
UndefinedConstant(), SmiConstant(message), args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Abort(BailoutReason reason) {
|
||||||
|
CallRuntime(Runtime::kAbort, NoContextConstant(), SmiConstant(reason));
|
||||||
|
Unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void DescriptorLookup(Node* unique_name, Node* descriptors, Node* bitfield3,
|
void DescriptorLookup(Node* unique_name, Node* descriptors, Node* bitfield3,
|
||||||
Label* if_found, Variable* var_name_index,
|
Label* if_found, Variable* var_name_index,
|
||||||
|
@ -554,10 +554,16 @@ Node* CodeAssembler::Projection(int index, Node* value) {
|
|||||||
|
|
||||||
void CodeAssembler::GotoIfException(Node* node, Label* if_exception,
|
void CodeAssembler::GotoIfException(Node* node, Label* if_exception,
|
||||||
Variable* exception_var) {
|
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);
|
Label success(this), exception(this, Label::kDeferred);
|
||||||
success.MergeVariables();
|
success.MergeVariables();
|
||||||
exception.MergeVariables();
|
exception.MergeVariables();
|
||||||
DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
|
|
||||||
|
|
||||||
raw_assembler()->Continuations(node, success.label_, exception.label_);
|
raw_assembler()->Continuations(node, success.label_, exception.label_);
|
||||||
|
|
||||||
|
@ -321,6 +321,8 @@ enum ContextLookupFlags {
|
|||||||
promise_value_thunk_finally_shared_fun) \
|
promise_value_thunk_finally_shared_fun) \
|
||||||
V(PROMISE_THROWER_FINALLY_SHARED_FUN, SharedFunctionInfo, \
|
V(PROMISE_THROWER_FINALLY_SHARED_FUN, SharedFunctionInfo, \
|
||||||
promise_thrower_finally_shared_fun) \
|
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(PROMISE_PROTOTYPE_MAP_INDEX, Map, promise_prototype_map) \
|
||||||
V(REGEXP_EXEC_FUNCTION_INDEX, JSFunction, regexp_exec_function) \
|
V(REGEXP_EXEC_FUNCTION_INDEX, JSFunction, regexp_exec_function) \
|
||||||
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
|
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
|
||||||
|
@ -40,6 +40,10 @@ void StaticNewSpaceVisitor<StaticVisitor>::Initialize() {
|
|||||||
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
|
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
|
||||||
int>::Visit);
|
int>::Visit);
|
||||||
|
|
||||||
|
table_.Register(
|
||||||
|
kVisitCell,
|
||||||
|
&FixedBodyVisitor<StaticVisitor, Cell::BodyDescriptor, int>::Visit);
|
||||||
|
|
||||||
table_.Register(
|
table_.Register(
|
||||||
kVisitSymbol,
|
kVisitSymbol,
|
||||||
&FixedBodyVisitor<StaticVisitor, Symbol::BodyDescriptor, int>::Visit);
|
&FixedBodyVisitor<StaticVisitor, Symbol::BodyDescriptor, int>::Visit);
|
||||||
|
@ -32,6 +32,10 @@ class ScavengingVisitor : public StaticVisitorBase {
|
|||||||
table_.Register(kVisitSeqOneByteString, &EvacuateSeqOneByteString);
|
table_.Register(kVisitSeqOneByteString, &EvacuateSeqOneByteString);
|
||||||
table_.Register(kVisitSeqTwoByteString, &EvacuateSeqTwoByteString);
|
table_.Register(kVisitSeqTwoByteString, &EvacuateSeqTwoByteString);
|
||||||
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
|
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
|
||||||
|
table_.Register(
|
||||||
|
kVisitCell,
|
||||||
|
&ObjectEvacuationStrategy<POINTER_OBJECT>::template VisitSpecialized<
|
||||||
|
Cell::kSize>);
|
||||||
table_.Register(kVisitThinString, &EvacuateThinString);
|
table_.Register(kVisitThinString, &EvacuateThinString);
|
||||||
table_.Register(kVisitByteArray, &EvacuateByteArray);
|
table_.Register(kVisitByteArray, &EvacuateByteArray);
|
||||||
table_.Register(kVisitFixedArray, &EvacuateFixedArray);
|
table_.Register(kVisitFixedArray, &EvacuateFixedArray);
|
||||||
|
@ -23,70 +23,6 @@ var GlobalPromise = global.Promise;
|
|||||||
|
|
||||||
// Combinators.
|
// 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
|
// ES#sec-promise.race
|
||||||
// Promise.race ( iterable )
|
// Promise.race ( iterable )
|
||||||
function PromiseRace(iterable) {
|
function PromiseRace(iterable) {
|
||||||
@ -125,7 +61,6 @@ function PromiseRace(iterable) {
|
|||||||
// Install exported functions.
|
// Install exported functions.
|
||||||
|
|
||||||
utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
|
utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
|
||||||
"all", PromiseAll,
|
|
||||||
"race", PromiseRace,
|
"race", PromiseRace,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user