__ 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);
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())));
__ 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
__ 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.
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()));
// 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
// 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));
__ 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);
#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 __
-
static Address GetInterruptImmediateLoadAddress(Address pc) {
Address load_address = pc - 2 * Assembler::kInstrSize;
}
-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())));
// ---------------------------------------------------------------------------
// 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
__ 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));
__ 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
__ Blr(x12);
// Unlink this frame from the handler chain.
- __ PopTryHandler();
+ __ PopStackHandler();
__ Bind(&exit);
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()));
// 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
// 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);
__ 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));
__ 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);
}
-#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
}
-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()));
// ---------------------------------------------------------------------------
// 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();
// ---------------------------------------------------------------------------
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();
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);
// 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);
}
}
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;
}
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.
}
-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()) {
}
}
-bool StackFrame::HasHandler() const {
- StackHandlerIterator it(this, top_handler());
- return !it.done();
-}
-
-
#ifdef DEBUG
static bool GcSafeCodeContains(HeapObject* object, Address addr);
#endif
}
-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.
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);
}
}
+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) {
}
-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);
}
}
+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());
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));
}
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());
}
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);
}
// -------------------------------------------------------------------------
-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); }
};
+// 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);
};
// 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;
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);
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;
// 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();
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:
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");
// 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);
}
// 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
// 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
}
-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_;
}
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(); }
// 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_;
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.
*stack_depth += kElementCount;
return previous_;
}
+ virtual NestedStatement* AccumulateDepth(int* stack_depth) {
+ *stack_depth += kElementCount;
+ return previous_;
+ }
};
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();
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;
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_;
__ 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);
__ 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()));
__ 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.
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()));
// 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
// 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())));
__ 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);
#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 __
-
static const byte kJnsInstruction = 0x79;
static const byte kJnsOffset = 0x11;
}
-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));
// ---------------------------------------------------------------------------
// 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
// 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());
}
-// 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();
// 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();
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();
+ // For optimized frames we perform a lookup in the handler table.
+ if (frame->is_optimized() && catchable_by_js) {
+ 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;
- // Restore the next handler.
- thread_local_top()->handler_ = handler->next()->address();
+ // 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 -
+ stack_slots * kPointerSize;
- // Gather information from the handler.
+ // Gather information from the frame.
code = frame->LookupCode();
- context = handler->context();
- offset = Smi::cast(code->handler_table()->get(handler->index()))->value();
- handler_sp = handler->address() + StackHandlerConstants::kSize;
+ handler_sp = return_sp;
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;
+ // 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
- // argument slots on the stack are dropped as returning would.
+ // 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 -
- frame_code->stack_slots() * kPointerSize;
+ stack_slots * kPointerSize;
+ STATIC_ASSERT(TryBlockConstant::kElementCount == 1);
+ context = Context::cast(Memory::Object_at(return_sp - kPointerSize));
// Gather information from the frame.
- code = frame_code;
- offset = handler_offset;
+ code = frame->LookupCode();
handler_sp = return_sp;
handler_fp = frame->fp();
break;
}
-// 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;
}
}
-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);
}
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());
}
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;
}
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();
// 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
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_;
};
VerifyObjectField(kReceiverOffset);
VerifyObjectField(kOperandStackOffset);
VerifyObjectField(kContinuationOffset);
- VerifyObjectField(kStackHandlerIndexOffset);
}
}
+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
CAST_ACCESSOR(FixedTypedArrayBase)
CAST_ACCESSOR(Foreign)
CAST_ACCESSOR(GlobalObject)
+CAST_ACCESSOR(HandlerTable)
CAST_ACCESSOR(HeapObject)
CAST_ACCESSOR(JSArray)
CAST_ACCESSOR(JSArrayBuffer)
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);
}
+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();
}
+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";
#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";
}
V(DeoptimizationInputData) \
V(DeoptimizationOutputData) \
V(DependentCode) \
+ V(HandlerTable) \
V(FixedArray) \
V(FixedDoubleArray) \
V(WeakFixedArray) \
};
+// 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;
// [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.
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 };
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) {
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)
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();
}
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;
}
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);
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();
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 =
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) \
\
/* Harmony generators */ \
F(CreateJSGeneratorObject, 0, 1) \
- F(SuspendJSGeneratorObject, 1, 1) \
+ F(SuspendJSGeneratorObject, -1, 1) \
F(ResumeJSGeneratorObject, 3, 1) \
F(GeneratorClose, 1, 1) \
\
__ 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);
__ 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);
__ 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.
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()));
// 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
// 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()));
__ 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);
#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 __
-
static const byte kJnsInstruction = 0x79;
static const byte kNopByteOne = 0x66;
}
-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));
// ---------------------------------------------------------------------------
// 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
"})";
FunctionTester T(src);
-#if 0 // TODO(mstarzinger): Enable once we can.
T.CheckCall(T.Val(2), T.Val(1));
-#endif
}
try {
try {
- %DebugPushPromise(new Promise(function() {}));
+ %DebugPushPromise(new Promise(function() {}), function() {});
} catch (e) {
}
throw new Error();
['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)'
##############################################################################