// Convert current context to test context: End post-test code.
}
+
void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
Move(expr->context(), r0);
Register FastCodeGenerator::result_register() { return r0; }
-#undef __
+// ----------------------------------------------------------------------------
+// Non-local control flow support.
+
+void FastCodeGenerator::EnterFinallyBlock() {
+ ASSERT(!result_register().is(r1));
+ // Cook return address in link register to stack (smi encoded Code* delta)
+ __ sub(r1, lr, Operand(masm_->CodeObject()));
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
+ ASSERT_EQ(0, kSmiTag);
+ __ add(r1, r1, Operand(r1)); // Convert to smi.
+ __ push(r1);
+ // Store result register while executing finally block.
+ __ push(result_register());
+}
+
+
+void FastCodeGenerator::ExitFinallyBlock() {
+ ASSERT(!result_register().is(r1));
+ // Restore result register from stack.
+ __ pop(result_register());
+ // Uncook return address and return.
+ __ pop(r1);
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
+ __ mov(r1, Operand(r1, ASR, 1)); // Un-smi-tag value.
+ __ add(pc, r1, Operand(masm_->CodeObject()));
+}
+
+
+void FastCodeGenerator::ThrowException() {
+ __ push(result_register());
+ __ CallRuntime(Runtime::kThrow, 1);
+}
+
+#undef __
} } // namespace v8::internal
void CodeGenSelector::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
- BAILOUT("TryFinallyStatement");
+ Visit(stmt->try_block());
+ CHECK_BAILOUT;
+ Visit(stmt->finally_block());
}
void FastCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
- UNREACHABLE();
+ // Try finally is compiled by setting up a try-handler on the stack while
+ // executing the try body, and removing it again afterwards.
+ //
+ // The try-finally construct can enter the finally block in three ways:
+ // 1. By exiting the try-block normally. This removes the try-handler and
+ // calls the finally block code before continuing.
+ // 2. By exiting the try-block with a function-local control flow transfer
+ // (break/continue/return). The site of the, e.g., break removes the
+ // try handler and calls the finally block code before continuing
+ // its outward control transfer.
+ // 3. by exiting the try-block with a thrown exception.
+ // This can happen in nested function calls. It traverses the try-handler
+ // chaing and consumes the try-handler entry before jumping to the
+ // handler code. The handler code then calls the finally-block before
+ // rethrowing the exception.
+ //
+ // The finally block must assume a return address on top of the stack
+ // (or in the link register on ARM chips) and a value (return value or
+ // exception) in the result register (rax/eax/r0), both of which must
+ // be preserved. The return address isn't GC-safe, so it should be
+ // cooked before GC.
+ Label finally_entry;
+ Label try_handler_setup;
+
+ // Setup the try-handler chain. Use a call to
+ // Jump to try-handler setup and try-block code. Use call to put try-handler
+ // address on stack.
+ __ Call(&try_handler_setup);
+ // Try handler code. Return address of call is pushed on handler stack.
+ {
+ // This code is only executed during stack-handler traversal when an
+ // exception is thrown. The execption is in the result register, which
+ // is retained by the finally block.
+ // Call the finally block and then rethrow the exception.
+ __ Call(&finally_entry);
+ ThrowException();
+ }
+
+ __ bind(&finally_entry);
+ {
+ // Finally block implementation.
+ EnterFinallyBlock();
+ Finally finally_block(this);
+ Visit(stmt->finally_block());
+ ExitFinallyBlock(); // Return to the calling code.
+ }
+
+ __ bind(&try_handler_setup);
+ {
+ // Setup try handler (stack pointer registers).
+ __ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER);
+ TryFinally try_block(this, &finally_entry);
+ VisitStatements(stmt->try_block()->statements());
+ __ PopTryHandler();
+ }
+ // Execute the finally block on the way out.
+ __ Call(&finally_entry);
}
void SetStatementPosition(Statement* stmt);
void SetSourcePosition(int pos);
+ // Non-local control flow support.
+ void EnterFinallyBlock();
+ void ExitFinallyBlock();
+ void ThrowException();
+
+ // Loop nesting counter.
int loop_depth() { return loop_depth_; }
void increment_loop_depth() { loop_depth_++; }
void decrement_loop_depth() {
// Convert current context to test context: End post-test code.
}
-Register FastCodeGenerator::result_register() { return eax; }
void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
Move(expr->context(), eax);
}
-#undef __
+Register FastCodeGenerator::result_register() { return eax; }
+
+// ----------------------------------------------------------------------------
+// Non-local control flow support.
+
+void FastCodeGenerator::EnterFinallyBlock() {
+ // Cook return address on top of stack (smi encoded Code* delta)
+ ASSERT(!result_register().is(edx));
+ __ mov(edx, Operand(esp, 0));
+ __ sub(Operand(edx), Immediate(masm_->CodeObject()));
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
+ ASSERT_EQ(0, kSmiTag);
+ __ add(edx, Operand(edx)); // Convert to smi.
+ __ mov(Operand(esp, 0), edx);
+ // Store result register while executing finally block.
+ __ push(result_register());
+}
+
+
+void FastCodeGenerator::ExitFinallyBlock() {
+ ASSERT(!result_register().is(edx));
+ // Restore result register from stack.
+ __ pop(result_register());
+ // Uncook return address.
+ __ mov(edx, Operand(esp, 0));
+ __ sar(edx, 1); // Convert smi to int.
+ __ add(Operand(edx), Immediate(masm_->CodeObject()));
+ __ mov(Operand(esp, 0), edx);
+ // And return.
+ __ ret(0);
+}
+
+
+void FastCodeGenerator::ThrowException() {
+ __ push(result_register());
+ __ CallRuntime(Runtime::kThrow, 1);
+}
+
+#undef __
} } // namespace v8::internal
// Unlink the stack handler on top of the stack from the try handler chain.
void PopTryHandler();
-
// ---------------------------------------------------------------------------
// Inline caching support
Register FastCodeGenerator::result_register() { return rax; }
+// ----------------------------------------------------------------------------
+// Non-local control flow support.
+
+
+void FastCodeGenerator::EnterFinallyBlock() {
+ ASSERT(!result_register().is(rdx));
+ ASSERT(!result_register().is(rcx));
+ // Cook return address on top of stack (smi encoded Code* delta)
+ __ movq(rdx, Operand(rsp, 0));
+ __ Move(rcx, masm_->CodeObject());
+ __ subq(rdx, rcx);
+ __ Integer32ToSmi(rdx, rdx);
+ __ movq(Operand(rsp, 0), rdx);
+ // Store result register while executing finally block.
+ __ push(result_register());
+}
+
+
+void FastCodeGenerator::ExitFinallyBlock() {
+ ASSERT(!result_register().is(rdx));
+ ASSERT(!result_register().is(rcx));
+ // Restore result register from stack.
+ __ pop(result_register());
+ // Uncook return address.
+ __ movq(rdx, Operand(rsp, 0));
+ __ SmiToInteger32(rdx, rdx);
+ __ Move(rcx, masm_->CodeObject());
+ __ addq(rdx, rcx);
+ __ movq(Operand(rsp, 0), rdx);
+ // And return.
+ __ ret(0);
+}
+
+
+void FastCodeGenerator::ThrowException() {
+ __ push(result_register());
+ __ CallRuntime(Runtime::kThrow, 1);
+}
+
#undef __
void MacroAssembler::PopTryHandler() {
ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
+ // Unlink this handler.
movq(kScratchRegister, ExternalReference(Top::k_handler_address));
pop(Operand(kScratchRegister, 0));
+ // Remove the remaining fields.
addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
}
assertFalse(caught);
assertTrue(finalized);
+function return_from_nested_finally_in_finally() {
+ try {
+ return 1;
+ } finally {
+ try {
+ return 2;
+ } finally {
+ return 42;
+ }
+ }
+}
+
+assertEquals(42, return_from_nested_finally_in_finally());
+
+function break_from_nested_finally_in_finally() {
+ L: try {
+ return 1;
+ } finally {
+ try {
+ return 2;
+ } finally {
+ break L;
+ }
+ }
+ return 42;
+}
+
+assertEquals(42, break_from_nested_finally_in_finally());
+
+function continue_from_nested_finally_in_finally() {
+ do {
+ try {
+ return 1;
+ } finally {
+ try {
+ return 2;
+ } finally {
+ continue;
+ }
+ }
+ } while (false);
+ return 42;
+}
+
+assertEquals(42, continue_from_nested_finally_in_finally());