Commit 52ff3ae4 authored by Caitlin Potter's avatar Caitlin Potter Committed by Commit Bot

[builtins] implement RunMicrotasks pump as a code stub

- Implement RunMicrotasks in CSA to prevent a potentially large number
  of jumps between C++ and JS code while consuming te queue. Appears to
  provide a ~60% speedup in microtask-heavy code, which from limited
  testing appears to scale linearly.

  The code-stub microtask pump bails out to the old C++ microtask pump
  if it encounters a CallHandlerInfo microtask, and remains in C++ for
  the remainder of the queue (returning to the JS/stub implementation
  after the bailed out queue is exhausted).

- Add a variation of JSEntryStub which enters the new RunMicrotasks code
  stub.

- Add a new RunMicrotasks helper to Execution, which uses the
  RunMicrotasks entry stub.

Bug: 
Change-Id: I4667d4dd633d24455ea5d7cef239da0af1a7365e
Reviewed-on: https://chromium-review.googlesource.com/650486
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49842}
parent fd554885
......@@ -405,6 +405,7 @@ class HandleScopeImplementer {
call_depth_(0),
microtasks_depth_(0),
microtasks_suppressions_(0),
entered_contexts_count_(0),
entered_context_count_during_microtasks_(0),
#ifdef DEBUG
debug_microtasks_depth_(0),
......@@ -531,6 +532,7 @@ class HandleScopeImplementer {
int call_depth_;
int microtasks_depth_;
int microtasks_suppressions_;
size_t entered_contexts_count_;
size_t entered_context_count_during_microtasks_;
#ifdef DEBUG
int debug_microtasks_depth_;
......@@ -546,10 +548,25 @@ class HandleScopeImplementer {
friend class DeferredHandles;
friend class DeferredHandleScope;
friend class HandleScopeImplementerOffsets;
DISALLOW_COPY_AND_ASSIGN(HandleScopeImplementer);
};
class HandleScopeImplementerOffsets {
public:
enum Offsets {
kMicrotaskContext = offsetof(HandleScopeImplementer, microtask_context_),
kEnteredContexts = offsetof(HandleScopeImplementer, entered_contexts_),
kEnteredContextsCount =
offsetof(HandleScopeImplementer, entered_contexts_count_),
kEnteredContextCountDuringMicrotasks = offsetof(
HandleScopeImplementer, entered_context_count_during_microtasks_)
};
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(HandleScopeImplementerOffsets);
};
const int kHandleBlockSize = v8::internal::KB - 2; // fit in one page
......@@ -584,9 +601,13 @@ bool HandleScopeImplementer::HasSavedContexts() {
void HandleScopeImplementer::EnterContext(Handle<Context> context) {
entered_contexts_.push_back(*context);
entered_contexts_count_ = entered_contexts_.size();
}
void HandleScopeImplementer::LeaveContext() { entered_contexts_.pop_back(); }
void HandleScopeImplementer::LeaveContext() {
entered_contexts_.pop_back();
entered_contexts_count_ = entered_contexts_.size();
}
bool HandleScopeImplementer::LastEnteredContextWas(Handle<Context> context) {
return !entered_contexts_.empty() && entered_contexts_.back() == *context;
......
......@@ -414,6 +414,8 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
// Set up the reserved register for 0.0.
__ vmov(kDoubleRegZero, Double(0.0));
__ InitializeRootRegister();
// Get address of argv, see stm above.
// r0: code entry
// r1: function
......@@ -509,12 +511,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
// r2: receiver
// r3: argc
// r4: argv
if (type() == StackFrame::CONSTRUCT_ENTRY) {
__ Call(BUILTIN_CODE(isolate(), JSConstructEntryTrampoline),
RelocInfo::CODE_TARGET);
} else {
__ Call(BUILTIN_CODE(isolate(), JSEntryTrampoline), RelocInfo::CODE_TARGET);
}
__ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
// Unlink this frame from the handler chain.
__ PopStackHandler();
......
......@@ -517,6 +517,9 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
// Set up the reserved register for 0.0.
__ Fmov(fp_zero, 0.0);
// Initialize the root array register
__ InitializeRootRegister();
// Build an entry frame (see layout below).
StackFrame::Type marker = type();
int64_t bad_frame_pointer = -1L; // Bad frame pointer to fail if it is used.
......@@ -612,13 +615,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
// x2: receiver.
// x3: argc.
// x4: argv.
if (type() == StackFrame::CONSTRUCT_ENTRY) {
__ Call(BUILTIN_CODE(isolate(), JSConstructEntryTrampoline),
RelocInfo::CODE_TARGET);
} else {
__ Call(BUILTIN_CODE(isolate(), JSEntryTrampoline), RelocInfo::CODE_TARGET);
}
__ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
// Pop the stack handler and unlink this frame from the handler chain.
static_assert(StackHandlerConstants::kNextOffset == 0 * kPointerSize,
......
......@@ -801,6 +801,16 @@ ExternalReference ExternalReference::builtins_address(Isolate* isolate) {
return ExternalReference(isolate->builtins()->builtins_table_address());
}
ExternalReference ExternalReference::handle_scope_implementer_address(
Isolate* isolate) {
return ExternalReference(isolate->handle_scope_implementer_address());
}
ExternalReference ExternalReference::pending_microtask_count_address(
Isolate* isolate) {
return ExternalReference(isolate->pending_microtask_count_address());
}
ExternalReference ExternalReference::interpreter_dispatch_table_address(
Isolate* isolate) {
return ExternalReference(isolate->interpreter()->dispatch_table_address());
......
......@@ -824,6 +824,9 @@ class ExternalReference BASE_EMBEDDED {
// The builtins table as an external reference, used by lazy deserialization.
static ExternalReference builtins_address(Isolate* isolate);
static ExternalReference handle_scope_implementer_address(Isolate* isolate);
static ExternalReference pending_microtask_count_address(Isolate* isolate);
// One-of-a-kind references. These references are not part of a general
// pattern. This means that they have to be added to the
// ExternalReferenceTable in serialize.cc manually.
......
......@@ -2410,8 +2410,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
}
{ // Internal: PromiseHandle
Handle<JSFunction> function = SimpleCreateFunction(
isolate, factory->empty_string(), Builtins::kPromiseHandle, 5, false);
Handle<JSFunction> function =
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kPromiseHandleJS, 5, false);
native_context()->set_promise_handle(*function);
}
......
......@@ -629,8 +629,6 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
__ mov(cp, Operand(context_address));
__ ldr(cp, MemOperand(cp));
__ InitializeRootRegister();
// Push the function and the receiver onto the stack.
__ Push(r1, r2);
......
......@@ -226,6 +226,8 @@ namespace internal {
TFS(RejectNativePromise, kPromise, kValue, kDebugEvent) \
TFS(PerformNativePromiseThen, kPromise, kResolveReaction, kRejectReaction, \
kResultPromise) \
TFC(RunMicrotasks, RunMicrotasks, 1) \
TFS(PromiseResolveThenableJob, kMicrotask) \
\
/* Object property helpers */ \
TFS(HasProperty, kKey, kObject) \
......@@ -777,7 +779,9 @@ namespace internal {
/* ES #sec-fulfillpromise */ \
TFJ(ResolvePromise, 2, kPromise, kValue) \
TFS(PromiseHandleReject, kPromise, kOnReject, kException) \
TFJ(PromiseHandle, 5, kValue, kHandler, kDeferredPromise, \
TFS(PromiseHandle, kValue, kHandler, kDeferredPromise, kDeferredOnResolve, \
kDeferredOnReject) \
TFJ(PromiseHandleJS, 5, kValue, kHandler, kDeferredPromise, \
kDeferredOnResolve, kDeferredOnReject) \
/* ES #sec-promise.resolve */ \
TFJ(PromiseResolveWrapper, 1, kValue) \
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/api.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h"
......@@ -13,6 +14,9 @@
namespace v8 {
namespace internal {
template <typename T>
using TNode = compiler::TNode<T>;
// -----------------------------------------------------------------------------
// Interrupt and stack checks.
......@@ -607,5 +611,376 @@ TF_BUILTIN(SameValue, CodeStubAssembler) {
Return(FalseConstant());
}
class InternalBuiltinsAssembler : public CodeStubAssembler {
public:
explicit InternalBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
TNode<IntPtrT> GetPendingMicrotaskCount();
void SetPendingMicrotaskCount(TNode<IntPtrT> count);
TNode<FixedArray> GetMicrotaskQueue();
void SetMicrotaskQueue(TNode<FixedArray> queue);
TNode<Context> GetCurrentContext();
void SetCurrentContext(TNode<Context> context);
void EnterMicrotaskContext(TNode<Context> context);
void LeaveMicrotaskContext();
TNode<Object> GetPendingException() {
auto ref = ExternalReference(kPendingExceptionAddress, isolate());
return TNode<Object>::UncheckedCast(
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
}
void ClearPendingException() {
auto ref = ExternalReference(kPendingExceptionAddress, isolate());
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
TheHoleConstant());
}
TNode<Object> GetScheduledException() {
auto ref = ExternalReference::scheduled_exception_address(isolate());
return TNode<Object>::UncheckedCast(
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
}
void ClearScheduledException() {
auto ref = ExternalReference::scheduled_exception_address(isolate());
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
TheHoleConstant());
}
};
TNode<IntPtrT> InternalBuiltinsAssembler::GetPendingMicrotaskCount() {
auto ref = ExternalReference::pending_microtask_count_address(isolate());
if (kIntSize == 8) {
return TNode<IntPtrT>::UncheckedCast(
Load(MachineType::Int64(), ExternalConstant(ref)));
} else {
Node* const value = Load(MachineType::Int32(), ExternalConstant(ref));
return ChangeInt32ToIntPtr(value);
}
}
void InternalBuiltinsAssembler::SetPendingMicrotaskCount(TNode<IntPtrT> count) {
auto ref = ExternalReference::pending_microtask_count_address(isolate());
auto rep = kIntSize == 8 ? MachineRepresentation::kWord64
: MachineRepresentation::kWord32;
if (kIntSize == 4 && kPointerSize == 8) {
Node* const truncated_count =
TruncateInt64ToInt32(TNode<Int64T>::UncheckedCast(count));
StoreNoWriteBarrier(rep, ExternalConstant(ref), truncated_count);
} else {
StoreNoWriteBarrier(rep, ExternalConstant(ref), count);
}
}
TNode<FixedArray> InternalBuiltinsAssembler::GetMicrotaskQueue() {
return TNode<FixedArray>::UncheckedCast(
LoadRoot(Heap::kMicrotaskQueueRootIndex));
}
void InternalBuiltinsAssembler::SetMicrotaskQueue(TNode<FixedArray> queue) {
StoreRoot(Heap::kMicrotaskQueueRootIndex, queue);
}
TNode<Context> InternalBuiltinsAssembler::GetCurrentContext() {
auto ref = ExternalReference(kContextAddress, isolate());
return TNode<Context>::UncheckedCast(
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
}
void InternalBuiltinsAssembler::SetCurrentContext(TNode<Context> context) {
auto ref = ExternalReference(kContextAddress, isolate());
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
context);
}
void InternalBuiltinsAssembler::EnterMicrotaskContext(
TNode<Context> microtask_context) {
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
Node* const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
StoreNoWriteBarrier(
MachineType::PointerRepresentation(), hsi,
IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
BitcastTaggedToWord(microtask_context));
// Load mirrored std::vector length from
// HandleScopeImplementer::entered_contexts_count_
auto type = kSizetSize == 8 ? MachineType::Uint64() : MachineType::Uint32();
Node* entered_contexts_length = Load(
type, hsi,
IntPtrConstant(HandleScopeImplementerOffsets::kEnteredContextsCount));
auto rep = kSizetSize == 8 ? MachineRepresentation::kWord64
: MachineRepresentation::kWord32;
StoreNoWriteBarrier(
rep, hsi,
IntPtrConstant(
HandleScopeImplementerOffsets::kEnteredContextCountDuringMicrotasks),
entered_contexts_length);
}
void InternalBuiltinsAssembler::LeaveMicrotaskContext() {
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
Node* const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
StoreNoWriteBarrier(
MachineType::PointerRepresentation(), hsi,
IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
IntPtrConstant(0));
if (kSizetSize == 4) {
StoreNoWriteBarrier(
MachineRepresentation::kWord32, hsi,
IntPtrConstant(HandleScopeImplementerOffsets::
kEnteredContextCountDuringMicrotasks),
Int32Constant(0));
} else {
StoreNoWriteBarrier(
MachineRepresentation::kWord64, hsi,
IntPtrConstant(HandleScopeImplementerOffsets::
kEnteredContextCountDuringMicrotasks),
Int64Constant(0));
}
}
TF_BUILTIN(RunMicrotasks, InternalBuiltinsAssembler) {
Label init_queue_loop(this);
Goto(&init_queue_loop);
BIND(&init_queue_loop);
{
TVARIABLE(IntPtrT, index, IntPtrConstant(0));
Label loop(this, &index);
TNode<IntPtrT> num_tasks = GetPendingMicrotaskCount();
ReturnIf(IntPtrEqual(num_tasks, IntPtrConstant(0)), UndefinedConstant());
TNode<FixedArray> queue = GetMicrotaskQueue();
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(
LoadAndUntagFixedArrayBaseLength(queue), num_tasks));
CSA_ASSERT(this, IntPtrGreaterThan(num_tasks, IntPtrConstant(0)));
SetPendingMicrotaskCount(IntPtrConstant(0));
SetMicrotaskQueue(
TNode<FixedArray>::UncheckedCast(EmptyFixedArrayConstant()));
Goto(&loop);
BIND(&loop);
{
TNode<HeapObject> microtask =
TNode<HeapObject>::UncheckedCast(LoadFixedArrayElement(queue, index));
index = IntPtrAdd(index, IntPtrConstant(1));
CSA_ASSERT(this, TaggedIsNotSmi(microtask));
TNode<Map> microtask_map = LoadMap(microtask);
TNode<Int32T> microtask_type = LoadMapInstanceType(microtask_map);
Label is_call_handler_info(this);
Label is_function(this);
Label is_promise_resolve_thenable_job(this);
Label is_promise_reaction_job(this);
Label is_unreachable(this);
int32_t case_values[] = {TUPLE3_TYPE, // CallHandlerInfo
JS_FUNCTION_TYPE,
PROMISE_RESOLVE_THENABLE_JOB_INFO_TYPE,
PROMISE_REACTION_JOB_INFO_TYPE};
Label* case_labels[] = {&is_call_handler_info, &is_function,
&is_promise_resolve_thenable_job,
&is_promise_reaction_job};
static_assert(arraysize(case_values) == arraysize(case_labels), "");
Switch(microtask_type, &is_unreachable, case_values, case_labels,
arraysize(case_labels));
BIND(&is_call_handler_info);
{
// Bailout to C++ slow path for the remainder of the loop.
auto index_ref =
ExternalReference(kMicrotaskQueueBailoutIndexAddress, isolate());
auto count_ref =
ExternalReference(kMicrotaskQueueBailoutCountAddress, isolate());
auto rep = kIntSize == 4 ? MachineRepresentation::kWord32
: MachineRepresentation::kWord64;
// index was pre-incremented, decrement for bailout to C++.
Node* value = IntPtrSub(index, IntPtrConstant(1));
if (kPointerSize == 4) {
DCHECK_EQ(kIntSize, 4);
StoreNoWriteBarrier(rep, ExternalConstant(index_ref), value);
StoreNoWriteBarrier(rep, ExternalConstant(count_ref), num_tasks);
} else {
Node* count = num_tasks;
if (kIntSize == 4) {
value = TruncateInt64ToInt32(value);
count = TruncateInt64ToInt32(count);
}
StoreNoWriteBarrier(rep, ExternalConstant(index_ref), value);
StoreNoWriteBarrier(rep, ExternalConstant(count_ref), count);
}
Return(queue);
}
BIND(&is_function);
{
Label cont(this);
VARIABLE(exception, MachineRepresentation::kTagged, TheHoleConstant());
TNode<Context> old_context = GetCurrentContext();
TNode<Context> fn_context = TNode<Context>::UncheckedCast(
LoadObjectField(microtask, JSFunction::kContextOffset));
TNode<Context> native_context =
TNode<Context>::UncheckedCast(LoadNativeContext(fn_context));
SetCurrentContext(native_context);
EnterMicrotaskContext(fn_context);
Node* const call = CallJS(CodeFactory::Call(isolate()), native_context,
microtask, UndefinedConstant());
GotoIfException(call, &cont);
Goto(&cont);
BIND(&cont);
LeaveMicrotaskContext();
SetCurrentContext(old_context);
Branch(IntPtrLessThan(index, num_tasks), &loop, &init_queue_loop);
}
BIND(&is_promise_resolve_thenable_job);
{
VARIABLE(exception, MachineRepresentation::kTagged, TheHoleConstant());
TNode<Context> old_context = GetCurrentContext();
TNode<Context> microtask_context =
TNode<Context>::UncheckedCast(LoadObjectField(
microtask, PromiseResolveThenableJobInfo::kContextOffset));
TNode<Context> native_context =
TNode<Context>::UncheckedCast(LoadNativeContext(microtask_context));
SetCurrentContext(native_context);
EnterMicrotaskContext(microtask_context);
Label if_unhandled_exception(this), done(this);
Node* const ret = CallBuiltin(Builtins::kPromiseResolveThenableJob,
native_context, microtask);
GotoIfException(ret, &if_unhandled_exception, &exception);
Goto(&done);
BIND(&if_unhandled_exception);
CallRuntime(Runtime::kReportMessage, native_context, exception.value());
Goto(&done);
BIND(&done);
LeaveMicrotaskContext();
SetCurrentContext(old_context);
Branch(IntPtrLessThan(index, num_tasks), &loop, &init_queue_loop);
}
BIND(&is_promise_reaction_job);
{
Label if_multiple(this);
Label if_single(this);
Node* const value =
LoadObjectField(microtask, PromiseReactionJobInfo::kValueOffset);
Node* const tasks =
LoadObjectField(microtask, PromiseReactionJobInfo::kTasksOffset);
Node* const deferred_promises = LoadObjectField(
microtask, PromiseReactionJobInfo::kDeferredPromiseOffset);
Node* const deferred_on_resolves = LoadObjectField(
microtask, PromiseReactionJobInfo::kDeferredOnResolveOffset);
Node* const deferred_on_rejects = LoadObjectField(
microtask, PromiseReactionJobInfo::kDeferredOnRejectOffset);
TNode<Context> old_context = GetCurrentContext();
TNode<Context> microtask_context = TNode<Context>::UncheckedCast(
LoadObjectField(microtask, PromiseReactionJobInfo::kContextOffset));
TNode<Context> native_context =
TNode<Context>::UncheckedCast(LoadNativeContext(microtask_context));
SetCurrentContext(native_context);
EnterMicrotaskContext(microtask_context);
Branch(IsFixedArray(deferred_promises), &if_multiple, &if_single);
BIND(&if_single);
{
CallBuiltin(Builtins::kPromiseHandle, native_context, value, tasks,
deferred_promises, deferred_on_resolves,
deferred_on_rejects);
LeaveMicrotaskContext();
SetCurrentContext(old_context);
Branch(IntPtrLessThan(index, num_tasks), &loop, &init_queue_loop);
}
BIND(&if_multiple);
{
TVARIABLE(IntPtrT, inner_index, IntPtrConstant(0));
TNode<IntPtrT> inner_length =
LoadAndUntagFixedArrayBaseLength(deferred_promises);
Label inner_loop(this, &inner_index), done(this);
CSA_ASSERT(this, IntPtrGreaterThan(inner_length, IntPtrConstant(0)));
Goto(&inner_loop);
BIND(&inner_loop);
{
Node* const task = LoadFixedArrayElement(tasks, inner_index);
Node* const deferred_promise =
LoadFixedArrayElement(deferred_promises, inner_index);
Node* const deferred_on_resolve =
LoadFixedArrayElement(deferred_on_resolves, inner_index);
Node* const deferred_on_reject =
LoadFixedArrayElement(deferred_on_rejects, inner_index);
CallBuiltin(Builtins::kPromiseHandle, native_context, value, task,
deferred_promise, deferred_on_resolve,
deferred_on_reject);
inner_index = IntPtrAdd(inner_index, IntPtrConstant(1));
Branch(IntPtrLessThan(inner_index, inner_length), &inner_loop,
&done);
}
BIND(&done);
LeaveMicrotaskContext();
SetCurrentContext(old_context);
Branch(IntPtrLessThan(index, num_tasks), &loop, &init_queue_loop);
}
}
BIND(&is_unreachable);
Unreachable();
}
}
}
TF_BUILTIN(PromiseResolveThenableJob, InternalBuiltinsAssembler) {
VARIABLE(exception, MachineRepresentation::kTagged, TheHoleConstant());
Callable call = CodeFactory::Call(isolate());
Label reject_promise(this, Label::kDeferred);
TNode<PromiseResolveThenableJobInfo> microtask =
TNode<PromiseResolveThenableJobInfo>::UncheckedCast(
Parameter(Descriptor::kMicrotask));
TNode<Context> context =
TNode<Context>::UncheckedCast(Parameter(Descriptor::kContext));
TNode<JSReceiver> thenable = TNode<JSReceiver>::UncheckedCast(LoadObjectField(
microtask, PromiseResolveThenableJobInfo::kThenableOffset));
TNode<JSReceiver> then = TNode<JSReceiver>::UncheckedCast(
LoadObjectField(microtask, PromiseResolveThenableJobInfo::kThenOffset));
TNode<JSFunction> resolve = TNode<JSFunction>::UncheckedCast(LoadObjectField(
microtask, PromiseResolveThenableJobInfo::kResolveOffset));
TNode<JSFunction> reject = TNode<JSFunction>::UncheckedCast(
LoadObjectField(microtask, PromiseResolveThenableJobInfo::kRejectOffset));
Node* const result = CallJS(call, context, then, thenable, resolve, reject);
GotoIfException(result, &reject_promise, &exception);
Return(UndefinedConstant());
BIND(&reject_promise);
CallJS(call, context, reject, UndefinedConstant(), exception.value());
Return(UndefinedConstant());
}
} // namespace internal
} // namespace v8
......@@ -1183,7 +1183,14 @@ TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) {
BIND(&if_customhandler);
{
CallJS(call_callable, context, on_reject, UndefinedConstant(), exception);
VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
Label if_exception(this);
Node* const ret = CallJS(call_callable, context, on_reject,
UndefinedConstant(), exception);
GotoIfException(ret, &if_exception, &var_exception);
Return(UndefinedConstant());
BIND(&if_exception);
CallRuntime(Runtime::kReportMessage, context, var_exception.value());
Return(UndefinedConstant());
}
}
......@@ -1297,6 +1304,20 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) {
}
}
TF_BUILTIN(PromiseHandleJS, PromiseBuiltinsAssembler) {
Node* const value = Parameter(Descriptor::kValue);
Node* const handler = Parameter(Descriptor::kHandler);
Node* const deferred_promise = Parameter(Descriptor::kDeferredPromise);
Node* const deferred_on_resolve = Parameter(Descriptor::kDeferredOnResolve);
Node* const deferred_on_reject = Parameter(Descriptor::kDeferredOnReject);
Node* const context = Parameter(Descriptor::kContext);
Node* const result =
CallBuiltin(Builtins::kPromiseHandle, context, value, handler,
deferred_promise, deferred_on_resolve, deferred_on_reject);
Return(result);
}
// ES#sec-promise.prototype.catch
// Promise.prototype.catch ( onRejected )
TF_BUILTIN(PromiseCatch, PromiseBuiltinsAssembler) {
......
......@@ -697,10 +697,18 @@ class CEntryStub : public PlatformCodeStub {
class JSEntryStub : public PlatformCodeStub {
public:
enum class SpecialTarget { kNone, kRunMicrotasks };
JSEntryStub(Isolate* isolate, StackFrame::Type type)
: PlatformCodeStub(isolate) {
DCHECK(type == StackFrame::ENTRY || type == StackFrame::CONSTRUCT_ENTRY);
minor_key_ = StackFrameTypeBits::encode(type);
minor_key_ = StackFrameTypeBits::encode(type) |
SpecialTargetBits::encode(SpecialTarget::kNone);
}
JSEntryStub(Isolate* isolate, SpecialTarget target)
: PlatformCodeStub(isolate) {
minor_key_ = StackFrameTypeBits::encode(StackFrame::ENTRY) |
SpecialTargetBits::encode(target);
}
private:
......@@ -715,7 +723,26 @@ class JSEntryStub : public PlatformCodeStub {
return StackFrameTypeBits::decode(minor_key_);
}
SpecialTarget special_target() const {
return SpecialTargetBits::decode(minor_key_);
}
Handle<Code> EntryTrampoline() {
switch (special_target()) {
case SpecialTarget::kNone:
return (type() == StackFrame::CONSTRUCT_ENTRY)
? BUILTIN_CODE(isolate(), JSConstructEntryTrampoline)
: BUILTIN_CODE(isolate(), JSEntryTrampoline);
case SpecialTarget::kRunMicrotasks:
return BUILTIN_CODE(isolate(), RunMicrotasks);
}
UNREACHABLE();
return Handle<Code>();
}
class StackFrameTypeBits : public BitField<StackFrame::Type, 0, 5> {};
class SpecialTargetBits
: public BitField<SpecialTarget, StackFrameTypeBits::kNext, 1> {};
int handler_offset_;
......
......@@ -55,7 +55,8 @@ namespace {
MUST_USE_RESULT MaybeHandle<Object> Invoke(
Isolate* isolate, bool is_construct, Handle<Object> target,
Handle<Object> receiver, int argc, Handle<Object> args[],
Handle<Object> new_target, Execution::MessageHandling message_handling) {
Handle<Object> new_target, Execution::MessageHandling message_handling,
Execution::Target execution_target) {
DCHECK(!receiver->IsJSGlobalObject());
#ifdef USE_SIMULATOR
......@@ -117,9 +118,18 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(
Object* receiver, int argc,
Object*** args);
Handle<Code> code = is_construct
? isolate->factory()->js_construct_entry_code()
: isolate->factory()->js_entry_code();
Handle<Code> code;
switch (execution_target) {
case Execution::Target::kCallable:
code = is_construct ? isolate->factory()->js_construct_entry_code()
: isolate->factory()->js_entry_code();
break;
case Execution::Target::kRunMicrotasks:
code = isolate->factory()->js_run_microtasks_entry_code();
break;
default:
UNREACHABLE();
}
{
// Save and restore context around invocation and block the
......@@ -167,7 +177,8 @@ MUST_USE_RESULT MaybeHandle<Object> Invoke(
MaybeHandle<Object> CallInternal(Isolate* isolate, Handle<Object> callable,
Handle<Object> receiver, int argc,
Handle<Object> argv[],
Execution::MessageHandling message_handling) {
Execution::MessageHandling message_handling,
Execution::Target target) {
// Convert calls on global objects to be calls on the global
// receiver instead to avoid having a 'this' pointer which refers
// directly to a global object.
......@@ -176,7 +187,8 @@ MaybeHandle<Object> CallInternal(Isolate* isolate, Handle<Object> callable,
handle(Handle<JSGlobalObject>::cast(receiver)->global_proxy(), isolate);
}
return Invoke(isolate, false, callable, receiver, argc, argv,
isolate->factory()->undefined_value(), message_handling);
isolate->factory()->undefined_value(), message_handling,
target);
}
} // namespace
......@@ -186,7 +198,7 @@ MaybeHandle<Object> Execution::Call(Isolate* isolate, Handle<Object> callable,
Handle<Object> receiver, int argc,
Handle<Object> argv[]) {
return CallInternal(isolate, callable, receiver, argc, argv,
MessageHandling::kReport);
MessageHandling::kReport, Execution::Target::kCallable);
}
......@@ -203,15 +215,13 @@ MaybeHandle<Object> Execution::New(Isolate* isolate, Handle<Object> constructor,
Handle<Object> argv[]) {
return Invoke(isolate, true, constructor,
isolate->factory()->undefined_value(), argc, argv, new_target,
MessageHandling::kReport);
MessageHandling::kReport, Execution::Target::kCallable);
}
MaybeHandle<Object> Execution::TryCall(Isolate* isolate,
Handle<Object> callable,
Handle<Object> receiver, int argc,
Handle<Object> args[],
MessageHandling message_handling,
MaybeHandle<Object>* exception_out) {
MaybeHandle<Object> Execution::TryCall(
Isolate* isolate, Handle<Object> callable, Handle<Object> receiver,
int argc, Handle<Object> args[], MessageHandling message_handling,
MaybeHandle<Object>* exception_out, Target target) {
bool is_termination = false;
MaybeHandle<Object> maybe_result;
if (exception_out != nullptr) *exception_out = MaybeHandle<Object>();
......@@ -226,8 +236,8 @@ MaybeHandle<Object> Execution::TryCall(Isolate* isolate,
catcher.SetVerbose(false);
catcher.SetCaptureMessage(false);
maybe_result =
CallInternal(isolate, callable, receiver, argc, args, message_handling);
maybe_result = CallInternal(isolate, callable, receiver, argc, args,
message_handling, target);
if (maybe_result.is_null()) {
DCHECK(isolate->has_pending_exception());
......@@ -253,6 +263,13 @@ MaybeHandle<Object> Execution::TryCall(Isolate* isolate,
return maybe_result;
}
MaybeHandle<Object> Execution::RunMicrotasks(
Isolate* isolate, MessageHandling message_handling,
MaybeHandle<Object>* exception_out) {
auto undefined = isolate->factory()->undefined_value();
return TryCall(isolate, undefined, undefined, 0, {}, message_handling,
exception_out, Target::kRunMicrotasks);
}
void StackGuard::SetStackLimit(uintptr_t limit) {
ExecutionAccess access(isolate_);
......
......@@ -20,6 +20,7 @@ class Execution final : public AllStatic {
public:
// Whether to report pending messages, or keep them pending on the isolate.
enum class MessageHandling { kReport, kKeepPending };
enum class Target { kCallable, kRunMicrotasks };
// Call a function, the caller supplies a receiver and an array
// of arguments.
......@@ -54,7 +55,12 @@ class Execution final : public AllStatic {
Handle<Object> receiver, int argc,
Handle<Object> argv[],
MessageHandling message_handling,
MaybeHandle<Object>* exception_out);
MaybeHandle<Object>* exception_out,
Target target = Target::kCallable);
// Convenience method for performing RunMicrotasks
static MaybeHandle<Object> RunMicrotasks(Isolate* isolate,
MessageHandling message_handling,
MaybeHandle<Object>* exception_out);
};
......
......@@ -92,6 +92,10 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
"LDoubleConstant::one_half");
Add(ExternalReference::isolate_address(isolate).address(), "isolate");
Add(ExternalReference::builtins_address(isolate).address(), "builtins");
Add(ExternalReference::handle_scope_implementer_address(isolate).address(),
"Isolate::handle_scope_implementer_address");
Add(ExternalReference::pending_microtask_count_address(isolate).address(),
"Isolate::pending_microtask_count_address()");
Add(ExternalReference::interpreter_dispatch_table_address(isolate).address(),
"Interpreter::dispatch_table_address");
Add(ExternalReference::bytecode_size_table_address(isolate).address(),
......
......@@ -1459,7 +1459,9 @@ enum class ConcurrencyMode { kNotConcurrent, kConcurrent };
C(PendingHandlerFP, pending_handler_fp) \
C(PendingHandlerSP, pending_handler_sp) \
C(ExternalCaughtException, external_caught_exception) \
C(JSEntrySP, js_entry_sp)
C(JSEntrySP, js_entry_sp) \
C(MicrotaskQueueBailoutIndex, microtask_queue_bailout_index) \
C(MicrotaskQueueBailoutCount, microtask_queue_bailout_count)
enum IsolateAddressId {
#define DECLARE_ENUM(CamelName, hacker_name) k##CamelName##Address,
......
......@@ -2629,6 +2629,10 @@ void Heap::CreateJSConstructEntryStub() {
set_js_construct_entry_code(*stub.GetCode());
}
void Heap::CreateJSRunMicrotasksEntryStub() {
JSEntryStub stub(isolate(), JSEntryStub::SpecialTarget::kRunMicrotasks);
set_js_run_microtasks_entry_code(*stub.GetCode());
}
void Heap::CreateFixedStubs() {
// Here we create roots for fixed stubs. They are needed at GC
......@@ -2660,6 +2664,7 @@ void Heap::CreateFixedStubs() {
// To workaround the problem, make separate functions without inlining.
Heap::CreateJSEntryStub();
Heap::CreateJSConstructEntryStub();
Heap::CreateJSRunMicrotasksEntryStub();
}
bool Heap::RootCanBeWrittenAfterInitialization(Heap::RootListIndex root_index) {
......
......@@ -256,7 +256,8 @@ using v8::MemoryPressureLevel;
DeserializeLazyHandlerExtraWide) \
/* JS Entries */ \
V(Code, js_entry_code, JsEntryCode) \
V(Code, js_construct_entry_code, JsConstructEntryCode)
V(Code, js_construct_entry_code, JsConstructEntryCode) \
V(Code, js_run_microtasks_entry_code, JsRunMicrotasksEntryCode)
// Entries in this list are limited to Smis and are not visited during GC.
#define SMI_ROOT_LIST(V) \
......@@ -1830,6 +1831,7 @@ class Heap {
// because of a gcc-4.4 bug that assigns wrong vtable entries.
NO_INLINE(void CreateJSEntryStub());
NO_INLINE(void CreateJSConstructEntryStub());
NO_INLINE(void CreateJSRunMicrotasksEntryStub());
void CreateFixedStubs();
......
......@@ -486,12 +486,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
// pop the faked function when we return. Notice that we cannot store a
// reference to the trampoline code directly in this stub, because the
// builtin stubs may not have been generated yet.
if (type() == StackFrame::CONSTRUCT_ENTRY) {
__ Call(BUILTIN_CODE(isolate(), JSConstructEntryTrampoline),
RelocInfo::CODE_TARGET);
} else {
__ Call(BUILTIN_CODE(isolate(), JSEntryTrampoline), RelocInfo::CODE_TARGET);
}
__ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
// Unlink this frame from the handler chain.
__ PopStackHandler();
......
......@@ -78,6 +78,7 @@ class PlatformInterfaceDescriptor;
V(ResumeGenerator) \
V(FrameDropperTrampoline) \
V(WasmRuntimeCall) \
V(RunMicrotasks) \
BUILTIN_LIST_TFS(V)
class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
......@@ -846,6 +847,13 @@ class WasmRuntimeCallDescriptor final : public CallInterfaceDescriptor {
0)
};
class RunMicrotasksDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_EMPTY_PARAMETERS()
DECLARE_DEFAULT_DESCRIPTOR(RunMicrotasksDescriptor, CallInterfaceDescriptor,
0)
};
#define DEFINE_TFS_BUILTIN_DESCRIPTOR(Name, ...) \
class Name##Descriptor : public CallInterfaceDescriptor { \
public: \
......
......@@ -109,6 +109,8 @@ void ThreadLocalTop::InitializeInternal() {
rethrowing_message_ = false;
pending_message_obj_ = nullptr;
scheduled_exception_ = nullptr;
microtask_queue_bailout_index_ = -1;
microtask_queue_bailout_count_ = 0;
}
......@@ -1810,21 +1812,9 @@ bool Isolate::IsExternalHandlerOnTop(Object* exception) {
return (entry_handler > external_handler);
}
void Isolate::ReportPendingMessages() {
DCHECK(AllowExceptions::IsAllowed(this));
// The embedder might run script in response to an exception.
AllowJavascriptExecutionDebugOnly allow_script(this);
void Isolate::ReportPendingMessagesImpl(bool report_externally) {
Object* exception = pending_exception();
// Try to propagate the exception to an external v8::TryCatch handler. If
// propagation was unsuccessful, then we will get another chance at reporting
// the pending message if the exception is re-thrown.
bool has_been_propagated = PropagatePendingExceptionToExternalTryCatch();
if (!has_been_propagated) return;
// Clear the pending message object early to avoid endless recursion.
Object* message_obj = thread_local_top_.pending_message_obj_;
clear_pending_message();
......@@ -1837,7 +1827,7 @@ void Isolate::ReportPendingMessages() {
// depending on whether and external v8::TryCatch or an internal JavaScript
// handler is on top.
bool should_report_exception;
if (IsExternalHandlerOnTop(exception)) {
if (report_externally) {
// Only report the exception if the external handler is verbose.
should_report_exception = try_catch_handler()->is_verbose_;
} else {
......@@ -1858,6 +1848,85 @@ void Isolate::ReportPendingMessages() {
}
}
void Isolate::ReportPendingMessages() {
DCHECK(AllowExceptions::IsAllowed(this));
// The embedder might run script in response to an exception.
AllowJavascriptExecutionDebugOnly allow_script(this);
Object* exception = pending_exception();
// Try to propagate the exception to an external v8::TryCatch handler. If
// propagation was unsuccessful, then we will get another chance at reporting
// the pending message if the exception is re-thrown.
bool has_been_propagated = PropagatePendingExceptionToExternalTryCatch();
if (!has_been_propagated) return;
ReportPendingMessagesImpl(IsExternalHandlerOnTop(exception));
}
void Isolate::ReportPendingMessagesFromJavaScript() {
DCHECK(AllowExceptions::IsAllowed(this));
auto IsHandledByJavaScript = [=]() {
// In this situation, the exception is always a non-terminating exception.
// Get the top-most JS_ENTRY handler, cannot be on top if it doesn't exist.
Address entry_handler = Isolate::handler(thread_local_top());
DCHECK_NOT_NULL(entry_handler);
entry_handler =
reinterpret_cast<StackHandler*>(entry_handler)->next()->address();
// Get the address of the external handler so we can compare the address to
// determine which one is closer to the top of the stack.
Address external_handler = thread_local_top()->try_catch_handler_address();
if (external_handler == nullptr) return true;
return (entry_handler < external_handler);
};
auto IsHandledExternally = [=]() {
Address external_handler = thread_local_top()->try_catch_handler_address();
if (external_handler == nullptr) return false;
// Get the top-most JS_ENTRY handler, cannot be on top if it doesn't exist.
Address entry_handler = Isolate::handler(thread_local_top());
DCHECK_NOT_NULL(entry_handler);
entry_handler =
reinterpret_cast<StackHandler*>(entry_handler)->next()->address();
return (entry_handler > external_handler);
};
auto PropagateToExternalHandler = [=]() {
if (IsHandledByJavaScript()) {
thread_local_top_.external_caught_exception_ = false;
return false;
}
if (!IsHandledExternally()) {
thread_local_top_.external_caught_exception_ = false;
return true;
}
thread_local_top_.external_caught_exception_ = true;
v8::TryCatch* handler = try_catch_handler();
DCHECK(thread_local_top_.pending_message_obj_->IsJSMessageObject() ||
thread_local_top_.pending_message_obj_->IsTheHole(this));
handler->can_continue_ = true;
handler->has_terminated_ = false;
handler->exception_ = pending_exception();
// Propagate to the external try-catch only if we got an actual message.
if (thread_local_top_.pending_message_obj_->IsTheHole(this)) return true;
handler->message_obj_ = thread_local_top_.pending_message_obj_;
return true;
};
// Try to propagate to an external v8::TryCatch handler.
if (!PropagateToExternalHandler()) return;
ReportPendingMessagesImpl(true);
}
MessageLocation Isolate::GetMessageLocation() {
DCHECK(has_pending_exception());
......@@ -2041,8 +2110,23 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
}
Handle<Object> retval = undefined;
PromiseOnStack* promise_on_stack = tltop->promise_on_stack_;
for (JavaScriptFrameIterator it(this); !it.done(); it.Advance()) {
switch (PredictException(it.frame())) {
for (StackFrameIterator it(this); !it.done(); it.Advance()) {
StackFrame* frame = it.frame();
HandlerTable::CatchPrediction catch_prediction;
if (frame->is_java_script()) {
catch_prediction = PredictException(JavaScriptFrame::cast(frame));
} else if (frame->type() == StackFrame::STUB) {
Code* code = frame->LookupCode();
if (!code->IsCode() || code->kind() != Code::BUILTIN ||
!code->handler_table()->length() || !code->is_turbofanned()) {
continue;
}
catch_prediction = code->GetBuiltinCatchPrediction();
} else {
continue;
}
switch (catch_prediction) {
case HandlerTable::UNCAUGHT:
continue;
case HandlerTable::CAUGHT:
......@@ -3691,72 +3775,89 @@ void Isolate::RunMicrotasksInternal() {
if (!pending_microtask_count()) return;
TRACE_EVENT0("v8.execute", "RunMicrotasks");
TRACE_EVENT_CALL_STATS_SCOPED(this, "v8", "V8.RunMicrotasks");
while (pending_microtask_count() > 0) {
HandleScope scope(this);
int num_tasks = pending_microtask_count();
// Do not use factory()->microtask_queue() here; we need a fresh handle!
Handle<FixedArray> queue(heap()->microtask_queue(), this);
DCHECK(num_tasks <= queue->length());
set_pending_microtask_count(0);
heap()->set_microtask_queue(heap()->empty_fixed_array());
Isolate* isolate = this;
FOR_WITH_HANDLE_SCOPE(isolate, int, i = 0, i, i < num_tasks, i++, {
Handle<Object> microtask(queue->get(i), this);
if (microtask->IsCallHandlerInfo()) {
Handle<CallHandlerInfo> callback_info =
Handle<CallHandlerInfo>::cast(microtask);
v8::MicrotaskCallback callback =
v8::ToCData<v8::MicrotaskCallback>(callback_info->callback());
void* data = v8::ToCData<void*>(callback_info->data());
callback(data);
} else {
SaveContext save(this);
Context* context;
if (microtask->IsJSFunction()) {
context = Handle<JSFunction>::cast(microtask)->context();
} else if (microtask->IsPromiseResolveThenableJobInfo()) {
context =
Handle<PromiseResolveThenableJobInfo>::cast(microtask)->context();
} else {
context = Handle<PromiseReactionJobInfo>::cast(microtask)->context();
}
do {
HandleScope handle_scope(this);
set_microtask_queue_bailout_index(-1);
set_microtask_queue_bailout_count(-1);
MaybeHandle<Object> maybe_exception;
MaybeHandle<Object> maybe_result = Execution::RunMicrotasks(
this, Execution::MessageHandling::kReport, &maybe_exception);
if (maybe_result.is_null() && maybe_exception.is_null()) {
heap()->set_microtask_queue(heap()->empty_fixed_array());
set_pending_microtask_count(0);
return;
}
set_context(context->native_context());
handle_scope_implementer_->EnterMicrotaskContext(
Handle<Context>(context, this));
MaybeHandle<Object> result;
MaybeHandle<Object> maybe_exception;
if (microtask->IsJSFunction()) {
Handle<JSFunction> microtask_function =
Handle<JSFunction>::cast(microtask);
result = Execution::TryCall(
this, microtask_function, factory()->undefined_value(), 0,
nullptr, Execution::MessageHandling::kReport, &maybe_exception);
} else if (microtask->IsPromiseResolveThenableJobInfo()) {
PromiseResolveThenableJob(
Handle<PromiseResolveThenableJobInfo>::cast(microtask), &result,
&maybe_exception);
} else {
PromiseReactionJob(Handle<PromiseReactionJobInfo>::cast(microtask),
&result, &maybe_exception);
}
Handle<Object> result = maybe_result.ToHandleChecked();
if (result->IsUndefined(this)) return;
handle_scope_implementer_->LeaveMicrotaskContext();
Handle<FixedArray> queue = Handle<FixedArray>::cast(result);
int num_tasks = microtask_queue_bailout_count();
DCHECK_GE(microtask_queue_bailout_index(), 0);
// If execution is terminating, just bail out.
if (result.is_null() && maybe_exception.is_null()) {
// Clear out any remaining callbacks in the queue.
heap()->set_microtask_queue(heap()->empty_fixed_array());
set_pending_microtask_count(0);
return;
}
}
});
}
Isolate* isolate = this;
FOR_WITH_HANDLE_SCOPE(
isolate, int, i = microtask_queue_bailout_index(), i, i < num_tasks,
i++, {
Handle<Object> microtask(queue->get(i), this);
if (microtask->IsCallHandlerInfo()) {
Handle<CallHandlerInfo> callback_info =
Handle<CallHandlerInfo>::cast(microtask);
v8::MicrotaskCallback callback =
v8::ToCData<v8::MicrotaskCallback>(callback_info->callback());
void* data = v8::ToCData<void*>(callback_info->data());
callback(data);
} else {
SaveContext save(this);
Context* context;
if (microtask->IsJSFunction()) {
context = Handle<JSFunction>::cast(microtask)->context();
} else if (microtask->IsPromiseResolveThenableJobInfo()) {
context = Handle<PromiseResolveThenableJobInfo>::cast(microtask)
->context();
} else {
context =
Handle<PromiseReactionJobInfo>::cast(microtask)->context();
}
set_context(context->native_context());
handle_scope_implementer_->EnterMicrotaskContext(
Handle<Context>(context, this));
MaybeHandle<Object> result;
MaybeHandle<Object> maybe_exception;
if (microtask->IsJSFunction()) {
Handle<JSFunction> microtask_function =
Handle<JSFunction>::cast(microtask);
result = Execution::TryCall(
this, microtask_function, factory()->undefined_value(), 0,
nullptr, Execution::MessageHandling::kReport,
&maybe_exception);
} else if (microtask->IsPromiseResolveThenableJobInfo()) {
PromiseResolveThenableJob(
Handle<PromiseResolveThenableJobInfo>::cast(microtask),
&result, &maybe_exception);
} else {
PromiseReactionJob(
Handle<PromiseReactionJobInfo>::cast(microtask), &result,
&maybe_exception);
}
handle_scope_implementer_->LeaveMicrotaskContext();
// If execution is terminating, just bail out.
if (result.is_null() && maybe_exception.is_null()) {
// Clear out any remaining callbacks in the queue.
heap()->set_microtask_queue(heap()->empty_fixed_array());
set_pending_microtask_count(0);
return;
}
}
});
} while (pending_microtask_count() > 0);
}
void Isolate::SetUseCounterCallback(v8::Isolate::UseCounterCallback callback) {
......
......@@ -373,6 +373,9 @@ class ThreadLocalTop BASE_EMBEDDED {
// Call back function to report unsafe JS accesses.
v8::FailedAccessCheckCallback failed_access_check_callback_;
int microtask_queue_bailout_index_;
int microtask_queue_bailout_count_;
private:
void InitializeInternal();
......@@ -675,6 +678,18 @@ class Isolate {
return &thread_local_top_.js_entry_sp_;
}
THREAD_LOCAL_TOP_ACCESSOR(int, microtask_queue_bailout_index)
Address microtask_queue_bailout_index_address() {
return reinterpret_cast<Address>(
&thread_local_top_.microtask_queue_bailout_index_);
}
THREAD_LOCAL_TOP_ACCESSOR(int, microtask_queue_bailout_count)
Address microtask_queue_bailout_count_address() {
return reinterpret_cast<Address>(
&thread_local_top_.microtask_queue_bailout_count_);
}
// Returns the global object of the current context. It could be
// a builtin object, or a JS global object.
inline Handle<JSGlobalObject> global_object();
......@@ -808,6 +823,11 @@ class Isolate {
// Un-schedule an exception that was caught by a TryCatch handler.
void CancelScheduledExceptionFromTryCatch(v8::TryCatch* handler);
void ReportPendingMessages();
void ReportPendingMessagesFromJavaScript();
// Implements code shared between the two above methods
void ReportPendingMessagesImpl(bool report_externally);
// Return pending location if any or unfilled structure.
MessageLocation GetMessageLocation();
......@@ -1210,6 +1230,7 @@ class Isolate {
void PromiseResolveThenableJob(Handle<PromiseResolveThenableJobInfo> info,
MaybeHandle<Object>* result,
MaybeHandle<Object>* maybe_exception);
void EnqueueMicrotask(Handle<Object> microtask);
void RunMicrotasks();
bool IsRunningMicrotasks() const { return is_running_microtasks_; }
......@@ -1233,6 +1254,14 @@ class Isolate {
return reinterpret_cast<Address>(&promise_hook_or_debug_is_active_);
}
Address pending_microtask_count_address() {
return reinterpret_cast<Address>(&pending_microtask_count_);
}
Address handle_scope_implementer_address() {
return reinterpret_cast<Address>(&handle_scope_implementer_);
}
void DebugStateUpdated();
void SetPromiseHook(PromiseHook hook);
......
......@@ -589,13 +589,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
// callee saved registers + ra
// 4 args slots
// args
if (type() == StackFrame::CONSTRUCT_ENTRY) {
__ Call(BUILTIN_CODE(isolate, JSConstructEntryTrampoline),
RelocInfo::CODE_TARGET);
} else {
__ Call(BUILTIN_CODE(isolate, JSEntryTrampoline), RelocInfo::CODE_TARGET);
}
__ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
// Unlink this frame from the handler chain.
__ PopStackHandler();
......
......@@ -587,13 +587,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
// callee saved registers + ra
// [ O32: 4 args slots]
// args
if (type() == StackFrame::CONSTRUCT_ENTRY) {
__ Call(BUILTIN_CODE(isolate, JSConstructEntryTrampoline),
RelocInfo::CODE_TARGET);
} else {
__ Call(BUILTIN_CODE(isolate, JSEntryTrampoline), RelocInfo::CODE_TARGET);
}
__ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
// Unlink this frame from the handler chain.
__ PopStackHandler();
......
......@@ -6,6 +6,7 @@
#include <memory>
#include "src/api.h"
#include "src/arguments.h"
#include "src/ast/prettyprinter.h"
#include "src/bootstrapper.h"
......@@ -650,5 +651,22 @@ RUNTIME_FUNCTION(Runtime_GetTemplateObject) {
description, isolate->native_context());
}
RUNTIME_FUNCTION(Runtime_ReportMessage) {
// Helper to report messages and continue JS execution. This is intended to
// behave similarly to reporting exceptions which reach the top-level in
// Execution.cc, but allow the JS code to continue. This is useful for
// implementing algorithms such as RunMicrotasks in JS.
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, message_obj, 0);
DCHECK(!isolate->has_pending_exception());
isolate->set_pending_exception(*message_obj);
isolate->ReportPendingMessagesFromJavaScript();
isolate->clear_pending_exception();
return isolate->heap()->undefined_value();
}
} // namespace internal
} // namespace v8
......@@ -339,7 +339,8 @@ namespace internal {
F(Typeof, 1, 1) \
F(UnwindAndFindExceptionHandler, 0, 1) \
F(AllowDynamicFunction, 1, 1) \
F(GetTemplateObject, 1, 1)
F(GetTemplateObject, 1, 1) \
F(ReportMessage, 1, 1)
#define FOR_EACH_INTRINSIC_LITERALS(F) \
F(CreateRegExpLiteral, 4, 1) \
......
......@@ -523,12 +523,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
// external reference instead of inlining the call target address directly
// in the code, because the builtin stubs may not have been generated yet
// at the time this code is generated.
if (type() == StackFrame::CONSTRUCT_ENTRY) {
__ Call(BUILTIN_CODE(isolate(), JSConstructEntryTrampoline),
RelocInfo::CODE_TARGET);
} else {
__ Call(BUILTIN_CODE(isolate(), JSEntryTrampoline), RelocInfo::CODE_TARGET);
}
__ Call(EntryTrampoline(), RelocInfo::CODE_TARGET);
// Unlink this frame from the handler chain.
__ PopStackHandler();
......
......@@ -43,7 +43,7 @@ Running test: testRejectedPromiseWithError
columnNumber : 11
exception : {
className : Error
description : Error: MyError at foo (<anonymous>:13:11) at throwError (<anonymous>:15:3) at <anonymous>
description : Error: MyError at foo (<anonymous>:13:11) at throwError (<anonymous>:15:3)
objectId : <objectId>
subtype : error
type : object
......@@ -58,14 +58,14 @@ Running test: testRejectedPromiseWithError
functionName : foo
lineNumber : 12
scriptId : <scriptId>
url :
url :
}
[1] : {
columnNumber : 2
functionName : throwError
lineNumber : 14
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -73,7 +73,7 @@ Running test: testRejectedPromiseWithError
}
result : {
className : Error
description : Error: MyError at foo (<anonymous>:13:11) at throwError (<anonymous>:15:3) at <anonymous>
description : Error: MyError at foo (<anonymous>:13:11) at throwError (<anonymous>:15:3)
objectId : <objectId>
subtype : error
type : object
......@@ -89,7 +89,7 @@ Running test: testRejectedPromiseWithSyntaxError
columnNumber : 5
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token } at foo (<anonymous>:21:5) at throwSyntaxError (<anonymous>:23:3) at <anonymous>
description : SyntaxError: Unexpected token } at foo (<anonymous>:21:5) at throwSyntaxError (<anonymous>:23:3)
objectId : <objectId>
subtype : error
type : object
......@@ -104,14 +104,14 @@ Running test: testRejectedPromiseWithSyntaxError
functionName : foo
lineNumber : 20
scriptId : <scriptId>
url :
url :
}
[1] : {
columnNumber : 2
functionName : throwSyntaxError
lineNumber : 22
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -119,7 +119,7 @@ Running test: testRejectedPromiseWithSyntaxError
}
result : {
className : SyntaxError
description : SyntaxError: Unexpected token } at foo (<anonymous>:21:5) at throwSyntaxError (<anonymous>:23:3) at <anonymous>
description : SyntaxError: Unexpected token } at foo (<anonymous>:21:5) at throwSyntaxError (<anonymous>:23:3)
objectId : <objectId>
subtype : error
type : object
......
......@@ -86,7 +86,7 @@ let buffer = builder.toBuffer();
// Test async compilation and instantiation.
assertPromiseResult(WebAssembly.instantiate(buffer), pair => {
testTrapLocations(pair.instance, 6);
testTrapLocations(pair.instance, 5);
});
// Test sync compilation and instantiation.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment