Switch full-codegen from StackHandlers to handler table.
This switches full-codegen to no longer push and pop StackHandler markers onto the operand stack, but relies on a range-based handler table instead. We only use StackHandlers in JSEntryStubs to mark the transition from C to JS code. Note that this makes deoptimization and OSR from within any try-block work out of the box, makes the non-exception paths faster and should overall be neutral on the memory footprint (pros). On the other hand it makes the exception paths slower and actually throwing and exception more expensive (cons). R=yangguo@chromium.org TEST=cctest/test-run-jsexceptions/DeoptTry Review URL: https://codereview.chromium.org/1010883002 Cr-Commit-Position: refs/heads/master@{#27440}
This commit is contained in:
parent
755e43811d
commit
38a719f965
@ -1076,9 +1076,8 @@ void CEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ mov(fp, Operand(pending_handler_fp_address));
|
||||
__ ldr(fp, MemOperand(fp));
|
||||
|
||||
// If the handler is a JS frame, restore the context to the frame.
|
||||
// (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp
|
||||
// or cp.
|
||||
// If the handler is a JS frame, restore the context to the frame. Note that
|
||||
// the context will be set to (cp == 0) for non-JS frames.
|
||||
__ cmp(cp, Operand(0));
|
||||
__ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
|
||||
|
||||
@ -1181,7 +1180,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
handler_offset_ = handler_entry.pos();
|
||||
// Caught exception: Store result (exception) in the pending exception
|
||||
// field in the JSEnv and return a failure sentinel. Coming in here the
|
||||
// fp will be invalid because the PushTryHandler below sets it to 0 to
|
||||
// fp will be invalid because the PushStackHandler below sets it to 0 to
|
||||
// signal the existence of the JSEntry frame.
|
||||
__ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
|
||||
isolate())));
|
||||
@ -1190,11 +1189,10 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ LoadRoot(r0, Heap::kExceptionRootIndex);
|
||||
__ b(&exit);
|
||||
|
||||
// Invoke: Link this frame into the handler chain. There's only one
|
||||
// handler block in this code object, so its index is 0.
|
||||
// Invoke: Link this frame into the handler chain.
|
||||
__ bind(&invoke);
|
||||
// Must preserve r0-r4, r5-r6 are available.
|
||||
__ PushTryHandler(StackHandler::JS_ENTRY, 0);
|
||||
__ PushStackHandler();
|
||||
// If an exception not caught by another handler occurs, this handler
|
||||
// returns control to the code after the bl(&invoke) above, which
|
||||
// restores all kCalleeSaved registers (including cp and fp) to their
|
||||
@ -1231,7 +1229,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ Call(ip);
|
||||
|
||||
// Unlink this frame from the handler chain.
|
||||
__ PopTryHandler();
|
||||
__ PopStackHandler();
|
||||
|
||||
__ bind(&exit); // r0 holds result
|
||||
// Check if the current stack frame is marked as the outermost JS frame.
|
||||
|
@ -107,7 +107,8 @@ class JumpPatchSite BASE_EMBEDDED {
|
||||
void FullCodeGenerator::Generate() {
|
||||
CompilationInfo* info = info_;
|
||||
handler_table_ =
|
||||
isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
|
||||
Handle<HandlerTable>::cast(isolate()->factory()->NewFixedArray(
|
||||
HandlerTable::LengthForRange(function()->handler_count()), TENURED));
|
||||
|
||||
profiling_counter_ = isolate()->factory()->NewCell(
|
||||
Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate()));
|
||||
@ -2196,7 +2197,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
|
||||
// catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; }
|
||||
__ bind(&l_catch);
|
||||
handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
|
||||
__ LoadRoot(load_name, Heap::kthrow_stringRootIndex); // "throw"
|
||||
__ ldr(r3, MemOperand(sp, 1 * kPointerSize)); // iter
|
||||
__ Push(load_name, r3, r0); // "throw", iter, except
|
||||
@ -2207,16 +2207,17 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
// re-boxing.
|
||||
__ bind(&l_try);
|
||||
__ pop(r0); // result
|
||||
__ PushTryHandler(StackHandler::CATCH, expr->index());
|
||||
const int handler_size = StackHandlerConstants::kSize;
|
||||
EnterTryBlock(expr->index(), &l_catch);
|
||||
const int try_block_size = TryCatch::kElementCount * kPointerSize;
|
||||
__ push(r0); // result
|
||||
__ jmp(&l_suspend);
|
||||
__ bind(&l_continuation);
|
||||
__ jmp(&l_resume);
|
||||
__ bind(&l_suspend);
|
||||
const int generator_object_depth = kPointerSize + handler_size;
|
||||
const int generator_object_depth = kPointerSize + try_block_size;
|
||||
__ ldr(r0, MemOperand(sp, generator_object_depth));
|
||||
__ push(r0); // g
|
||||
__ Push(Smi::FromInt(expr->index())); // handler-index
|
||||
DCHECK(l_continuation.pos() > 0 && Smi::IsValid(l_continuation.pos()));
|
||||
__ mov(r1, Operand(Smi::FromInt(l_continuation.pos())));
|
||||
__ str(r1, FieldMemOperand(r0, JSGeneratorObject::kContinuationOffset));
|
||||
@ -2224,12 +2225,12 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
__ mov(r1, cp);
|
||||
__ RecordWriteField(r0, JSGeneratorObject::kContextOffset, r1, r2,
|
||||
kLRHasBeenSaved, kDontSaveFPRegs);
|
||||
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
|
||||
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 2);
|
||||
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
__ pop(r0); // result
|
||||
EmitReturnSequence();
|
||||
__ bind(&l_resume); // received in r0
|
||||
__ PopTryHandler();
|
||||
ExitTryBlock(expr->index());
|
||||
|
||||
// receiver = iter; f = 'next'; arg = received;
|
||||
__ bind(&l_next);
|
||||
@ -5352,34 +5353,6 @@ void FullCodeGenerator::ExitFinallyBlock() {
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
#define __ ACCESS_MASM(masm())
|
||||
|
||||
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
|
||||
int* stack_depth,
|
||||
int* context_length) {
|
||||
// The macros used here must preserve the result register.
|
||||
|
||||
// Because the handler block contains the context of the finally
|
||||
// code, we can restore it directly from there for the finally code
|
||||
// rather than iteratively unwinding contexts via their previous
|
||||
// links.
|
||||
__ Drop(*stack_depth); // Down to the handler block.
|
||||
if (*context_length > 0) {
|
||||
// Restore the context to its dedicated register and the stack.
|
||||
__ ldr(cp, MemOperand(sp, StackHandlerConstants::kContextOffset));
|
||||
__ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
}
|
||||
__ PopTryHandler();
|
||||
__ bl(finally_entry_);
|
||||
|
||||
*stack_depth = 0;
|
||||
*context_length = 0;
|
||||
return previous_;
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
|
@ -1396,37 +1396,22 @@ void MacroAssembler::DebugBreak() {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
|
||||
int handler_index) {
|
||||
void MacroAssembler::PushStackHandler() {
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize);
|
||||
|
||||
// For the JSEntry handler, we must preserve r0-r4, r5-r6 are available.
|
||||
// We will build up the handler from the bottom by pushing on the stack.
|
||||
// Set up the the index (r6) for pushing.
|
||||
mov(r6, Operand(handler_index));
|
||||
|
||||
// Push the context and index.
|
||||
if (kind == StackHandler::JS_ENTRY) {
|
||||
mov(cp, Operand(Smi::FromInt(0))); // Indicates no context.
|
||||
stm(db_w, sp, r6.bit() | cp.bit());
|
||||
} else {
|
||||
stm(db_w, sp, r6.bit() | cp.bit());
|
||||
}
|
||||
|
||||
// Link the current handler as the next handler.
|
||||
mov(r6, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
ldr(r5, MemOperand(r6));
|
||||
push(r5);
|
||||
|
||||
// Set this new handler as the current one.
|
||||
str(sp, MemOperand(r6));
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PopTryHandler() {
|
||||
void MacroAssembler::PopStackHandler() {
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
pop(r1);
|
||||
mov(ip, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
|
@ -643,12 +643,12 @@ class MacroAssembler: public Assembler {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exception handling
|
||||
|
||||
// Push a new try handler and link into try handler chain.
|
||||
void PushTryHandler(StackHandler::Kind kind, int handler_index);
|
||||
// Push a new stack handler and link into stack handler chain.
|
||||
void PushStackHandler();
|
||||
|
||||
// Unlink the stack handler on top of the stack from the try handler chain.
|
||||
// Unlink the stack handler on top of the stack from the stack handler chain.
|
||||
// Must preserve the result register.
|
||||
void PopTryHandler();
|
||||
void PopStackHandler();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inline caching support
|
||||
|
@ -1226,9 +1226,8 @@ void CEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ Mov(fp, Operand(pending_handler_fp_address));
|
||||
__ Ldr(fp, MemOperand(fp));
|
||||
|
||||
// If the handler is a JS frame, restore the context to the frame.
|
||||
// (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp
|
||||
// or cp.
|
||||
// If the handler is a JS frame, restore the context to the frame. Note that
|
||||
// the context will be set to (cp == 0) for non-JS frames.
|
||||
Label not_js_frame;
|
||||
__ Cbz(cp, ¬_js_frame);
|
||||
__ Str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
@ -1346,10 +1345,9 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ LoadRoot(x0, Heap::kExceptionRootIndex);
|
||||
__ B(&exit);
|
||||
|
||||
// Invoke: Link this frame into the handler chain. There's only one
|
||||
// handler block in this code object, so its index is 0.
|
||||
// Invoke: Link this frame into the handler chain.
|
||||
__ Bind(&invoke);
|
||||
__ PushTryHandler(StackHandler::JS_ENTRY, 0);
|
||||
__ PushStackHandler();
|
||||
// If an exception not caught by another handler occurs, this handler
|
||||
// returns control to the code after the B(&invoke) above, which
|
||||
// restores all callee-saved registers (including cp and fp) to their
|
||||
@ -1383,7 +1381,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ Blr(x12);
|
||||
|
||||
// Unlink this frame from the handler chain.
|
||||
__ PopTryHandler();
|
||||
__ PopStackHandler();
|
||||
|
||||
|
||||
__ Bind(&exit);
|
||||
|
@ -105,7 +105,8 @@ class JumpPatchSite BASE_EMBEDDED {
|
||||
void FullCodeGenerator::Generate() {
|
||||
CompilationInfo* info = info_;
|
||||
handler_table_ =
|
||||
isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
|
||||
Handle<HandlerTable>::cast(isolate()->factory()->NewFixedArray(
|
||||
HandlerTable::LengthForRange(function()->handler_count()), TENURED));
|
||||
|
||||
profiling_counter_ = isolate()->factory()->NewCell(
|
||||
Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate()));
|
||||
@ -5043,7 +5044,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
|
||||
// catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; }
|
||||
__ Bind(&l_catch);
|
||||
handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
|
||||
__ LoadRoot(load_name, Heap::kthrow_stringRootIndex); // "throw"
|
||||
__ Peek(x3, 1 * kPointerSize); // iter
|
||||
__ Push(load_name, x3, x0); // "throw", iter, except
|
||||
@ -5054,8 +5054,8 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
// re-boxing.
|
||||
__ Bind(&l_try);
|
||||
__ Pop(x0); // result
|
||||
__ PushTryHandler(StackHandler::CATCH, expr->index());
|
||||
const int handler_size = StackHandlerConstants::kSize;
|
||||
EnterTryBlock(expr->index(), &l_catch);
|
||||
const int try_block_size = TryCatch::kElementCount * kPointerSize;
|
||||
__ Push(x0); // result
|
||||
__ B(&l_suspend);
|
||||
|
||||
@ -5066,9 +5066,10 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
__ B(&l_resume);
|
||||
|
||||
__ Bind(&l_suspend);
|
||||
const int generator_object_depth = kPointerSize + handler_size;
|
||||
const int generator_object_depth = kPointerSize + try_block_size;
|
||||
__ Peek(x0, generator_object_depth);
|
||||
__ Push(x0); // g
|
||||
__ Push(Smi::FromInt(expr->index())); // handler-index
|
||||
DCHECK((l_continuation.pos() > 0) && Smi::IsValid(l_continuation.pos()));
|
||||
__ Mov(x1, Smi::FromInt(l_continuation.pos()));
|
||||
__ Str(x1, FieldMemOperand(x0, JSGeneratorObject::kContinuationOffset));
|
||||
@ -5076,12 +5077,12 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
__ Mov(x1, cp);
|
||||
__ RecordWriteField(x0, JSGeneratorObject::kContextOffset, x1, x2,
|
||||
kLRHasBeenSaved, kDontSaveFPRegs);
|
||||
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
|
||||
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 2);
|
||||
__ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
__ Pop(x0); // result
|
||||
EmitReturnSequence();
|
||||
__ Bind(&l_resume); // received in x0
|
||||
__ PopTryHandler();
|
||||
ExitTryBlock(expr->index());
|
||||
|
||||
// receiver = iter; f = 'next'; arg = received;
|
||||
__ Bind(&l_next);
|
||||
@ -5474,37 +5475,6 @@ BackEdgeTable::BackEdgeState BackEdgeTable::GetBackEdgeState(
|
||||
}
|
||||
|
||||
|
||||
#define __ ACCESS_MASM(masm())
|
||||
|
||||
|
||||
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
|
||||
int* stack_depth,
|
||||
int* context_length) {
|
||||
ASM_LOCATION("FullCodeGenerator::TryFinally::Exit");
|
||||
// The macros used here must preserve the result register.
|
||||
|
||||
// Because the handler block contains the context of the finally
|
||||
// code, we can restore it directly from there for the finally code
|
||||
// rather than iteratively unwinding contexts via their previous
|
||||
// links.
|
||||
__ Drop(*stack_depth); // Down to the handler block.
|
||||
if (*context_length > 0) {
|
||||
// Restore the context to its dedicated register and the stack.
|
||||
__ Peek(cp, StackHandlerConstants::kContextOffset);
|
||||
__ Str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
}
|
||||
__ PopTryHandler();
|
||||
__ Bl(finally_entry_);
|
||||
|
||||
*stack_depth = 0;
|
||||
*context_length = 0;
|
||||
return previous_;
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_TARGET_ARCH_ARM64
|
||||
|
@ -3038,39 +3038,26 @@ void MacroAssembler::DebugBreak() {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
|
||||
int handler_index) {
|
||||
void MacroAssembler::PushStackHandler() {
|
||||
DCHECK(jssp.Is(StackPointer()));
|
||||
// Adjust this code if the asserts don't hold.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize);
|
||||
|
||||
// For the JSEntry handler, we must preserve the live registers x0-x4.
|
||||
// (See JSEntryStub::GenerateBody().)
|
||||
|
||||
// Set up the index for pushing.
|
||||
Mov(x11, handler_index);
|
||||
|
||||
// Push the context and state.
|
||||
if (kind == StackHandler::JS_ENTRY) {
|
||||
DCHECK(Smi::FromInt(0) == 0);
|
||||
Push(xzr, x11);
|
||||
} else {
|
||||
Push(cp, x11);
|
||||
}
|
||||
|
||||
// Link the current handler as the next handler.
|
||||
Mov(x11, ExternalReference(Isolate::kHandlerAddress, isolate()));
|
||||
Ldr(x10, MemOperand(x11));
|
||||
Push(x10);
|
||||
|
||||
// Set this new handler as the current one.
|
||||
Str(jssp, MemOperand(x11));
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PopTryHandler() {
|
||||
void MacroAssembler::PopStackHandler() {
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
Pop(x10);
|
||||
Mov(x11, ExternalReference(Isolate::kHandlerAddress, isolate()));
|
||||
|
@ -1273,12 +1273,12 @@ class MacroAssembler : public Assembler {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exception handling
|
||||
|
||||
// Push a new try handler and link into try handler chain.
|
||||
void PushTryHandler(StackHandler::Kind kind, int handler_index);
|
||||
// Push a new stack handler and link into stack handler chain.
|
||||
void PushStackHandler();
|
||||
|
||||
// Unlink the stack handler on top of the stack from the try handler chain.
|
||||
// Unlink the stack handler on top of the stack from the stack handler chain.
|
||||
// Must preserve the result register.
|
||||
void PopTryHandler();
|
||||
void PopStackHandler();
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -1334,7 +1334,10 @@ void AstGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
||||
try_control.BeginTry();
|
||||
{
|
||||
ControlScopeForCatch scope(this, &try_control);
|
||||
STATIC_ASSERT(TryBlockConstant::kElementCount == 1);
|
||||
environment()->Push(current_context());
|
||||
Visit(stmt->try_block());
|
||||
environment()->Pop();
|
||||
}
|
||||
try_control.EndTry();
|
||||
|
||||
@ -1381,7 +1384,10 @@ void AstGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
||||
try_control.BeginTry();
|
||||
{
|
||||
ControlScopeForFinally scope(this, commands, &try_control);
|
||||
STATIC_ASSERT(TryBlockConstant::kElementCount == 1);
|
||||
environment()->Push(current_context());
|
||||
Visit(stmt->try_block());
|
||||
environment()->Pop();
|
||||
}
|
||||
try_control.EndTry(commands->GetFallThroughToken(), fallthrough_result);
|
||||
|
||||
|
@ -134,12 +134,13 @@ Handle<Code> CodeGenerator::GenerateCode() {
|
||||
|
||||
// Emit exception handler table.
|
||||
if (!handlers_.empty()) {
|
||||
Handle<FixedArray> table = isolate()->factory()->NewFixedArray(
|
||||
static_cast<int>(handlers_.size()) * 2, TENURED);
|
||||
Handle<HandlerTable> table =
|
||||
Handle<HandlerTable>::cast(isolate()->factory()->NewFixedArray(
|
||||
HandlerTable::LengthForReturn(static_cast<int>(handlers_.size())),
|
||||
TENURED));
|
||||
for (size_t i = 0; i < handlers_.size(); ++i) {
|
||||
int table_index = static_cast<int>(i * 2);
|
||||
table->set(table_index + 0, Smi::FromInt(handlers_[i].pc_offset));
|
||||
table->set(table_index + 1, Smi::FromInt(handlers_[i].handler->pos()));
|
||||
table->SetReturnOffset(static_cast<int>(i), handlers_[i].pc_offset);
|
||||
table->SetReturnHandler(static_cast<int>(i), handlers_[i].handler->pos());
|
||||
}
|
||||
result->set_handler_table(*table);
|
||||
}
|
||||
|
@ -1185,8 +1185,9 @@ void Debug::FloodHandlerWithOneShot() {
|
||||
}
|
||||
for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) {
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
if (frame->HasHandler()) {
|
||||
// Flood the function with the catch block with break points
|
||||
int stack_slots = 0; // The computed stack slot count is not used.
|
||||
if (frame->LookupExceptionHandlerInTable(&stack_slots) > 0) {
|
||||
// Flood the function with the catch/finally block with break points.
|
||||
FloodWithOneShot(Handle<JSFunction>(frame->function()));
|
||||
return;
|
||||
}
|
||||
@ -2503,7 +2504,8 @@ MaybeHandle<Object> Debug::PromiseHasUserDefinedRejectHandler(
|
||||
|
||||
|
||||
void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
|
||||
bool uncaught = !isolate_->PredictWhetherExceptionIsCaught(*exception);
|
||||
Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
|
||||
bool uncaught = (catch_type == Isolate::NOT_CAUGHT);
|
||||
if (promise->IsJSObject()) {
|
||||
Handle<JSObject> jspromise = Handle<JSObject>::cast(promise);
|
||||
// Mark the promise as already having triggered a message.
|
||||
|
@ -44,41 +44,11 @@ inline StackHandler* StackHandler::next() const {
|
||||
}
|
||||
|
||||
|
||||
inline bool StackHandler::includes(Address address) const {
|
||||
Address start = this->address();
|
||||
Address end = start + StackHandlerConstants::kSize;
|
||||
return start <= address && address <= end;
|
||||
}
|
||||
|
||||
|
||||
inline void StackHandler::Iterate(ObjectVisitor* v, Code* holder) const {
|
||||
v->VisitPointer(context_address());
|
||||
}
|
||||
|
||||
|
||||
inline StackHandler* StackHandler::FromAddress(Address address) {
|
||||
return reinterpret_cast<StackHandler*>(address);
|
||||
}
|
||||
|
||||
|
||||
inline Context* StackHandler::context() const {
|
||||
const int offset = StackHandlerConstants::kContextOffset;
|
||||
return Context::cast(Memory::Object_at(address() + offset));
|
||||
}
|
||||
|
||||
|
||||
inline int StackHandler::index() const {
|
||||
const int offset = StackHandlerConstants::kStateIntOffset;
|
||||
return Memory::int_at(address() + offset);
|
||||
}
|
||||
|
||||
|
||||
inline Object** StackHandler::context_address() const {
|
||||
const int offset = StackHandlerConstants::kContextOffset;
|
||||
return reinterpret_cast<Object**>(address() + offset);
|
||||
}
|
||||
|
||||
|
||||
inline StackFrame::StackFrame(StackFrameIteratorBase* iterator)
|
||||
: iterator_(iterator), isolate_(iterator_->isolate()) {
|
||||
}
|
||||
|
162
src/frames.cc
162
src/frames.cc
@ -380,12 +380,6 @@ Code* StackFrame::GetSafepointData(Isolate* isolate,
|
||||
}
|
||||
|
||||
|
||||
bool StackFrame::HasHandler() const {
|
||||
StackHandlerIterator it(this, top_handler());
|
||||
return !it.done();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool GcSafeCodeContains(HeapObject* object, Address addr);
|
||||
#endif
|
||||
@ -608,15 +602,6 @@ void StandardFrame::SetCallerFp(Address caller_fp) {
|
||||
}
|
||||
|
||||
|
||||
bool StandardFrame::IsExpressionInsideHandler(int n) const {
|
||||
Address address = GetExpressionAddress(n);
|
||||
for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
|
||||
if (it.handler()->includes(address)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void StandardFrame::IterateCompiledFrame(ObjectVisitor* v) const {
|
||||
// Make sure that we're not doing "safe" stack frame iteration. We cannot
|
||||
// possibly find pointers in optimized frames in that state.
|
||||
@ -710,12 +695,6 @@ int StubFrame::GetNumberOfIncomingArguments() const {
|
||||
|
||||
|
||||
void OptimizedFrame::Iterate(ObjectVisitor* v) const {
|
||||
#ifdef DEBUG
|
||||
// Make sure that optimized frames do not contain any stack handlers.
|
||||
StackHandlerIterator it(this, top_handler());
|
||||
DCHECK(it.done());
|
||||
#endif
|
||||
|
||||
IterateCompiledFrame(v);
|
||||
}
|
||||
|
||||
@ -782,6 +761,15 @@ void JavaScriptFrame::Summarize(List<FrameSummary>* functions) {
|
||||
}
|
||||
|
||||
|
||||
int JavaScriptFrame::LookupExceptionHandlerInTable(int* stack_slots) {
|
||||
Code* code = LookupCode();
|
||||
DCHECK(!code->is_optimized_code());
|
||||
HandlerTable* table = HandlerTable::cast(code->handler_table());
|
||||
int pc_offset = static_cast<int>(pc() - code->entry());
|
||||
return table->LookupRange(pc_offset, stack_slots);
|
||||
}
|
||||
|
||||
|
||||
void JavaScriptFrame::PrintFunctionAndOffset(JSFunction* function, Code* code,
|
||||
Address pc, FILE* file,
|
||||
bool print_line_number) {
|
||||
@ -843,66 +831,19 @@ void JavaScriptFrame::PrintTop(Isolate* isolate, FILE* file, bool print_args,
|
||||
}
|
||||
|
||||
|
||||
void JavaScriptFrame::SaveOperandStack(FixedArray* store,
|
||||
int* stack_handler_index) const {
|
||||
void JavaScriptFrame::SaveOperandStack(FixedArray* store) const {
|
||||
int operands_count = store->length();
|
||||
DCHECK_LE(operands_count, ComputeOperandsCount());
|
||||
|
||||
// Visit the stack in LIFO order, saving operands and stack handlers into the
|
||||
// array. The saved stack handlers store a link to the next stack handler,
|
||||
// which will allow RestoreOperandStack to rewind the handlers.
|
||||
StackHandlerIterator it(this, top_handler());
|
||||
int i = operands_count - 1;
|
||||
*stack_handler_index = -1;
|
||||
for (; !it.done(); it.Advance()) {
|
||||
StackHandler* handler = it.handler();
|
||||
// Save operands pushed after the handler was pushed.
|
||||
for (; GetOperandSlot(i) < handler->address(); i--) {
|
||||
store->set(i, GetOperand(i));
|
||||
}
|
||||
DCHECK_GE(i + 1, StackHandlerConstants::kSlotCount);
|
||||
DCHECK_EQ(handler->address(), GetOperandSlot(i));
|
||||
int next_stack_handler_index = i + 1 - StackHandlerConstants::kSlotCount;
|
||||
handler->Unwind(isolate(), store, next_stack_handler_index,
|
||||
*stack_handler_index);
|
||||
*stack_handler_index = next_stack_handler_index;
|
||||
i -= StackHandlerConstants::kSlotCount;
|
||||
}
|
||||
|
||||
// Save any remaining operands.
|
||||
for (; i >= 0; i--) {
|
||||
for (int i = 0; i < operands_count; i++) {
|
||||
store->set(i, GetOperand(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JavaScriptFrame::RestoreOperandStack(FixedArray* store,
|
||||
int stack_handler_index) {
|
||||
void JavaScriptFrame::RestoreOperandStack(FixedArray* store) {
|
||||
int operands_count = store->length();
|
||||
DCHECK_LE(operands_count, ComputeOperandsCount());
|
||||
int i = 0;
|
||||
while (i <= stack_handler_index) {
|
||||
if (i < stack_handler_index) {
|
||||
// An operand.
|
||||
DCHECK_EQ(GetOperand(i), isolate()->heap()->the_hole_value());
|
||||
Memory::Object_at(GetOperandSlot(i)) = store->get(i);
|
||||
i++;
|
||||
} else {
|
||||
// A stack handler.
|
||||
DCHECK_EQ(i, stack_handler_index);
|
||||
// The FixedArray store grows up. The stack grows down. So the operand
|
||||
// slot for i actually points to the bottom of the top word in the
|
||||
// handler. The base of the StackHandler* is the address of the bottom
|
||||
// word, which will be the last slot that is in the handler.
|
||||
int handler_slot_index = i + StackHandlerConstants::kSlotCount - 1;
|
||||
StackHandler *handler =
|
||||
StackHandler::FromAddress(GetOperandSlot(handler_slot_index));
|
||||
stack_handler_index = handler->Rewind(isolate(), store, i, fp());
|
||||
i += StackHandlerConstants::kSlotCount;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < operands_count; i++) {
|
||||
for (int i = 0; i < operands_count; i++) {
|
||||
DCHECK_EQ(GetOperand(i), isolate()->heap()->the_hole_value());
|
||||
Memory::Object_at(GetOperandSlot(i)) = store->get(i);
|
||||
}
|
||||
@ -1035,6 +976,16 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
|
||||
}
|
||||
|
||||
|
||||
int OptimizedFrame::LookupExceptionHandlerInTable(int* stack_slots) {
|
||||
Code* code = LookupCode();
|
||||
DCHECK(code->is_optimized_code());
|
||||
HandlerTable* table = HandlerTable::cast(code->handler_table());
|
||||
int pc_offset = static_cast<int>(pc() - code->entry());
|
||||
*stack_slots = code->stack_slots();
|
||||
return table->LookupReturn(pc_offset);
|
||||
}
|
||||
|
||||
|
||||
DeoptimizationInputData* OptimizedFrame::GetDeoptimizationData(
|
||||
int* deopt_index) {
|
||||
DCHECK(is_optimized());
|
||||
@ -1286,7 +1237,6 @@ void JavaScriptFrame::Print(StringStream* accumulator,
|
||||
accumulator->Add(" // expression stack (top to bottom)\n");
|
||||
}
|
||||
for (int i = expressions_count - 1; i >= expressions_start; i--) {
|
||||
if (IsExpressionInsideHandler(i)) continue;
|
||||
accumulator->Add(" [%02d] : %o\n", i, GetExpression(i));
|
||||
}
|
||||
|
||||
@ -1335,16 +1285,6 @@ void ArgumentsAdaptorFrame::Print(StringStream* accumulator,
|
||||
|
||||
|
||||
void EntryFrame::Iterate(ObjectVisitor* v) const {
|
||||
StackHandlerIterator it(this, top_handler());
|
||||
DCHECK(!it.done());
|
||||
StackHandler* handler = it.handler();
|
||||
handler->Iterate(v, LookupCode());
|
||||
#ifdef DEBUG
|
||||
// Make sure that the entry frame does not contain more than one
|
||||
// stack handler.
|
||||
it.Advance();
|
||||
DCHECK(it.done());
|
||||
#endif
|
||||
IteratePc(v, pc_address(), LookupCode());
|
||||
}
|
||||
|
||||
@ -1353,17 +1293,6 @@ void StandardFrame::IterateExpressions(ObjectVisitor* v) const {
|
||||
const int offset = StandardFrameConstants::kLastObjectOffset;
|
||||
Object** base = &Memory::Object_at(sp());
|
||||
Object** limit = &Memory::Object_at(fp() + offset) + 1;
|
||||
for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
|
||||
StackHandler* handler = it.handler();
|
||||
// Traverse pointers down to - but not including - the next
|
||||
// handler in the handler chain. Update the base to skip the
|
||||
// handler and allow the handler to traverse its own pointers.
|
||||
const Address address = handler->address();
|
||||
v->VisitPointers(base, reinterpret_cast<Object**>(address));
|
||||
base = reinterpret_cast<Object**>(address + StackHandlerConstants::kSize);
|
||||
// Traverse the pointers in the handler itself.
|
||||
handler->Iterate(v, LookupCode());
|
||||
}
|
||||
v->VisitPointers(base, limit);
|
||||
}
|
||||
|
||||
@ -1529,51 +1458,6 @@ InnerPointerToCodeCache::InnerPointerToCodeCacheEntry*
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
void StackHandler::Unwind(Isolate* isolate,
|
||||
FixedArray* array,
|
||||
int offset,
|
||||
int previous_handler_offset) const {
|
||||
STATIC_ASSERT(StackHandlerConstants::kSlotCount >= 3);
|
||||
DCHECK_LE(0, offset);
|
||||
DCHECK_GE(array->length(), offset + StackHandlerConstants::kSlotCount);
|
||||
// Unwinding a stack handler into an array chains it in the opposite
|
||||
// direction, re-using the "next" slot as a "previous" link, so that stack
|
||||
// handlers can be later re-wound in the correct order.
|
||||
int s = Memory::int_at(address() + StackHandlerConstants::kStateIntOffset);
|
||||
array->set(offset, Smi::FromInt(previous_handler_offset)); // next
|
||||
array->set(offset + 1, Smi::FromInt(static_cast<int>(s))); // state
|
||||
array->set(offset + 2, *context_address()); // context
|
||||
|
||||
*isolate->handler_address() = next()->address();
|
||||
}
|
||||
|
||||
|
||||
int StackHandler::Rewind(Isolate* isolate,
|
||||
FixedArray* array,
|
||||
int offset,
|
||||
Address fp) {
|
||||
STATIC_ASSERT(StackHandlerConstants::kSlotCount >= 3);
|
||||
DCHECK_LE(0, offset);
|
||||
DCHECK_GE(array->length(), offset + StackHandlerConstants::kSlotCount);
|
||||
Smi* prev_handler_offset = Smi::cast(array->get(offset));
|
||||
Smi* smi_state = Smi::cast(array->get(offset + 1));
|
||||
Object* context = array->get(offset + 2);
|
||||
|
||||
Memory::Address_at(address() + StackHandlerConstants::kNextOffset) =
|
||||
*isolate->handler_address();
|
||||
Memory::int_at(address() + StackHandlerConstants::kStateIntOffset) =
|
||||
smi_state->value();
|
||||
Memory::Object_at(address() + StackHandlerConstants::kContextOffset) =
|
||||
context;
|
||||
|
||||
*isolate->handler_address() = address();
|
||||
|
||||
return prev_handler_offset->value();
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
int NumRegs(RegList reglist) { return base::bits::CountPopulation32(reglist); }
|
||||
|
||||
|
||||
|
62
src/frames.h
62
src/frames.h
@ -66,57 +66,34 @@ class InnerPointerToCodeCache {
|
||||
};
|
||||
|
||||
|
||||
// Every try-block pushes the context register.
|
||||
class TryBlockConstant : public AllStatic {
|
||||
public:
|
||||
static const int kElementCount = 1;
|
||||
};
|
||||
|
||||
|
||||
class StackHandlerConstants : public AllStatic {
|
||||
public:
|
||||
static const int kNextOffset = 0 * kPointerSize;
|
||||
static const int kStateOffset = 1 * kPointerSize;
|
||||
#if V8_TARGET_LITTLE_ENDIAN || !V8_HOST_ARCH_64_BIT
|
||||
static const int kStateIntOffset = kStateOffset;
|
||||
#else
|
||||
static const int kStateIntOffset = kStateOffset + kIntSize;
|
||||
#endif
|
||||
static const int kContextOffset = 2 * kPointerSize;
|
||||
static const int kNextOffset = 0 * kPointerSize;
|
||||
|
||||
static const int kSize = kContextOffset + kPointerSize;
|
||||
static const int kSize = kNextOffset + kPointerSize;
|
||||
static const int kSlotCount = kSize >> kPointerSizeLog2;
|
||||
};
|
||||
|
||||
|
||||
class StackHandler BASE_EMBEDDED {
|
||||
public:
|
||||
enum Kind {
|
||||
JS_ENTRY,
|
||||
CATCH,
|
||||
FINALLY,
|
||||
};
|
||||
|
||||
// Get the address of this stack handler.
|
||||
inline Address address() const;
|
||||
|
||||
// Get the next stack handler in the chain.
|
||||
inline StackHandler* next() const;
|
||||
|
||||
// Tells whether the given address is inside this handler.
|
||||
inline bool includes(Address address) const;
|
||||
|
||||
// Garbage collection support.
|
||||
inline void Iterate(ObjectVisitor* v, Code* holder) const;
|
||||
|
||||
// Conversion support.
|
||||
static inline StackHandler* FromAddress(Address address);
|
||||
|
||||
// Accessors.
|
||||
inline Context* context() const;
|
||||
inline int index() const;
|
||||
|
||||
// Generator support to preserve stack handlers.
|
||||
void Unwind(Isolate* isolate, FixedArray* array, int offset,
|
||||
int previous_handler_offset) const;
|
||||
int Rewind(Isolate* isolate, FixedArray* array, int offset, Address fp);
|
||||
|
||||
private:
|
||||
inline Object** context_address() const;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler);
|
||||
};
|
||||
|
||||
@ -256,9 +233,6 @@ class StackFrame BASE_EMBEDDED {
|
||||
// Get the id of this stack frame.
|
||||
Id id() const { return static_cast<Id>(OffsetFrom(caller_sp())); }
|
||||
|
||||
// Checks if this frame includes any stack handlers.
|
||||
bool HasHandler() const;
|
||||
|
||||
// Get the top handler from the current stack iterator.
|
||||
inline StackHandler* top_handler() const;
|
||||
|
||||
@ -483,10 +457,6 @@ class StandardFrame: public StackFrame {
|
||||
Address GetExpressionAddress(int n) const;
|
||||
static Address GetExpressionAddress(Address fp, int n);
|
||||
|
||||
// Determines if the n'th expression stack element is in a stack
|
||||
// handler or not. Requires traversing all handlers in this frame.
|
||||
bool IsExpressionInsideHandler(int n) const;
|
||||
|
||||
// Determines if the standard frame for the given frame pointer is
|
||||
// an arguments adaptor frame.
|
||||
static inline bool IsArgumentsAdaptorFrame(Address fp);
|
||||
@ -555,9 +525,9 @@ class JavaScriptFrame: public StandardFrame {
|
||||
inline Object* GetOperand(int index) const;
|
||||
inline int ComputeOperandsCount() const;
|
||||
|
||||
// Generator support to preserve operand stack and stack handlers.
|
||||
void SaveOperandStack(FixedArray* store, int* stack_handler_index) const;
|
||||
void RestoreOperandStack(FixedArray* store, int stack_handler_index);
|
||||
// Generator support to preserve operand stack.
|
||||
void SaveOperandStack(FixedArray* store) const;
|
||||
void RestoreOperandStack(FixedArray* store);
|
||||
|
||||
// Debugger access.
|
||||
void SetParameterValue(int index, Object* value) const;
|
||||
@ -591,6 +561,10 @@ class JavaScriptFrame: public StandardFrame {
|
||||
// Build a list with summaries for this frame including all inlined frames.
|
||||
virtual void Summarize(List<FrameSummary>* frames);
|
||||
|
||||
// Lookup exception handler for current {pc}, returns -1 if none found. Also
|
||||
// returns the expected number of stack slots at the handler site.
|
||||
virtual int LookupExceptionHandlerInTable(int* stack_slots);
|
||||
|
||||
// Architecture-specific register description.
|
||||
static Register fp_register();
|
||||
static Register context_register();
|
||||
@ -663,6 +637,10 @@ class OptimizedFrame : public JavaScriptFrame {
|
||||
|
||||
virtual void Summarize(List<FrameSummary>* frames);
|
||||
|
||||
// Lookup exception handler for current {pc}, returns -1 if none found. Also
|
||||
// returns the expected number of stack slots at the handler site.
|
||||
virtual int LookupExceptionHandlerInTable(int* stack_slots);
|
||||
|
||||
DeoptimizationInputData* GetDeoptimizationData(int* deopt_index);
|
||||
|
||||
protected:
|
||||
|
@ -1380,7 +1380,6 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
||||
Label try_entry, handler_entry, exit;
|
||||
__ jmp(&try_entry);
|
||||
__ bind(&handler_entry);
|
||||
handler_table()->set(stmt->index(), Smi::FromInt(handler_entry.pos()));
|
||||
// Exception handler code, the exception is in the result register.
|
||||
// Extend the context before executing the catch block.
|
||||
{ Comment cmnt(masm_, "[ Extend catch context");
|
||||
@ -1406,11 +1405,11 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
||||
|
||||
// Try block code. Sets up the exception handler chain.
|
||||
__ bind(&try_entry);
|
||||
__ PushTryHandler(StackHandler::CATCH, stmt->index());
|
||||
EnterTryBlock(stmt->index(), &handler_entry);
|
||||
{ TryCatch try_body(this);
|
||||
Visit(stmt->try_block());
|
||||
}
|
||||
__ PopTryHandler();
|
||||
ExitTryBlock(stmt->index());
|
||||
__ bind(&exit);
|
||||
}
|
||||
|
||||
@ -1444,7 +1443,6 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
||||
// Jump to try-handler setup and try-block code.
|
||||
__ jmp(&try_entry);
|
||||
__ bind(&handler_entry);
|
||||
handler_table()->set(stmt->index(), Smi::FromInt(handler_entry.pos()));
|
||||
// Exception handler code. This code is only executed when an exception
|
||||
// is thrown. The exception is in the result register, and must be
|
||||
// preserved by the finally block. Call the finally block and then
|
||||
@ -1463,11 +1461,11 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
||||
|
||||
// Set up try handler.
|
||||
__ bind(&try_entry);
|
||||
__ PushTryHandler(StackHandler::FINALLY, stmt->index());
|
||||
EnterTryBlock(stmt->index(), &handler_entry);
|
||||
{ TryFinally try_body(this, &finally_entry);
|
||||
Visit(stmt->try_block());
|
||||
}
|
||||
__ PopTryHandler();
|
||||
ExitTryBlock(stmt->index());
|
||||
// Execute the finally block on the way out. Clobber the unpredictable
|
||||
// value in the result register with one that's safe for GC because the
|
||||
// finally block will unconditionally preserve the result register on the
|
||||
@ -1622,13 +1620,54 @@ void FullCodeGenerator::VisitThrow(Throw* expr) {
|
||||
}
|
||||
|
||||
|
||||
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryCatch::Exit(
|
||||
int* stack_depth,
|
||||
int* context_length) {
|
||||
void FullCodeGenerator::EnterTryBlock(int index, Label* handler) {
|
||||
handler_table()->SetRangeStart(index, masm()->pc_offset());
|
||||
handler_table()->SetRangeHandler(index, handler->pos());
|
||||
|
||||
// Determine expression stack depth of try statement.
|
||||
int stack_depth = info_->scope()->num_stack_slots(); // Include stack locals.
|
||||
for (NestedStatement* current = nesting_stack_; current != NULL; /*nop*/) {
|
||||
current = current->AccumulateDepth(&stack_depth);
|
||||
}
|
||||
handler_table()->SetRangeDepth(index, stack_depth);
|
||||
|
||||
// Push context onto operand stack.
|
||||
STATIC_ASSERT(TryBlockConstant::kElementCount == 1);
|
||||
__ Push(context_register());
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::ExitTryBlock(int index) {
|
||||
handler_table()->SetRangeEnd(index, masm()->pc_offset());
|
||||
|
||||
// Drop context from operand stack.
|
||||
__ Drop(TryBlockConstant::kElementCount);
|
||||
}
|
||||
|
||||
|
||||
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
|
||||
int* stack_depth, int* context_length) {
|
||||
// The macros used here must preserve the result register.
|
||||
__ Drop(*stack_depth);
|
||||
__ PopTryHandler();
|
||||
|
||||
// Because the handler block contains the context of the finally
|
||||
// code, we can restore it directly from there for the finally code
|
||||
// rather than iteratively unwinding contexts via their previous
|
||||
// links.
|
||||
if (*context_length > 0) {
|
||||
__ Drop(*stack_depth); // Down to the handler block.
|
||||
// Restore the context to its dedicated register and the stack.
|
||||
STATIC_ASSERT(TryFinally::kElementCount == 1);
|
||||
__ Pop(codegen_->context_register());
|
||||
codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset,
|
||||
codegen_->context_register());
|
||||
} else {
|
||||
// Down to the handler block and also drop context.
|
||||
__ Drop(*stack_depth + kElementCount);
|
||||
}
|
||||
__ Call(finally_entry_);
|
||||
|
||||
*stack_depth = 0;
|
||||
*context_length = 0;
|
||||
return previous_;
|
||||
}
|
||||
|
||||
|
@ -156,6 +156,11 @@ class FullCodeGenerator: public AstVisitor {
|
||||
return previous_;
|
||||
}
|
||||
|
||||
// Like the Exit() method above, but limited to accumulating stack depth.
|
||||
virtual NestedStatement* AccumulateDepth(int* stack_depth) {
|
||||
return previous_;
|
||||
}
|
||||
|
||||
protected:
|
||||
MacroAssembler* masm() { return codegen_->masm(); }
|
||||
|
||||
@ -225,22 +230,36 @@ class FullCodeGenerator: public AstVisitor {
|
||||
// The try block of a try/catch statement.
|
||||
class TryCatch : public NestedStatement {
|
||||
public:
|
||||
explicit TryCatch(FullCodeGenerator* codegen) : NestedStatement(codegen) {
|
||||
}
|
||||
static const int kElementCount = TryBlockConstant::kElementCount;
|
||||
|
||||
explicit TryCatch(FullCodeGenerator* codegen) : NestedStatement(codegen) {}
|
||||
virtual ~TryCatch() {}
|
||||
|
||||
virtual NestedStatement* Exit(int* stack_depth, int* context_length);
|
||||
virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
|
||||
*stack_depth += kElementCount;
|
||||
return previous_;
|
||||
}
|
||||
virtual NestedStatement* AccumulateDepth(int* stack_depth) {
|
||||
*stack_depth += kElementCount;
|
||||
return previous_;
|
||||
}
|
||||
};
|
||||
|
||||
// The try block of a try/finally statement.
|
||||
class TryFinally : public NestedStatement {
|
||||
public:
|
||||
static const int kElementCount = TryBlockConstant::kElementCount;
|
||||
|
||||
TryFinally(FullCodeGenerator* codegen, Label* finally_entry)
|
||||
: NestedStatement(codegen), finally_entry_(finally_entry) {
|
||||
}
|
||||
virtual ~TryFinally() {}
|
||||
|
||||
virtual NestedStatement* Exit(int* stack_depth, int* context_length);
|
||||
virtual NestedStatement* AccumulateDepth(int* stack_depth) {
|
||||
*stack_depth += kElementCount;
|
||||
return previous_;
|
||||
}
|
||||
|
||||
private:
|
||||
Label* finally_entry_;
|
||||
@ -251,13 +270,17 @@ class FullCodeGenerator: public AstVisitor {
|
||||
public:
|
||||
static const int kElementCount = 3;
|
||||
|
||||
explicit Finally(FullCodeGenerator* codegen) : NestedStatement(codegen) { }
|
||||
explicit Finally(FullCodeGenerator* codegen) : NestedStatement(codegen) {}
|
||||
virtual ~Finally() {}
|
||||
|
||||
virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
|
||||
*stack_depth += kElementCount;
|
||||
return previous_;
|
||||
}
|
||||
virtual NestedStatement* AccumulateDepth(int* stack_depth) {
|
||||
*stack_depth += kElementCount;
|
||||
return previous_;
|
||||
}
|
||||
};
|
||||
|
||||
// The body of a for/in loop.
|
||||
@ -274,6 +297,10 @@ class FullCodeGenerator: public AstVisitor {
|
||||
*stack_depth += kElementCount;
|
||||
return previous_;
|
||||
}
|
||||
virtual NestedStatement* AccumulateDepth(int* stack_depth) {
|
||||
*stack_depth += kElementCount;
|
||||
return previous_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -675,6 +702,8 @@ class FullCodeGenerator: public AstVisitor {
|
||||
void SetSourcePosition(int pos);
|
||||
|
||||
// Non-local control flow support.
|
||||
void EnterTryBlock(int handler_index, Label* handler);
|
||||
void ExitTryBlock(int handler_index);
|
||||
void EnterFinallyBlock();
|
||||
void ExitFinallyBlock();
|
||||
|
||||
@ -730,7 +759,7 @@ class FullCodeGenerator: public AstVisitor {
|
||||
void PopulateDeoptimizationData(Handle<Code> code);
|
||||
void PopulateTypeFeedbackInfo(Handle<Code> code);
|
||||
|
||||
Handle<FixedArray> handler_table() { return handler_table_; }
|
||||
Handle<HandlerTable> handler_table() { return handler_table_; }
|
||||
|
||||
struct BailoutEntry {
|
||||
BailoutId id;
|
||||
@ -948,7 +977,7 @@ class FullCodeGenerator: public AstVisitor {
|
||||
ZoneList<BailoutEntry> bailout_entries_;
|
||||
ZoneList<BackEdgeEntry> back_edges_;
|
||||
int ic_total_count_;
|
||||
Handle<FixedArray> handler_table_;
|
||||
Handle<HandlerTable> handler_table_;
|
||||
Handle<Cell> profiling_counter_;
|
||||
bool generate_debug_code_;
|
||||
|
||||
|
@ -2556,9 +2556,8 @@ void CEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ mov(esp, Operand::StaticVariable(pending_handler_sp_address));
|
||||
__ mov(ebp, Operand::StaticVariable(pending_handler_fp_address));
|
||||
|
||||
// If the handler is a JS frame, restore the context to the frame.
|
||||
// (kind == ENTRY) == (ebp == 0) == (esi == 0), so we could test either
|
||||
// ebp or esi.
|
||||
// If the handler is a JS frame, restore the context to the frame. Note that
|
||||
// the context will be set to (esi == 0) for non-JS frames.
|
||||
Label skip;
|
||||
__ test(esi, esi);
|
||||
__ j(zero, &skip, Label::kNear);
|
||||
@ -2619,10 +2618,9 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ mov(eax, Immediate(isolate()->factory()->exception()));
|
||||
__ jmp(&exit);
|
||||
|
||||
// Invoke: Link this frame into the handler chain. There's only one
|
||||
// handler block in this code object, so its index is 0.
|
||||
// Invoke: Link this frame into the handler chain.
|
||||
__ bind(&invoke);
|
||||
__ PushTryHandler(StackHandler::JS_ENTRY, 0);
|
||||
__ PushStackHandler();
|
||||
|
||||
// Clear any pending exceptions.
|
||||
__ mov(edx, Immediate(isolate()->factory()->the_hole_value()));
|
||||
@ -2648,7 +2646,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ call(edx);
|
||||
|
||||
// Unlink this frame from the handler chain.
|
||||
__ PopTryHandler();
|
||||
__ PopStackHandler();
|
||||
|
||||
__ bind(&exit);
|
||||
// Check if the current stack frame is marked as the outermost JS frame.
|
||||
|
@ -95,7 +95,8 @@ class JumpPatchSite BASE_EMBEDDED {
|
||||
void FullCodeGenerator::Generate() {
|
||||
CompilationInfo* info = info_;
|
||||
handler_table_ =
|
||||
isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
|
||||
Handle<HandlerTable>::cast(isolate()->factory()->NewFixedArray(
|
||||
HandlerTable::LengthForRange(function()->handler_count()), TENURED));
|
||||
|
||||
profiling_counter_ = isolate()->factory()->NewCell(
|
||||
Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate()));
|
||||
@ -2121,7 +2122,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
|
||||
// catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; }
|
||||
__ bind(&l_catch);
|
||||
handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
|
||||
__ mov(load_name, isolate()->factory()->throw_string()); // "throw"
|
||||
__ push(load_name); // "throw"
|
||||
__ push(Operand(esp, 2 * kPointerSize)); // iter
|
||||
@ -2133,16 +2133,17 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
// re-boxing.
|
||||
__ bind(&l_try);
|
||||
__ pop(eax); // result
|
||||
__ PushTryHandler(StackHandler::CATCH, expr->index());
|
||||
const int handler_size = StackHandlerConstants::kSize;
|
||||
EnterTryBlock(expr->index(), &l_catch);
|
||||
const int try_block_size = TryCatch::kElementCount * kPointerSize;
|
||||
__ push(eax); // result
|
||||
__ jmp(&l_suspend);
|
||||
__ bind(&l_continuation);
|
||||
__ jmp(&l_resume);
|
||||
__ bind(&l_suspend);
|
||||
const int generator_object_depth = kPointerSize + handler_size;
|
||||
const int generator_object_depth = kPointerSize + try_block_size;
|
||||
__ mov(eax, Operand(esp, generator_object_depth));
|
||||
__ push(eax); // g
|
||||
__ push(Immediate(Smi::FromInt(expr->index()))); // handler-index
|
||||
DCHECK(l_continuation.pos() > 0 && Smi::IsValid(l_continuation.pos()));
|
||||
__ mov(FieldOperand(eax, JSGeneratorObject::kContinuationOffset),
|
||||
Immediate(Smi::FromInt(l_continuation.pos())));
|
||||
@ -2150,13 +2151,13 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
__ mov(ecx, esi);
|
||||
__ RecordWriteField(eax, JSGeneratorObject::kContextOffset, ecx, edx,
|
||||
kDontSaveFPRegs);
|
||||
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
|
||||
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 2);
|
||||
__ mov(context_register(),
|
||||
Operand(ebp, StandardFrameConstants::kContextOffset));
|
||||
__ pop(eax); // result
|
||||
EmitReturnSequence();
|
||||
__ bind(&l_resume); // received in eax
|
||||
__ PopTryHandler();
|
||||
ExitTryBlock(expr->index());
|
||||
|
||||
// receiver = iter; f = iter.next; arg = received;
|
||||
__ bind(&l_next);
|
||||
@ -5281,33 +5282,6 @@ void FullCodeGenerator::ExitFinallyBlock() {
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
#define __ ACCESS_MASM(masm())
|
||||
|
||||
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
|
||||
int* stack_depth,
|
||||
int* context_length) {
|
||||
// The macros used here must preserve the result register.
|
||||
|
||||
// Because the handler block contains the context of the finally
|
||||
// code, we can restore it directly from there for the finally code
|
||||
// rather than iteratively unwinding contexts via their previous
|
||||
// links.
|
||||
__ Drop(*stack_depth); // Down to the handler block.
|
||||
if (*context_length > 0) {
|
||||
// Restore the context to its dedicated register and the stack.
|
||||
__ mov(esi, Operand(esp, StackHandlerConstants::kContextOffset));
|
||||
__ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
|
||||
}
|
||||
__ PopTryHandler();
|
||||
__ call(finally_entry_);
|
||||
|
||||
*stack_depth = 0;
|
||||
*context_length = 0;
|
||||
return previous_;
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
|
@ -1023,34 +1023,21 @@ void MacroAssembler::LeaveApiExitFrame(bool restore_context) {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
|
||||
int handler_index) {
|
||||
void MacroAssembler::PushStackHandler() {
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize);
|
||||
|
||||
// We will build up the handler from the bottom by pushing on the stack.
|
||||
// First push the context.
|
||||
if (kind == StackHandler::JS_ENTRY) {
|
||||
push(Immediate(Smi::FromInt(0))); // No context.
|
||||
} else {
|
||||
push(esi);
|
||||
}
|
||||
|
||||
// Push the index.
|
||||
push(Immediate(handler_index));
|
||||
|
||||
// Link the current handler as the next handler.
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
push(Operand::StaticVariable(handler_address));
|
||||
|
||||
// Set this new handler as the current one.
|
||||
mov(Operand::StaticVariable(handler_address), esp);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PopTryHandler() {
|
||||
void MacroAssembler::PopStackHandler() {
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
pop(Operand::StaticVariable(handler_address));
|
||||
|
@ -570,11 +570,11 @@ class MacroAssembler: public Assembler {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exception handling
|
||||
|
||||
// Push a new try handler and link it into try handler chain.
|
||||
void PushTryHandler(StackHandler::Kind kind, int handler_index);
|
||||
// Push a new stack handler and link it into stack handler chain.
|
||||
void PushStackHandler();
|
||||
|
||||
// Unlink the stack handler on top of the stack from the try handler chain.
|
||||
void PopTryHandler();
|
||||
// Unlink the stack handler on top of the stack from the stack handler chain.
|
||||
void PopStackHandler();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inline caching support
|
||||
|
147
src/isolate.cc
147
src/isolate.cc
@ -978,7 +978,7 @@ Object* Isolate::Throw(Object* exception, MessageLocation* location) {
|
||||
// present. This flag is intended for use by JavaScript developers, so
|
||||
// print a user-friendly stack trace (not an internal one).
|
||||
if (FLAG_abort_on_uncaught_exception &&
|
||||
!PredictWhetherExceptionIsCaught(*exception_handle)) {
|
||||
PredictExceptionCatcher() != CAUGHT_BY_JAVASCRIPT) {
|
||||
FLAG_abort_on_uncaught_exception = false; // Prevent endless recursion.
|
||||
PrintF(stderr, "%s\n\nFROM\n",
|
||||
MessageHandler::GetLocalizedMessage(this, message_obj).get());
|
||||
@ -1003,18 +1003,6 @@ Object* Isolate::ReThrow(Object* exception) {
|
||||
}
|
||||
|
||||
|
||||
// TODO(turbofan): Make sure table is sorted and use binary search.
|
||||
static int LookupInHandlerTable(Code* code, int pc_offset) {
|
||||
FixedArray* handler_table = code->handler_table();
|
||||
for (int i = 0; i < handler_table->length(); i += 2) {
|
||||
int return_offset = Smi::cast(handler_table->get(i))->value();
|
||||
int handler_offset = Smi::cast(handler_table->get(i + 1))->value();
|
||||
if (pc_offset == return_offset) return handler_offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
Object* Isolate::FindHandler() {
|
||||
Object* exception = pending_exception();
|
||||
|
||||
@ -1036,7 +1024,6 @@ Object* Isolate::FindHandler() {
|
||||
// For JSEntryStub frames we always have a handler.
|
||||
if (frame->is_entry() || frame->is_entry_construct()) {
|
||||
StackHandler* handler = frame->top_handler();
|
||||
DCHECK_EQ(0, handler->index());
|
||||
|
||||
// Restore the next handler.
|
||||
thread_local_top()->handler_ = handler->next()->address();
|
||||
@ -1048,39 +1035,44 @@ Object* Isolate::FindHandler() {
|
||||
break;
|
||||
}
|
||||
|
||||
// For JavaScript frames which have a handler, we use the handler.
|
||||
if (frame->is_java_script() && catchable_by_js && frame->HasHandler()) {
|
||||
StackHandler* handler = frame->top_handler();
|
||||
|
||||
// Restore the next handler.
|
||||
thread_local_top()->handler_ = handler->next()->address();
|
||||
|
||||
// Gather information from the handler.
|
||||
code = frame->LookupCode();
|
||||
context = handler->context();
|
||||
offset = Smi::cast(code->handler_table()->get(handler->index()))->value();
|
||||
handler_sp = handler->address() + StackHandlerConstants::kSize;
|
||||
handler_fp = frame->fp();
|
||||
break;
|
||||
}
|
||||
|
||||
// For optimized frames we perform a lookup in the handler table.
|
||||
if (frame->is_optimized() && catchable_by_js) {
|
||||
Code* frame_code = frame->LookupCode();
|
||||
DCHECK(frame_code->is_optimized_code());
|
||||
int pc_offset = static_cast<int>(frame->pc() - frame_code->entry());
|
||||
int handler_offset = LookupInHandlerTable(frame_code, pc_offset);
|
||||
if (handler_offset < 0) continue;
|
||||
OptimizedFrame* js_frame = static_cast<OptimizedFrame*>(frame);
|
||||
int stack_slots = 0; // Will contain stack slot count of frame.
|
||||
offset = js_frame->LookupExceptionHandlerInTable(&stack_slots);
|
||||
if (offset < 0) continue;
|
||||
|
||||
// Compute the stack pointer from the frame pointer. This ensures that
|
||||
// argument slots on the stack are dropped as returning would.
|
||||
Address return_sp = frame->fp() -
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp -
|
||||
frame_code->stack_slots() * kPointerSize;
|
||||
stack_slots * kPointerSize;
|
||||
|
||||
// Gather information from the frame.
|
||||
code = frame_code;
|
||||
offset = handler_offset;
|
||||
code = frame->LookupCode();
|
||||
handler_sp = return_sp;
|
||||
handler_fp = frame->fp();
|
||||
break;
|
||||
}
|
||||
|
||||
// For JavaScript frames we perform a range lookup in the handler table.
|
||||
if (frame->is_java_script() && catchable_by_js) {
|
||||
JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(frame);
|
||||
int stack_slots = 0; // Will contain operand stack depth of handler.
|
||||
offset = js_frame->LookupExceptionHandlerInTable(&stack_slots);
|
||||
if (offset < 0) continue;
|
||||
|
||||
// Compute the stack pointer from the frame pointer. This ensures that
|
||||
// operand stack slots are dropped for nested statements. Also restore
|
||||
// correct context for the handler which is pushed within the try-block.
|
||||
Address return_sp = frame->fp() -
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp -
|
||||
stack_slots * kPointerSize;
|
||||
STATIC_ASSERT(TryBlockConstant::kElementCount == 1);
|
||||
context = Context::cast(Memory::Object_at(return_sp - kPointerSize));
|
||||
|
||||
// Gather information from the frame.
|
||||
code = frame->LookupCode();
|
||||
handler_sp = return_sp;
|
||||
handler_fp = frame->fp();
|
||||
break;
|
||||
@ -1103,33 +1095,40 @@ Object* Isolate::FindHandler() {
|
||||
}
|
||||
|
||||
|
||||
// TODO(mstarzinger): We shouldn't need the exception object here.
|
||||
bool Isolate::PredictWhetherExceptionIsCaught(Object* exception) {
|
||||
if (IsExternalHandlerOnTop(exception)) return true;
|
||||
Isolate::CatchType Isolate::PredictExceptionCatcher() {
|
||||
Address external_handler = thread_local_top()->try_catch_handler_address();
|
||||
Address entry_handler = Isolate::handler(thread_local_top());
|
||||
if (IsExternalHandlerOnTop(nullptr)) return CAUGHT_BY_EXTERNAL;
|
||||
|
||||
// Search for a JavaScript handler by performing a full walk over the stack
|
||||
// and dispatching according to the frame type.
|
||||
// Search for an exception handler by performing a full walk over the stack.
|
||||
for (StackFrameIterator iter(this); !iter.done(); iter.Advance()) {
|
||||
StackFrame* frame = iter.frame();
|
||||
|
||||
// For JavaScript frames which have a handler, we use the handler.
|
||||
if (frame->is_java_script() && frame->HasHandler()) {
|
||||
return true;
|
||||
// For JSEntryStub frames we update the JS_ENTRY handler.
|
||||
if (frame->is_entry() || frame->is_entry_construct()) {
|
||||
entry_handler = frame->top_handler()->next()->address();
|
||||
}
|
||||
|
||||
// For optimized frames we perform a lookup in the handler table.
|
||||
if (frame->is_optimized()) {
|
||||
Code* frame_code = frame->LookupCode();
|
||||
DCHECK(frame_code->is_optimized_code());
|
||||
int pc_offset = static_cast<int>(frame->pc() - frame_code->entry());
|
||||
int handler_offset = LookupInHandlerTable(frame_code, pc_offset);
|
||||
if (handler_offset < 0) continue;
|
||||
return true;
|
||||
// For JavaScript frames we perform a lookup in the handler table.
|
||||
if (frame->is_java_script()) {
|
||||
JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(frame);
|
||||
int stack_slots = 0; // The computed stack slot count is not used.
|
||||
if (js_frame->LookupExceptionHandlerInTable(&stack_slots) > 0) {
|
||||
return CAUGHT_BY_JAVASCRIPT;
|
||||
}
|
||||
}
|
||||
|
||||
// The exception has been externally caught if and only if there is an
|
||||
// external handler which is on top of the top-most JS_ENTRY handler.
|
||||
if (external_handler != nullptr && !try_catch_handler()->is_verbose_) {
|
||||
if (entry_handler == nullptr || entry_handler > external_handler) {
|
||||
return CAUGHT_BY_EXTERNAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handler not found.
|
||||
return false;
|
||||
return NOT_CAUGHT;
|
||||
}
|
||||
|
||||
|
||||
@ -1511,13 +1510,16 @@ bool Isolate::OptionalRescheduleException(bool is_bottom_call) {
|
||||
}
|
||||
|
||||
|
||||
void Isolate::PushPromise(Handle<JSObject> promise) {
|
||||
void Isolate::PushPromise(Handle<JSObject> promise,
|
||||
Handle<JSFunction> function) {
|
||||
ThreadLocalTop* tltop = thread_local_top();
|
||||
PromiseOnStack* prev = tltop->promise_on_stack_;
|
||||
StackHandler* handler = StackHandler::FromAddress(Isolate::handler(tltop));
|
||||
Handle<JSObject> global_handle =
|
||||
Handle<JSObject> global_promise =
|
||||
Handle<JSObject>::cast(global_handles()->Create(*promise));
|
||||
tltop->promise_on_stack_ = new PromiseOnStack(handler, global_handle, prev);
|
||||
Handle<JSFunction> global_function =
|
||||
Handle<JSFunction>::cast(global_handles()->Create(*function));
|
||||
tltop->promise_on_stack_ =
|
||||
new PromiseOnStack(global_function, global_promise, prev);
|
||||
}
|
||||
|
||||
|
||||
@ -1525,10 +1527,12 @@ void Isolate::PopPromise() {
|
||||
ThreadLocalTop* tltop = thread_local_top();
|
||||
if (tltop->promise_on_stack_ == NULL) return;
|
||||
PromiseOnStack* prev = tltop->promise_on_stack_->prev();
|
||||
Handle<Object> global_handle = tltop->promise_on_stack_->promise();
|
||||
Handle<Object> global_function = tltop->promise_on_stack_->function();
|
||||
Handle<Object> global_promise = tltop->promise_on_stack_->promise();
|
||||
delete tltop->promise_on_stack_;
|
||||
tltop->promise_on_stack_ = prev;
|
||||
global_handles()->Destroy(global_handle.location());
|
||||
global_handles()->Destroy(global_function.location());
|
||||
global_handles()->Destroy(global_promise.location());
|
||||
}
|
||||
|
||||
|
||||
@ -1536,12 +1540,21 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
|
||||
Handle<Object> undefined = factory()->undefined_value();
|
||||
ThreadLocalTop* tltop = thread_local_top();
|
||||
if (tltop->promise_on_stack_ == NULL) return undefined;
|
||||
StackHandler* promise_try = tltop->promise_on_stack_->handler();
|
||||
// Find the top-most try-catch handler.
|
||||
StackHandler* handler = StackHandler::FromAddress(Isolate::handler(tltop));
|
||||
// Throwing inside a Promise only leads to a reject if not caught by an inner
|
||||
// try-catch or try-finally.
|
||||
if (handler == promise_try) return tltop->promise_on_stack_->promise();
|
||||
Handle<JSFunction> promise_function = tltop->promise_on_stack_->function();
|
||||
// Find the top-most try-catch or try-finally handler.
|
||||
if (PredictExceptionCatcher() != CAUGHT_BY_JAVASCRIPT) return undefined;
|
||||
for (JavaScriptFrameIterator it(this); !it.done(); it.Advance()) {
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
int stack_slots = 0; // The computed stack slot count is not used.
|
||||
if (frame->LookupExceptionHandlerInTable(&stack_slots) > 0) {
|
||||
// Throwing inside a Promise only leads to a reject if not caught by an
|
||||
// inner try-catch or try-finally.
|
||||
if (frame->function() == *promise_function) {
|
||||
return tltop->promise_on_stack_->promise();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -687,7 +687,7 @@ class Isolate {
|
||||
bool OptionalRescheduleException(bool is_bottom_call);
|
||||
|
||||
// Push and pop a promise and the current try-catch handler.
|
||||
void PushPromise(Handle<JSObject> promise);
|
||||
void PushPromise(Handle<JSObject> promise, Handle<JSFunction> function);
|
||||
void PopPromise();
|
||||
Handle<Object> GetPromiseOnStackOnThrow();
|
||||
|
||||
@ -764,11 +764,12 @@ class Isolate {
|
||||
// clears and returns the current pending exception.
|
||||
Object* FindHandler();
|
||||
|
||||
// Tries to predict whether the exception will be caught. Note that this can
|
||||
// Tries to predict whether an exception will be caught. Note that this can
|
||||
// only produce an estimate, because it is undecidable whether a finally
|
||||
// clause will consume or re-throw an exception. We conservatively assume any
|
||||
// finally clause will behave as if the exception were consumed.
|
||||
bool PredictWhetherExceptionIsCaught(Object* exception);
|
||||
enum CatchType { NOT_CAUGHT, CAUGHT_BY_JAVASCRIPT, CAUGHT_BY_EXTERNAL };
|
||||
CatchType PredictExceptionCatcher();
|
||||
|
||||
void ScheduleThrow(Object* exception);
|
||||
// Re-set pending message, script and positions reported to the TryCatch
|
||||
@ -1370,15 +1371,15 @@ class Isolate {
|
||||
|
||||
class PromiseOnStack {
|
||||
public:
|
||||
PromiseOnStack(StackHandler* handler, Handle<JSObject> promise,
|
||||
PromiseOnStack(Handle<JSFunction> function, Handle<JSObject> promise,
|
||||
PromiseOnStack* prev)
|
||||
: handler_(handler), promise_(promise), prev_(prev) {}
|
||||
StackHandler* handler() { return handler_; }
|
||||
: function_(function), promise_(promise), prev_(prev) {}
|
||||
Handle<JSFunction> function() { return function_; }
|
||||
Handle<JSObject> promise() { return promise_; }
|
||||
PromiseOnStack* prev() { return prev_; }
|
||||
|
||||
private:
|
||||
StackHandler* handler_;
|
||||
Handle<JSFunction> function_;
|
||||
Handle<JSObject> promise_;
|
||||
PromiseOnStack* prev_;
|
||||
};
|
||||
|
@ -425,7 +425,6 @@ void JSGeneratorObject::JSGeneratorObjectVerify() {
|
||||
VerifyObjectField(kReceiverOffset);
|
||||
VerifyObjectField(kOperandStackOffset);
|
||||
VerifyObjectField(kContinuationOffset);
|
||||
VerifyObjectField(kStackHandlerIndexOffset);
|
||||
}
|
||||
|
||||
|
||||
|
@ -755,6 +755,14 @@ bool Object::IsDeoptimizationOutputData() const {
|
||||
}
|
||||
|
||||
|
||||
bool Object::IsHandlerTable() const {
|
||||
if (!IsFixedArray()) return false;
|
||||
// There's actually no way to see the difference between a fixed array and
|
||||
// a handler table array.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Object::IsDependentCode() const {
|
||||
if (!IsFixedArray()) return false;
|
||||
// There's actually no way to see the difference between a fixed array and
|
||||
@ -3342,6 +3350,7 @@ CAST_ACCESSOR(FixedDoubleArray)
|
||||
CAST_ACCESSOR(FixedTypedArrayBase)
|
||||
CAST_ACCESSOR(Foreign)
|
||||
CAST_ACCESSOR(GlobalObject)
|
||||
CAST_ACCESSOR(HandlerTable)
|
||||
CAST_ACCESSOR(HeapObject)
|
||||
CAST_ACCESSOR(JSArray)
|
||||
CAST_ACCESSOR(JSArrayBuffer)
|
||||
@ -6229,7 +6238,6 @@ ACCESSORS(JSGeneratorObject, context, Context, kContextOffset)
|
||||
ACCESSORS(JSGeneratorObject, receiver, Object, kReceiverOffset)
|
||||
SMI_ACCESSORS(JSGeneratorObject, continuation, kContinuationOffset)
|
||||
ACCESSORS(JSGeneratorObject, operand_stack, FixedArray, kOperandStackOffset)
|
||||
SMI_ACCESSORS(JSGeneratorObject, stack_handler_index, kStackHandlerIndexOffset)
|
||||
|
||||
bool JSGeneratorObject::is_suspended() {
|
||||
DCHECK_LT(kGeneratorExecuting, kGeneratorClosed);
|
||||
|
@ -8468,6 +8468,36 @@ Handle<DeoptimizationOutputData> DeoptimizationOutputData::New(
|
||||
}
|
||||
|
||||
|
||||
int HandlerTable::LookupRange(int pc_offset, int* stack_depth_out) {
|
||||
int innermost_handler = -1, innermost_start = -1;
|
||||
for (int i = 0; i < length(); i += kRangeEntrySize) {
|
||||
int start_offset = Smi::cast(get(i + kRangeStartIndex))->value();
|
||||
int end_offset = Smi::cast(get(i + kRangeEndIndex))->value();
|
||||
int handler_offset = Smi::cast(get(i + kRangeHandlerIndex))->value();
|
||||
int stack_depth = Smi::cast(get(i + kRangeDepthIndex))->value();
|
||||
if (pc_offset > start_offset && pc_offset <= end_offset) {
|
||||
DCHECK_NE(start_offset, innermost_start);
|
||||
if (start_offset < innermost_start) continue;
|
||||
innermost_handler = handler_offset;
|
||||
innermost_start = start_offset;
|
||||
*stack_depth_out = stack_depth;
|
||||
}
|
||||
}
|
||||
return innermost_handler;
|
||||
}
|
||||
|
||||
|
||||
// TODO(turbofan): Make sure table is sorted and use binary search.
|
||||
int HandlerTable::LookupReturn(int pc_offset) {
|
||||
for (int i = 0; i < length(); i += kReturnEntrySize) {
|
||||
int return_offset = Smi::cast(get(i + kReturnOffsetIndex))->value();
|
||||
int handler_offset = Smi::cast(get(i + kReturnHandlerIndex))->value();
|
||||
if (pc_offset == return_offset) return handler_offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
|
||||
if (IsEmpty()) return other->IsEmpty();
|
||||
@ -11494,6 +11524,30 @@ void DeoptimizationOutputData::DeoptimizationOutputDataPrint(
|
||||
}
|
||||
|
||||
|
||||
void HandlerTable::HandlerTableRangePrint(std::ostream& os) {
|
||||
os << " from to hdlr\n";
|
||||
for (int i = 0; i < length(); i += kRangeEntrySize) {
|
||||
int pc_start = Smi::cast(get(i + kRangeStartIndex))->value();
|
||||
int pc_end = Smi::cast(get(i + kRangeEndIndex))->value();
|
||||
int handler = Smi::cast(get(i + kRangeHandlerIndex))->value();
|
||||
int depth = Smi::cast(get(i + kRangeDepthIndex))->value();
|
||||
os << " (" << std::setw(4) << pc_start << "," << std::setw(4) << pc_end
|
||||
<< ") -> " << std::setw(4) << handler << " (depth=" << depth << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HandlerTable::HandlerTableReturnPrint(std::ostream& os) {
|
||||
os << " off hdlr\n";
|
||||
for (int i = 0; i < length(); i += kReturnEntrySize) {
|
||||
int pc_offset = Smi::cast(get(i + kReturnOffsetIndex))->value();
|
||||
int handler = Smi::cast(get(i + kReturnHandlerIndex))->value();
|
||||
os << " " << std::setw(4) << pc_offset << " -> " << std::setw(4)
|
||||
<< handler << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char* Code::ICState2String(InlineCacheState state) {
|
||||
switch (state) {
|
||||
case UNINITIALIZED: return "UNINITIALIZED";
|
||||
@ -11640,13 +11694,12 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT
|
||||
#endif
|
||||
}
|
||||
|
||||
if (handler_table()->length() > 0 && is_turbofanned()) {
|
||||
if (handler_table()->length() > 0) {
|
||||
os << "Handler Table (size = " << handler_table()->Size() << ")\n";
|
||||
for (int i = 0; i < handler_table()->length(); i += 2) {
|
||||
int pc_offset = Smi::cast(handler_table()->get(i))->value();
|
||||
int handler = Smi::cast(handler_table()->get(i + 1))->value();
|
||||
os << static_cast<const void*>(instruction_start() + pc_offset) << " "
|
||||
<< std::setw(4) << pc_offset << " " << std::setw(4) << handler << "\n";
|
||||
if (kind() == FUNCTION) {
|
||||
HandlerTable::cast(handler_table())->HandlerTableRangePrint(os);
|
||||
} else if (kind() == OPTIMIZED_FUNCTION) {
|
||||
HandlerTable::cast(handler_table())->HandlerTableReturnPrint(os);
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
|
@ -938,6 +938,7 @@ template <class C> inline bool Is(Object* obj);
|
||||
V(DeoptimizationInputData) \
|
||||
V(DeoptimizationOutputData) \
|
||||
V(DependentCode) \
|
||||
V(HandlerTable) \
|
||||
V(FixedArray) \
|
||||
V(FixedDoubleArray) \
|
||||
V(WeakFixedArray) \
|
||||
@ -5080,6 +5081,71 @@ class DeoptimizationOutputData: public FixedArray {
|
||||
};
|
||||
|
||||
|
||||
// HandlerTable is a fixed array containing entries for exception handlers in
|
||||
// the code object it is associated with. The tables comes in two flavors:
|
||||
// 1) Based on ranges: Used for unoptimized code. Contains one entry per
|
||||
// exception handler and a range representing the try-block covered by that
|
||||
// handler. Layout looks as follows:
|
||||
// [ range-start , range-end , handler-offset , stack-depth ]
|
||||
// 2) Based on return addresses: Used for turbofanned code. Contains one entry
|
||||
// per call-site that could throw an exception. Layout looks as follows:
|
||||
// [ return-address-offset , handler-offset ]
|
||||
class HandlerTable : public FixedArray {
|
||||
public:
|
||||
// Accessors for handler table based on ranges.
|
||||
void SetRangeStart(int index, int value) {
|
||||
set(index * kRangeEntrySize + kRangeStartIndex, Smi::FromInt(value));
|
||||
}
|
||||
void SetRangeEnd(int index, int value) {
|
||||
set(index * kRangeEntrySize + kRangeEndIndex, Smi::FromInt(value));
|
||||
}
|
||||
void SetRangeHandler(int index, int value) {
|
||||
set(index * kRangeEntrySize + kRangeHandlerIndex, Smi::FromInt(value));
|
||||
}
|
||||
void SetRangeDepth(int index, int value) {
|
||||
set(index * kRangeEntrySize + kRangeDepthIndex, Smi::FromInt(value));
|
||||
}
|
||||
|
||||
// Accessors for handler table based on return addresses.
|
||||
void SetReturnOffset(int index, int value) {
|
||||
set(index * kReturnEntrySize + kReturnOffsetIndex, Smi::FromInt(value));
|
||||
}
|
||||
void SetReturnHandler(int index, int value) {
|
||||
set(index * kReturnEntrySize + kReturnHandlerIndex, Smi::FromInt(value));
|
||||
}
|
||||
|
||||
// Lookup handler in a table based on ranges.
|
||||
int LookupRange(int pc_offset, int* stack_depth);
|
||||
|
||||
// Lookup handler in a table based on return addresses.
|
||||
int LookupReturn(int pc_offset);
|
||||
|
||||
// Returns the required length of the underlying fixed array.
|
||||
static int LengthForRange(int entries) { return entries * kRangeEntrySize; }
|
||||
static int LengthForReturn(int entries) { return entries * kReturnEntrySize; }
|
||||
|
||||
DECLARE_CAST(HandlerTable)
|
||||
|
||||
#if defined(OBJECT_PRINT) || defined(ENABLE_DISASSEMBLER)
|
||||
void HandlerTableRangePrint(std::ostream& os); // NOLINT
|
||||
void HandlerTableReturnPrint(std::ostream& os); // NOLINT
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Layout description for handler table based on ranges.
|
||||
static const int kRangeStartIndex = 0;
|
||||
static const int kRangeEndIndex = 1;
|
||||
static const int kRangeHandlerIndex = 2;
|
||||
static const int kRangeDepthIndex = 3;
|
||||
static const int kRangeEntrySize = 4;
|
||||
|
||||
// Layout description for handler table based on return addresses.
|
||||
static const int kReturnOffsetIndex = 0;
|
||||
static const int kReturnHandlerIndex = 1;
|
||||
static const int kReturnEntrySize = 2;
|
||||
};
|
||||
|
||||
|
||||
// Forward declaration.
|
||||
class Cell;
|
||||
class PropertyCell;
|
||||
@ -7362,11 +7428,6 @@ class JSGeneratorObject: public JSObject {
|
||||
// [operand_stack]: Saved operand stack.
|
||||
DECL_ACCESSORS(operand_stack, FixedArray)
|
||||
|
||||
// [stack_handler_index]: Index of first stack handler in operand_stack, or -1
|
||||
// if the captured activation had no stack handler.
|
||||
inline int stack_handler_index() const;
|
||||
inline void set_stack_handler_index(int stack_handler_index);
|
||||
|
||||
DECLARE_CAST(JSGeneratorObject)
|
||||
|
||||
// Dispatched behavior.
|
||||
@ -7383,9 +7444,7 @@ class JSGeneratorObject: public JSObject {
|
||||
static const int kReceiverOffset = kContextOffset + kPointerSize;
|
||||
static const int kContinuationOffset = kReceiverOffset + kPointerSize;
|
||||
static const int kOperandStackOffset = kContinuationOffset + kPointerSize;
|
||||
static const int kStackHandlerIndexOffset =
|
||||
kOperandStackOffset + kPointerSize;
|
||||
static const int kSize = kStackHandlerIndexOffset + kPointerSize;
|
||||
static const int kSize = kOperandStackOffset + kPointerSize;
|
||||
|
||||
// Resume mode, for use by runtime functions.
|
||||
enum ResumeMode { NEXT, THROW };
|
||||
|
@ -44,7 +44,7 @@ var lastMicrotaskId = 0;
|
||||
throw MakeTypeError('resolver_not_a_function', [resolver]);
|
||||
var promise = PromiseInit(this);
|
||||
try {
|
||||
%DebugPushPromise(promise);
|
||||
%DebugPushPromise(promise, Promise);
|
||||
resolver(function(x) { PromiseResolve(promise, x) },
|
||||
function(r) { PromiseReject(promise, r) });
|
||||
} catch (e) {
|
||||
@ -110,7 +110,7 @@ var lastMicrotaskId = 0;
|
||||
|
||||
function PromiseHandle(value, handler, deferred) {
|
||||
try {
|
||||
%DebugPushPromise(deferred.promise);
|
||||
%DebugPushPromise(deferred.promise, PromiseHandle);
|
||||
DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler);
|
||||
var result = handler(value);
|
||||
if (result === deferred.promise)
|
||||
|
@ -2784,10 +2784,11 @@ RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) {
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DebugPushPromise) {
|
||||
DCHECK(args.length() == 1);
|
||||
DCHECK(args.length() == 2);
|
||||
HandleScope scope(isolate);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
|
||||
isolate->PushPromise(promise);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 1);
|
||||
isolate->PushPromise(promise, function);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,6 @@ RUNTIME_FUNCTION(Runtime_CreateJSGeneratorObject) {
|
||||
generator->set_receiver(frame->receiver());
|
||||
generator->set_continuation(0);
|
||||
generator->set_operand_stack(isolate->heap()->empty_fixed_array());
|
||||
generator->set_stack_handler_index(-1);
|
||||
|
||||
return *generator;
|
||||
}
|
||||
@ -39,7 +38,7 @@ RUNTIME_FUNCTION(Runtime_CreateJSGeneratorObject) {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SuspendJSGeneratorObject) {
|
||||
HandleScope handle_scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
DCHECK(args.length() == 1 || args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator_object, 0);
|
||||
|
||||
JavaScriptFrameIterator stack_iterator(isolate);
|
||||
@ -52,28 +51,34 @@ RUNTIME_FUNCTION(Runtime_SuspendJSGeneratorObject) {
|
||||
DCHECK_LT(0, generator_object->continuation());
|
||||
|
||||
// We expect there to be at least two values on the operand stack: the return
|
||||
// value of the yield expression, and the argument to this runtime call.
|
||||
// value of the yield expression, and the arguments to this runtime call.
|
||||
// Neither of those should be saved.
|
||||
int operands_count = frame->ComputeOperandsCount();
|
||||
DCHECK_GE(operands_count, 2);
|
||||
operands_count -= 2;
|
||||
DCHECK_GE(operands_count, 1 + args.length());
|
||||
operands_count -= 1 + args.length();
|
||||
|
||||
// Second argument indicates that we need to patch the handler table because
|
||||
// a delegating yield introduced a try-catch statement at expression level,
|
||||
// hence the operand count was off when we statically computed it.
|
||||
// TODO(mstarzinger): This special case disappears with do-expressions.
|
||||
if (args.length() == 2) {
|
||||
CONVERT_SMI_ARG_CHECKED(handler_index, 1);
|
||||
Handle<Code> code(frame->unchecked_code());
|
||||
Handle<HandlerTable> table(HandlerTable::cast(code->handler_table()));
|
||||
int handler_depth = operands_count - TryBlockConstant::kElementCount;
|
||||
table->SetRangeDepth(handler_index, handler_depth);
|
||||
}
|
||||
|
||||
if (operands_count == 0) {
|
||||
// Although it's semantically harmless to call this function with an
|
||||
// operands_count of zero, it is also unnecessary.
|
||||
DCHECK_EQ(generator_object->operand_stack(),
|
||||
isolate->heap()->empty_fixed_array());
|
||||
DCHECK_EQ(generator_object->stack_handler_index(), -1);
|
||||
// If there are no operands on the stack, there shouldn't be a handler
|
||||
// active either.
|
||||
DCHECK(!frame->HasHandler());
|
||||
} else {
|
||||
int stack_handler_index = -1;
|
||||
Handle<FixedArray> operand_stack =
|
||||
isolate->factory()->NewFixedArray(operands_count);
|
||||
frame->SaveOperandStack(*operand_stack, &stack_handler_index);
|
||||
frame->SaveOperandStack(*operand_stack);
|
||||
generator_object->set_operand_stack(*operand_stack);
|
||||
generator_object->set_stack_handler_index(stack_handler_index);
|
||||
}
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
@ -115,10 +120,8 @@ RUNTIME_FUNCTION(Runtime_ResumeJSGeneratorObject) {
|
||||
FixedArray* operand_stack = generator_object->operand_stack();
|
||||
int operands_count = operand_stack->length();
|
||||
if (operands_count != 0) {
|
||||
frame->RestoreOperandStack(operand_stack,
|
||||
generator_object->stack_handler_index());
|
||||
frame->RestoreOperandStack(operand_stack);
|
||||
generator_object->set_operand_stack(isolate->heap()->empty_fixed_array());
|
||||
generator_object->set_stack_handler_index(-1);
|
||||
}
|
||||
|
||||
JSGeneratorObject::ResumeMode resume_mode =
|
||||
|
@ -75,7 +75,7 @@ namespace internal {
|
||||
F(SetInlineBuiltinFlag, 1, 1) \
|
||||
F(StoreArrayLiteralElement, 5, 1) \
|
||||
F(DebugPrepareStepInIfStepping, 1, 1) \
|
||||
F(DebugPushPromise, 1, 1) \
|
||||
F(DebugPushPromise, 2, 1) \
|
||||
F(DebugPopPromise, 0, 1) \
|
||||
F(DebugPromiseEvent, 1, 1) \
|
||||
F(DebugAsyncTaskEvent, 1, 1) \
|
||||
@ -457,7 +457,7 @@ namespace internal {
|
||||
\
|
||||
/* Harmony generators */ \
|
||||
F(CreateJSGeneratorObject, 0, 1) \
|
||||
F(SuspendJSGeneratorObject, 1, 1) \
|
||||
F(SuspendJSGeneratorObject, -1, 1) \
|
||||
F(ResumeJSGeneratorObject, 3, 1) \
|
||||
F(GeneratorClose, 1, 1) \
|
||||
\
|
||||
|
@ -2472,9 +2472,8 @@ void CEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ movp(rsp, masm->ExternalOperand(pending_handler_sp_address));
|
||||
__ movp(rbp, masm->ExternalOperand(pending_handler_fp_address));
|
||||
|
||||
// If the handler is a JS frame, restore the context to the frame.
|
||||
// (kind == ENTRY) == (rbp == 0) == (rsi == 0), so we could test either
|
||||
// rbp or rsi.
|
||||
// If the handler is a JS frame, restore the context to the frame. Note that
|
||||
// the context will be set to (rsi == 0) for non-JS frames.
|
||||
Label skip;
|
||||
__ testp(rsi, rsi);
|
||||
__ j(zero, &skip, Label::kNear);
|
||||
@ -2574,10 +2573,9 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ LoadRoot(rax, Heap::kExceptionRootIndex);
|
||||
__ jmp(&exit);
|
||||
|
||||
// Invoke: Link this frame into the handler chain. There's only one
|
||||
// handler block in this code object, so its index is 0.
|
||||
// Invoke: Link this frame into the handler chain.
|
||||
__ bind(&invoke);
|
||||
__ PushTryHandler(StackHandler::JS_ENTRY, 0);
|
||||
__ PushStackHandler();
|
||||
|
||||
// Clear any pending exceptions.
|
||||
__ LoadRoot(rax, Heap::kTheHoleValueRootIndex);
|
||||
@ -2603,7 +2601,7 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ call(kScratchRegister);
|
||||
|
||||
// Unlink this frame from the handler chain.
|
||||
__ PopTryHandler();
|
||||
__ PopStackHandler();
|
||||
|
||||
__ bind(&exit);
|
||||
// Check if the current stack frame is marked as the outermost JS frame.
|
||||
|
@ -95,7 +95,8 @@ class JumpPatchSite BASE_EMBEDDED {
|
||||
void FullCodeGenerator::Generate() {
|
||||
CompilationInfo* info = info_;
|
||||
handler_table_ =
|
||||
isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
|
||||
Handle<HandlerTable>::cast(isolate()->factory()->NewFixedArray(
|
||||
HandlerTable::LengthForRange(function()->handler_count()), TENURED));
|
||||
|
||||
profiling_counter_ = isolate()->factory()->NewCell(
|
||||
Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate()));
|
||||
@ -2155,7 +2156,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
|
||||
// catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; }
|
||||
__ bind(&l_catch);
|
||||
handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
|
||||
__ LoadRoot(load_name, Heap::kthrow_stringRootIndex); // "throw"
|
||||
__ Push(load_name);
|
||||
__ Push(Operand(rsp, 2 * kPointerSize)); // iter
|
||||
@ -2167,16 +2167,17 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
// re-boxing.
|
||||
__ bind(&l_try);
|
||||
__ Pop(rax); // result
|
||||
__ PushTryHandler(StackHandler::CATCH, expr->index());
|
||||
const int handler_size = StackHandlerConstants::kSize;
|
||||
EnterTryBlock(expr->index(), &l_catch);
|
||||
const int try_block_size = TryCatch::kElementCount * kPointerSize;
|
||||
__ Push(rax); // result
|
||||
__ jmp(&l_suspend);
|
||||
__ bind(&l_continuation);
|
||||
__ jmp(&l_resume);
|
||||
__ bind(&l_suspend);
|
||||
const int generator_object_depth = kPointerSize + handler_size;
|
||||
const int generator_object_depth = kPointerSize + try_block_size;
|
||||
__ movp(rax, Operand(rsp, generator_object_depth));
|
||||
__ Push(rax); // g
|
||||
__ Push(Smi::FromInt(expr->index())); // handler-index
|
||||
DCHECK(l_continuation.pos() > 0 && Smi::IsValid(l_continuation.pos()));
|
||||
__ Move(FieldOperand(rax, JSGeneratorObject::kContinuationOffset),
|
||||
Smi::FromInt(l_continuation.pos()));
|
||||
@ -2184,13 +2185,13 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
__ movp(rcx, rsi);
|
||||
__ RecordWriteField(rax, JSGeneratorObject::kContextOffset, rcx, rdx,
|
||||
kDontSaveFPRegs);
|
||||
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
|
||||
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 2);
|
||||
__ movp(context_register(),
|
||||
Operand(rbp, StandardFrameConstants::kContextOffset));
|
||||
__ Pop(rax); // result
|
||||
EmitReturnSequence();
|
||||
__ bind(&l_resume); // received in rax
|
||||
__ PopTryHandler();
|
||||
ExitTryBlock(expr->index());
|
||||
|
||||
// receiver = iter; f = 'next'; arg = received;
|
||||
__ bind(&l_next);
|
||||
@ -5302,34 +5303,6 @@ void FullCodeGenerator::ExitFinallyBlock() {
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
#define __ ACCESS_MASM(masm())
|
||||
|
||||
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
|
||||
int* stack_depth,
|
||||
int* context_length) {
|
||||
// The macros used here must preserve the result register.
|
||||
|
||||
// Because the handler block contains the context of the finally
|
||||
// code, we can restore it directly from there for the finally code
|
||||
// rather than iteratively unwinding contexts via their previous
|
||||
// links.
|
||||
__ Drop(*stack_depth); // Down to the handler block.
|
||||
if (*context_length > 0) {
|
||||
// Restore the context to its dedicated register and the stack.
|
||||
__ movp(rsi, Operand(rsp, StackHandlerConstants::kContextOffset));
|
||||
__ movp(Operand(rbp, StandardFrameConstants::kContextOffset), rsi);
|
||||
}
|
||||
__ PopTryHandler();
|
||||
__ call(finally_entry_);
|
||||
|
||||
*stack_depth = 0;
|
||||
*context_length = 0;
|
||||
return previous_;
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
|
@ -3001,34 +3001,21 @@ Operand MacroAssembler::SafepointRegisterSlot(Register reg) {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
|
||||
int handler_index) {
|
||||
void MacroAssembler::PushStackHandler() {
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize);
|
||||
|
||||
// We will build up the handler from the bottom by pushing on the stack.
|
||||
// First push the context.
|
||||
if (kind == StackHandler::JS_ENTRY) {
|
||||
Push(Smi::FromInt(0)); // No context.
|
||||
} else {
|
||||
Push(rsi);
|
||||
}
|
||||
|
||||
// Push the index.
|
||||
Push(Immediate(handler_index));
|
||||
|
||||
// Link the current handler as the next handler.
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
Push(ExternalOperand(handler_address));
|
||||
|
||||
// Set this new handler as the current one.
|
||||
movp(ExternalOperand(handler_address), rsp);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PopTryHandler() {
|
||||
void MacroAssembler::PopStackHandler() {
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
Pop(ExternalOperand(handler_address));
|
||||
|
@ -1109,11 +1109,11 @@ class MacroAssembler: public Assembler {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exception handling
|
||||
|
||||
// Push a new try handler and link it into try handler chain.
|
||||
void PushTryHandler(StackHandler::Kind kind, int handler_index);
|
||||
// Push a new stack handler and link it into stack handler chain.
|
||||
void PushStackHandler();
|
||||
|
||||
// Unlink the stack handler on top of the stack from the try handler chain.
|
||||
void PopTryHandler();
|
||||
// Unlink the stack handler on top of the stack from the stack handler chain.
|
||||
void PopStackHandler();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inline caching support
|
||||
|
@ -232,9 +232,7 @@ TEST(DeoptTry) {
|
||||
"})";
|
||||
FunctionTester T(src);
|
||||
|
||||
#if 0 // TODO(mstarzinger): Enable once we can.
|
||||
T.CheckCall(T.Val(2), T.Val(1));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ Debug.setBreakOnException();
|
||||
|
||||
try {
|
||||
try {
|
||||
%DebugPushPromise(new Promise(function() {}));
|
||||
%DebugPushPromise(new Promise(function() {}), function() {});
|
||||
} catch (e) {
|
||||
}
|
||||
throw new Error();
|
||||
|
@ -56,10 +56,10 @@
|
||||
['arch == arm64 and simulator_run == True', {
|
||||
'dfg-int-overflow-in-loop': [SKIP],
|
||||
}], # 'arch == arm64 and simulator_run == True'
|
||||
['dcheck_always_on == True and arch == arm64', {
|
||||
# Doesn't work with gcc 4.6 on arm64 for some reason.
|
||||
['dcheck_always_on == True and (arch == arm or arch == arm64)', {
|
||||
# Doesn't work with gcc 4.6 on arm or arm64 for some reason.
|
||||
'reentrant-caching': [SKIP],
|
||||
}], # 'dcheck_always_on == True and arch == arm64'
|
||||
}], # 'dcheck_always_on == True and (arch == arm or arch == arm64)'
|
||||
|
||||
|
||||
##############################################################################
|
||||
|
Loading…
Reference in New Issue
Block a user