// Copyright 2009 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" #include "codegen-inl.h" #include "compiler.h" #include "full-codegen.h" #include "stub-cache.h" #include "debug.h" #include "liveedit.h" namespace v8 { namespace internal { #define BAILOUT(reason) \ do { \ if (FLAG_trace_bailout) { \ PrintF("%s\n", reason); \ } \ has_supported_syntax_ = false; \ return; \ } while (false) #define CHECK_BAILOUT \ do { \ if (!has_supported_syntax_) return; \ } while (false) void FullCodeGenSyntaxChecker::Check(FunctionLiteral* fun) { Scope* scope = fun->scope(); VisitDeclarations(scope->declarations()); CHECK_BAILOUT; VisitStatements(fun->body()); } void FullCodeGenSyntaxChecker::VisitDeclarations( ZoneList<Declaration*>* decls) { for (int i = 0; i < decls->length(); i++) { Visit(decls->at(i)); CHECK_BAILOUT; } } void FullCodeGenSyntaxChecker::VisitStatements(ZoneList<Statement*>* stmts) { for (int i = 0, len = stmts->length(); i < len; i++) { Visit(stmts->at(i)); CHECK_BAILOUT; } } void FullCodeGenSyntaxChecker::VisitDeclaration(Declaration* decl) { Property* prop = decl->proxy()->AsProperty(); if (prop != NULL) { Visit(prop->obj()); Visit(prop->key()); } if (decl->fun() != NULL) { Visit(decl->fun()); } } void FullCodeGenSyntaxChecker::VisitBlock(Block* stmt) { VisitStatements(stmt->statements()); } void FullCodeGenSyntaxChecker::VisitExpressionStatement( ExpressionStatement* stmt) { Visit(stmt->expression()); } void FullCodeGenSyntaxChecker::VisitEmptyStatement(EmptyStatement* stmt) { // Supported. } void FullCodeGenSyntaxChecker::VisitIfStatement(IfStatement* stmt) { Visit(stmt->condition()); CHECK_BAILOUT; Visit(stmt->then_statement()); CHECK_BAILOUT; Visit(stmt->else_statement()); } void FullCodeGenSyntaxChecker::VisitContinueStatement(ContinueStatement* stmt) { // Supported. } void FullCodeGenSyntaxChecker::VisitBreakStatement(BreakStatement* stmt) { // Supported. } void FullCodeGenSyntaxChecker::VisitReturnStatement(ReturnStatement* stmt) { Visit(stmt->expression()); } void FullCodeGenSyntaxChecker::VisitWithEnterStatement( WithEnterStatement* stmt) { Visit(stmt->expression()); } void FullCodeGenSyntaxChecker::VisitWithExitStatement(WithExitStatement* stmt) { // Supported. } void FullCodeGenSyntaxChecker::VisitSwitchStatement(SwitchStatement* stmt) { BAILOUT("SwitchStatement"); } void FullCodeGenSyntaxChecker::VisitDoWhileStatement(DoWhileStatement* stmt) { Visit(stmt->cond()); CHECK_BAILOUT; Visit(stmt->body()); } void FullCodeGenSyntaxChecker::VisitWhileStatement(WhileStatement* stmt) { Visit(stmt->cond()); CHECK_BAILOUT; Visit(stmt->body()); } void FullCodeGenSyntaxChecker::VisitForStatement(ForStatement* stmt) { if (!FLAG_always_full_compiler) BAILOUT("ForStatement"); if (stmt->init() != NULL) { Visit(stmt->init()); CHECK_BAILOUT; } if (stmt->cond() != NULL) { Visit(stmt->cond()); CHECK_BAILOUT; } Visit(stmt->body()); if (stmt->next() != NULL) { CHECK_BAILOUT; Visit(stmt->next()); } } void FullCodeGenSyntaxChecker::VisitForInStatement(ForInStatement* stmt) { BAILOUT("ForInStatement"); } void FullCodeGenSyntaxChecker::VisitTryCatchStatement(TryCatchStatement* stmt) { Visit(stmt->try_block()); CHECK_BAILOUT; Visit(stmt->catch_block()); } void FullCodeGenSyntaxChecker::VisitTryFinallyStatement( TryFinallyStatement* stmt) { Visit(stmt->try_block()); CHECK_BAILOUT; Visit(stmt->finally_block()); } void FullCodeGenSyntaxChecker::VisitDebuggerStatement( DebuggerStatement* stmt) { // Supported. } void FullCodeGenSyntaxChecker::VisitFunctionLiteral(FunctionLiteral* expr) { // Supported. } void FullCodeGenSyntaxChecker::VisitFunctionBoilerplateLiteral( FunctionBoilerplateLiteral* expr) { BAILOUT("FunctionBoilerplateLiteral"); } void FullCodeGenSyntaxChecker::VisitConditional(Conditional* expr) { Visit(expr->condition()); CHECK_BAILOUT; Visit(expr->then_expression()); CHECK_BAILOUT; Visit(expr->else_expression()); } void FullCodeGenSyntaxChecker::VisitSlot(Slot* expr) { UNREACHABLE(); } void FullCodeGenSyntaxChecker::VisitVariableProxy(VariableProxy* expr) { // Supported. } void FullCodeGenSyntaxChecker::VisitLiteral(Literal* expr) { // Supported. } void FullCodeGenSyntaxChecker::VisitRegExpLiteral(RegExpLiteral* expr) { // Supported. } void FullCodeGenSyntaxChecker::VisitObjectLiteral(ObjectLiteral* expr) { ZoneList<ObjectLiteral::Property*>* properties = expr->properties(); for (int i = 0, len = properties->length(); i < len; i++) { ObjectLiteral::Property* property = properties->at(i); if (property->IsCompileTimeValue()) continue; Visit(property->key()); CHECK_BAILOUT; Visit(property->value()); CHECK_BAILOUT; } } void FullCodeGenSyntaxChecker::VisitArrayLiteral(ArrayLiteral* expr) { ZoneList<Expression*>* subexprs = expr->values(); for (int i = 0, len = subexprs->length(); i < len; i++) { Expression* subexpr = subexprs->at(i); if (subexpr->AsLiteral() != NULL) continue; if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; Visit(subexpr); CHECK_BAILOUT; } } void FullCodeGenSyntaxChecker::VisitCatchExtensionObject( CatchExtensionObject* expr) { Visit(expr->key()); CHECK_BAILOUT; Visit(expr->value()); } void FullCodeGenSyntaxChecker::VisitAssignment(Assignment* expr) { Token::Value op = expr->op(); if (op == Token::INIT_CONST) BAILOUT("initialize constant"); Variable* var = expr->target()->AsVariableProxy()->AsVariable(); Property* prop = expr->target()->AsProperty(); ASSERT(var == NULL || prop == NULL); if (var != NULL) { if (var->mode() == Variable::CONST) BAILOUT("Assignment to const"); // All other variables are supported. } else if (prop != NULL) { Visit(prop->obj()); CHECK_BAILOUT; Visit(prop->key()); CHECK_BAILOUT; } else { // This is a throw reference error. BAILOUT("non-variable/non-property assignment"); } Visit(expr->value()); } void FullCodeGenSyntaxChecker::VisitThrow(Throw* expr) { Visit(expr->exception()); } void FullCodeGenSyntaxChecker::VisitProperty(Property* expr) { Visit(expr->obj()); CHECK_BAILOUT; Visit(expr->key()); } void FullCodeGenSyntaxChecker::VisitCall(Call* expr) { Expression* fun = expr->expression(); ZoneList<Expression*>* args = expr->arguments(); Variable* var = fun->AsVariableProxy()->AsVariable(); // Check for supported calls if (var != NULL && var->is_possibly_eval()) { BAILOUT("call to the identifier 'eval'"); } else if (var != NULL && !var->is_this() && var->is_global()) { // Calls to global variables are supported. } else if (var != NULL && var->slot() != NULL && var->slot()->type() == Slot::LOOKUP) { BAILOUT("call to a lookup slot"); } else if (fun->AsProperty() != NULL) { Property* prop = fun->AsProperty(); Visit(prop->obj()); CHECK_BAILOUT; Visit(prop->key()); CHECK_BAILOUT; } else { // Otherwise the call is supported if the function expression is. Visit(fun); } // Check all arguments to the call. for (int i = 0; i < args->length(); i++) { Visit(args->at(i)); CHECK_BAILOUT; } } void FullCodeGenSyntaxChecker::VisitCallNew(CallNew* expr) { Visit(expr->expression()); CHECK_BAILOUT; ZoneList<Expression*>* args = expr->arguments(); // Check all arguments to the call for (int i = 0; i < args->length(); i++) { Visit(args->at(i)); CHECK_BAILOUT; } } void FullCodeGenSyntaxChecker::VisitCallRuntime(CallRuntime* expr) { // Check for inline runtime call if (expr->name()->Get(0) == '_' && CodeGenerator::FindInlineRuntimeLUT(expr->name()) != NULL) { BAILOUT("inlined runtime call"); } // Check all arguments to the call. (Relies on TEMP meaning STACK.) for (int i = 0; i < expr->arguments()->length(); i++) { Visit(expr->arguments()->at(i)); CHECK_BAILOUT; } } void FullCodeGenSyntaxChecker::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::ADD: case Token::BIT_NOT: case Token::NOT: case Token::SUB: case Token::TYPEOF: case Token::VOID: Visit(expr->expression()); break; case Token::DELETE: BAILOUT("UnaryOperation: DELETE"); default: UNREACHABLE(); } } void FullCodeGenSyntaxChecker::VisitCountOperation(CountOperation* expr) { Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); Property* prop = expr->expression()->AsProperty(); ASSERT(var == NULL || prop == NULL); if (var != NULL) { // All global variables are supported. if (!var->is_global()) { ASSERT(var->slot() != NULL); Slot::Type type = var->slot()->type(); if (type == Slot::LOOKUP) { BAILOUT("CountOperation with lookup slot"); } } } else if (prop != NULL) { Visit(prop->obj()); CHECK_BAILOUT; Visit(prop->key()); CHECK_BAILOUT; } else { // This is a throw reference error. BAILOUT("CountOperation non-variable/non-property expression"); } } void FullCodeGenSyntaxChecker::VisitBinaryOperation(BinaryOperation* expr) { Visit(expr->left()); CHECK_BAILOUT; Visit(expr->right()); } void FullCodeGenSyntaxChecker::VisitCompareOperation(CompareOperation* expr) { Visit(expr->left()); CHECK_BAILOUT; Visit(expr->right()); } void FullCodeGenSyntaxChecker::VisitThisFunction(ThisFunction* expr) { // Supported. } #undef BAILOUT #undef CHECK_BAILOUT #define __ ACCESS_MASM(masm()) Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) { Handle<Script> script = info->script(); if (!script->IsUndefined() && !script->source()->IsUndefined()) { int len = String::cast(script->source())->length(); Counters::total_full_codegen_source_size.Increment(len); } CodeGenerator::MakeCodePrologue(info); const int kInitialBufferSize = 4 * KB; MacroAssembler masm(NULL, kInitialBufferSize); LiveEditFunctionTracker live_edit_tracker(info->function()); FullCodeGenerator cgen(&masm); cgen.Generate(info, PRIMARY); if (cgen.HasStackOverflow()) { ASSERT(!Top::has_pending_exception()); return Handle<Code>::null(); } Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP); Handle<Code> result = CodeGenerator::MakeCodeEpilogue(&masm, flags, info); live_edit_tracker.RecordFunctionCode(result); return result; } int FullCodeGenerator::SlotOffset(Slot* slot) { ASSERT(slot != NULL); // Offset is negative because higher indexes are at lower addresses. int offset = -slot->index() * kPointerSize; // Adjust by a (parameter or local) base offset. switch (slot->type()) { case Slot::PARAMETER: offset += (scope()->num_parameters() + 1) * kPointerSize; break; case Slot::LOCAL: offset += JavaScriptFrameConstants::kLocal0Offset; break; case Slot::CONTEXT: case Slot::LOOKUP: UNREACHABLE(); } return offset; } void FullCodeGenerator::VisitDeclarations( ZoneList<Declaration*>* declarations) { int length = declarations->length(); int globals = 0; for (int i = 0; i < length; i++) { Declaration* decl = declarations->at(i); Variable* var = decl->proxy()->var(); Slot* slot = var->slot(); // If it was not possible to allocate the variable at compile // time, we need to "declare" it at runtime to make sure it // actually exists in the local context. if ((slot != NULL && slot->type() == Slot::LOOKUP) || !var->is_global()) { VisitDeclaration(decl); } else { // Count global variables and functions for later processing globals++; } } // Compute array of global variable and function declarations. // Do nothing in case of no declared global functions or variables. if (globals > 0) { Handle<FixedArray> array = Factory::NewFixedArray(2 * globals, TENURED); for (int j = 0, i = 0; i < length; i++) { Declaration* decl = declarations->at(i); Variable* var = decl->proxy()->var(); Slot* slot = var->slot(); if ((slot == NULL || slot->type() != Slot::LOOKUP) && var->is_global()) { array->set(j++, *(var->name())); if (decl->fun() == NULL) { if (var->mode() == Variable::CONST) { // In case this is const property use the hole. array->set_the_hole(j++); } else { array->set_undefined(j++); } } else { Handle<JSFunction> function = Compiler::BuildBoilerplate(decl->fun(), script(), this); // Check for stack-overflow exception. if (HasStackOverflow()) return; array->set(j++, *function); } } } // Invoke the platform-dependent code generator to do the actual // declaration the global variables and functions. DeclareGlobals(array); } } void FullCodeGenerator::SetFunctionPosition(FunctionLiteral* fun) { if (FLAG_debug_info) { CodeGenerator::RecordPositions(masm_, fun->start_position()); } } void FullCodeGenerator::SetReturnPosition(FunctionLiteral* fun) { if (FLAG_debug_info) { CodeGenerator::RecordPositions(masm_, fun->end_position()); } } void FullCodeGenerator::SetStatementPosition(Statement* stmt) { if (FLAG_debug_info) { CodeGenerator::RecordPositions(masm_, stmt->statement_pos()); } } void FullCodeGenerator::SetStatementPosition(int pos) { if (FLAG_debug_info) { CodeGenerator::RecordPositions(masm_, pos); } } void FullCodeGenerator::SetSourcePosition(int pos) { if (FLAG_debug_info && pos != RelocInfo::kNoPosition) { masm_->RecordPosition(pos); } } void FullCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) { Label eval_right, done; // Set up the appropriate context for the left subexpression based // on the operation and our own context. Initially assume we can // inherit both true and false labels from our context. if (expr->op() == Token::OR) { switch (context_) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: VisitForControl(expr->left(), &done, &eval_right); break; case Expression::kValue: VisitForValueControl(expr->left(), location_, &done, &eval_right); break; case Expression::kTest: VisitForControl(expr->left(), true_label_, &eval_right); break; case Expression::kValueTest: VisitForValueControl(expr->left(), location_, true_label_, &eval_right); break; case Expression::kTestValue: VisitForControl(expr->left(), true_label_, &eval_right); break; } } else { ASSERT_EQ(Token::AND, expr->op()); switch (context_) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: VisitForControl(expr->left(), &eval_right, &done); break; case Expression::kValue: VisitForControlValue(expr->left(), location_, &eval_right, &done); break; case Expression::kTest: VisitForControl(expr->left(), &eval_right, false_label_); break; case Expression::kValueTest: VisitForControl(expr->left(), &eval_right, false_label_); break; case Expression::kTestValue: VisitForControlValue(expr->left(), location_, &eval_right, false_label_); break; } } __ bind(&eval_right); Visit(expr->right()); __ bind(&done); } void FullCodeGenerator::VisitBlock(Block* stmt) { Comment cmnt(masm_, "[ Block"); Breakable nested_statement(this, stmt); SetStatementPosition(stmt); VisitStatements(stmt->statements()); __ bind(nested_statement.break_target()); } void FullCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) { Comment cmnt(masm_, "[ ExpressionStatement"); SetStatementPosition(stmt); VisitForEffect(stmt->expression()); } void FullCodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) { Comment cmnt(masm_, "[ EmptyStatement"); SetStatementPosition(stmt); } void FullCodeGenerator::VisitIfStatement(IfStatement* stmt) { Comment cmnt(masm_, "[ IfStatement"); SetStatementPosition(stmt); Label then_part, else_part, done; // Do not worry about optimizing for empty then or else bodies. VisitForControl(stmt->condition(), &then_part, &else_part); __ bind(&then_part); Visit(stmt->then_statement()); __ jmp(&done); __ bind(&else_part); Visit(stmt->else_statement()); __ bind(&done); } void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { Comment cmnt(masm_, "[ ContinueStatement"); SetStatementPosition(stmt); NestedStatement* current = nesting_stack_; int stack_depth = 0; while (!current->IsContinueTarget(stmt->target())) { stack_depth = current->Exit(stack_depth); current = current->outer(); } __ Drop(stack_depth); Iteration* loop = current->AsIteration(); __ jmp(loop->continue_target()); } void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) { Comment cmnt(masm_, "[ BreakStatement"); SetStatementPosition(stmt); NestedStatement* current = nesting_stack_; int stack_depth = 0; while (!current->IsBreakTarget(stmt->target())) { stack_depth = current->Exit(stack_depth); current = current->outer(); } __ Drop(stack_depth); Breakable* target = current->AsBreakable(); __ jmp(target->break_target()); } void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { Comment cmnt(masm_, "[ ReturnStatement"); SetStatementPosition(stmt); Expression* expr = stmt->expression(); VisitForValue(expr, kAccumulator); // Exit all nested statements. NestedStatement* current = nesting_stack_; int stack_depth = 0; while (current != NULL) { stack_depth = current->Exit(stack_depth); current = current->outer(); } __ Drop(stack_depth); EmitReturnSequence(stmt->statement_pos()); } void FullCodeGenerator::VisitWithEnterStatement(WithEnterStatement* stmt) { Comment cmnt(masm_, "[ WithEnterStatement"); SetStatementPosition(stmt); VisitForValue(stmt->expression(), kStack); if (stmt->is_catch_block()) { __ CallRuntime(Runtime::kPushCatchContext, 1); } else { __ CallRuntime(Runtime::kPushContext, 1); } // Both runtime calls return the new context in both the context and the // result registers. // Update local stack frame context field. StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); } void FullCodeGenerator::VisitWithExitStatement(WithExitStatement* stmt) { Comment cmnt(masm_, "[ WithExitStatement"); SetStatementPosition(stmt); // Pop context. LoadContextField(context_register(), Context::PREVIOUS_INDEX); // Update local stack frame context field. StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); } void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { UNREACHABLE(); } void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { Comment cmnt(masm_, "[ DoWhileStatement"); SetStatementPosition(stmt); Label body, stack_limit_hit, stack_check_success; Iteration loop_statement(this, stmt); increment_loop_depth(); __ bind(&body); Visit(stmt->body()); // Check stack before looping. __ StackLimitCheck(&stack_limit_hit); __ bind(&stack_check_success); __ bind(loop_statement.continue_target()); SetStatementPosition(stmt->condition_position()); VisitForControl(stmt->cond(), &body, loop_statement.break_target()); __ bind(&stack_limit_hit); StackCheckStub stack_stub; __ CallStub(&stack_stub); __ jmp(&stack_check_success); __ bind(loop_statement.break_target()); decrement_loop_depth(); } void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { Comment cmnt(masm_, "[ WhileStatement"); SetStatementPosition(stmt); Label body, stack_limit_hit, stack_check_success; Iteration loop_statement(this, stmt); increment_loop_depth(); // Emit the test at the bottom of the loop. __ jmp(loop_statement.continue_target()); __ bind(&body); Visit(stmt->body()); __ bind(loop_statement.continue_target()); // Check stack before looping. __ StackLimitCheck(&stack_limit_hit); __ bind(&stack_check_success); VisitForControl(stmt->cond(), &body, loop_statement.break_target()); __ bind(&stack_limit_hit); StackCheckStub stack_stub; __ CallStub(&stack_stub); __ jmp(&stack_check_success); __ bind(loop_statement.break_target()); decrement_loop_depth(); } void FullCodeGenerator::VisitForStatement(ForStatement* stmt) { Comment cmnt(masm_, "[ ForStatement"); SetStatementPosition(stmt); Label test, body, stack_limit_hit, stack_check_success; Iteration loop_statement(this, stmt); if (stmt->init() != NULL) { Visit(stmt->init()); } increment_loop_depth(); // Emit the test at the bottom of the loop (even if empty). __ jmp(&test); __ bind(&body); Visit(stmt->body()); __ bind(loop_statement.continue_target()); SetStatementPosition(stmt); if (stmt->next() != NULL) { Visit(stmt->next()); } __ bind(&test); // Check stack before looping. __ StackLimitCheck(&stack_limit_hit); __ bind(&stack_check_success); if (stmt->cond() != NULL) { VisitForControl(stmt->cond(), &body, loop_statement.break_target()); } else { __ jmp(&body); } __ bind(&stack_limit_hit); StackCheckStub stack_stub; __ CallStub(&stack_stub); __ jmp(&stack_check_success); __ bind(loop_statement.break_target()); decrement_loop_depth(); } void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { UNREACHABLE(); } void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { Comment cmnt(masm_, "[ TryCatchStatement"); SetStatementPosition(stmt); // The try block adds a handler to the exception handler chain // before entering, and removes it again when exiting normally. // If an exception is thrown during execution of the try block, // control is passed to the handler, which also consumes the handler. // At this point, the exception is in a register, and store it in // the temporary local variable (prints as ".catch-var") before // executing the catch block. The catch block has been rewritten // to introduce a new scope to bind the catch variable and to remove // that scope again afterwards. Label try_handler_setup, catch_entry, done; __ Call(&try_handler_setup); // Try handler code, exception in result register. // Store exception in local .catch variable before executing catch block. { // The catch variable is *always* a variable proxy for a local variable. Variable* catch_var = stmt->catch_var()->AsVariableProxy()->AsVariable(); ASSERT_NOT_NULL(catch_var); Slot* variable_slot = catch_var->slot(); ASSERT_NOT_NULL(variable_slot); ASSERT_EQ(Slot::LOCAL, variable_slot->type()); StoreToFrameField(SlotOffset(variable_slot), result_register()); } Visit(stmt->catch_block()); __ jmp(&done); // Try block code. Sets up the exception handler chain. __ bind(&try_handler_setup); { TryCatch try_block(this, &catch_entry); __ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER); Visit(stmt->try_block()); __ PopTryHandler(); } __ bind(&done); } void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { Comment cmnt(masm_, "[ TryFinallyStatement"); SetStatementPosition(stmt); // 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 // chain 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); __ push(result_register()); __ CallRuntime(Runtime::kReThrow, 1); } __ bind(&finally_entry); { // Finally block implementation. Finally finally_block(this); EnterFinallyBlock(); Visit(stmt->finally_block()); ExitFinallyBlock(); // Return to the calling code. } __ bind(&try_handler_setup); { // Setup try handler (stack pointer registers). TryFinally try_block(this, &finally_entry); __ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER); Visit(stmt->try_block()); __ PopTryHandler(); } // Execute the finally block on the way out. __ Call(&finally_entry); } void FullCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { #ifdef ENABLE_DEBUGGER_SUPPORT Comment cmnt(masm_, "[ DebuggerStatement"); SetStatementPosition(stmt); __ DebugBreak(); // Ignore the return value. #endif } void FullCodeGenerator::VisitFunctionBoilerplateLiteral( FunctionBoilerplateLiteral* expr) { UNREACHABLE(); } void FullCodeGenerator::VisitConditional(Conditional* expr) { Comment cmnt(masm_, "[ Conditional"); Label true_case, false_case, done; VisitForControl(expr->condition(), &true_case, &false_case); __ bind(&true_case); Visit(expr->then_expression()); // If control flow falls through Visit, jump to done. if (context_ == Expression::kEffect || context_ == Expression::kValue) { __ jmp(&done); } __ bind(&false_case); Visit(expr->else_expression()); // If control flow falls through Visit, merge it with true case here. if (context_ == Expression::kEffect || context_ == Expression::kValue) { __ bind(&done); } } void FullCodeGenerator::VisitSlot(Slot* expr) { // Slots do not appear directly in the AST. UNREACHABLE(); } void FullCodeGenerator::VisitLiteral(Literal* expr) { Comment cmnt(masm_, "[ Literal"); Apply(context_, expr); } void FullCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { // Call runtime routine to allocate the catch extension object and // assign the exception value to the catch variable. Comment cmnt(masm_, "[ CatchExtensionObject"); VisitForValue(expr->key(), kStack); VisitForValue(expr->value(), kStack); // Create catch extension object. __ CallRuntime(Runtime::kCreateCatchExtensionObject, 2); Apply(context_, result_register()); } void FullCodeGenerator::VisitThrow(Throw* expr) { Comment cmnt(masm_, "[ Throw"); VisitForValue(expr->exception(), kStack); __ CallRuntime(Runtime::kThrow, 1); // Never returns here. } int FullCodeGenerator::TryFinally::Exit(int stack_depth) { // The macros used here must preserve the result register. __ Drop(stack_depth); __ PopTryHandler(); __ Call(finally_entry_); return 0; } int FullCodeGenerator::TryCatch::Exit(int stack_depth) { // The macros used here must preserve the result register. __ Drop(stack_depth); __ PopTryHandler(); return 0; } #undef __ } } // namespace v8::internal