// 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" 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(); if (scope->num_heap_slots() > 0) { // We support functions with a local context if they do not have // parameters that need to be copied into the context. for (int i = 0, len = scope->num_parameters(); i < len; i++) { Slot* slot = scope->parameter(i)->slot(); if (slot != NULL && slot->type() == Slot::CONTEXT) { BAILOUT("Function has context-allocated parameters."); } } } VisitDeclarations(scope->declarations()); CHECK_BAILOUT; VisitStatements(fun->body()); } void FullCodeGenSyntaxChecker::VisitDeclarations( ZoneList* decls) { for (int i = 0; i < decls->length(); i++) { Visit(decls->at(i)); CHECK_BAILOUT; } } void FullCodeGenSyntaxChecker::VisitStatements(ZoneList* 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) {} 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_fast_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) { Variable* var = expr->var(); if (!var->is_global()) { Slot* slot = var->slot(); if (slot != NULL) { Slot::Type type = slot->type(); // When LOOKUP slots are enabled, some currently dead code // implementing unary typeof will become live. if (type == Slot::LOOKUP) { BAILOUT("Lookup slot"); } } else { // If not global or a slot, it is a parameter rewritten to an explicit // property reference on the (shadow) arguments object. #ifdef DEBUG Property* property = var->AsProperty(); ASSERT_NOT_NULL(property); Variable* object = property->obj()->AsVariableProxy()->AsVariable(); ASSERT_NOT_NULL(object); ASSERT_NOT_NULL(object->slot()); ASSERT_NOT_NULL(property->key()->AsLiteral()); ASSERT(property->key()->AsLiteral()->handle()->IsSmi()); #endif } } } void FullCodeGenSyntaxChecker::VisitLiteral(Literal* expr) { // Supported. } void FullCodeGenSyntaxChecker::VisitRegExpLiteral(RegExpLiteral* expr) { // Supported. } void FullCodeGenSyntaxChecker::VisitObjectLiteral(ObjectLiteral* expr) { ZoneList* 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* 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) { // We support plain non-compound assignments to properties, parameters and // non-context (stack-allocated) locals, and global variables. 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 global variables are supported. if (!var->is_global()) { ASSERT(var->slot() != NULL); Slot::Type type = var->slot()->type(); if (type == Slot::LOOKUP) { BAILOUT("Lookup slot"); } } } 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* 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* 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::VOID: case Token::NOT: case Token::TYPEOF: Visit(expr->expression()); break; case Token::BIT_NOT: BAILOUT("UnaryOperation: BIT_NOT"); case Token::DELETE: BAILOUT("UnaryOperation: DELETE"); case Token::ADD: BAILOUT("UnaryOperation: ADD"); case Token::SUB: BAILOUT("UnaryOperation: SUB"); 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 FullCodeGenerator::MakeCode(FunctionLiteral* fun, Handle