diff options
author | Ali Ijaz Sheikh <ofrobots@google.com> | 2016-03-01 08:58:05 -0800 |
---|---|---|
committer | Ali Sheikh <ofrobots@lemonhope.roam.corp.google.com> | 2016-03-03 20:35:20 -0800 |
commit | 069e02ab47656b3efd1b6829c65856b2e1c2d1db (patch) | |
tree | eb643e0a2e88fd64bb9fc927423458d2ae96c2db /deps/v8/src/ast | |
parent | 8938355398c79f583a468284b768652d12ba9bc9 (diff) | |
download | node-new-069e02ab47656b3efd1b6829c65856b2e1c2d1db.tar.gz |
deps: upgrade to V8 4.9.385.18
Pick up the current branch head for V8 4.9
https://github.com/v8/v8/commit/1ecba0f
PR-URL: https://github.com/nodejs/node/pull/4722
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Michaƫl Zasso <mic.besace@gmail.com>
Diffstat (limited to 'deps/v8/src/ast')
23 files changed, 12982 insertions, 0 deletions
diff --git a/deps/v8/src/ast/OWNERS b/deps/v8/src/ast/OWNERS new file mode 100644 index 0000000000..7cd947998d --- /dev/null +++ b/deps/v8/src/ast/OWNERS @@ -0,0 +1,7 @@ +set noparent + +adamk@chromium.org +bmeurer@chromium.org +littledan@chromium.org +mstarzinger@chromium.org +rossberg@chromium.org diff --git a/deps/v8/src/ast/ast-expression-rewriter.cc b/deps/v8/src/ast/ast-expression-rewriter.cc new file mode 100644 index 0000000000..49cc7f6ff4 --- /dev/null +++ b/deps/v8/src/ast/ast-expression-rewriter.cc @@ -0,0 +1,409 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/ast.h" +#include "src/ast/ast-expression-rewriter.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// Implementation of AstExpressionRewriter +// The AST is traversed but no actual rewriting takes place, unless the +// Visit methods are overriden in subclasses. + +#define REWRITE_THIS(node) \ + do { \ + if (!RewriteExpression(node)) return; \ + } while (false) +#define NOTHING() DCHECK_NULL(replacement_) + + +void AstExpressionRewriter::VisitDeclarations( + ZoneList<Declaration*>* declarations) { + for (int i = 0; i < declarations->length(); i++) { + AST_REWRITE_LIST_ELEMENT(Declaration, declarations, i); + } +} + + +void AstExpressionRewriter::VisitStatements(ZoneList<Statement*>* statements) { + for (int i = 0; i < statements->length(); i++) { + AST_REWRITE_LIST_ELEMENT(Statement, statements, i); + // Not stopping when a jump statement is found. + } +} + + +void AstExpressionRewriter::VisitExpressions( + ZoneList<Expression*>* expressions) { + for (int i = 0; i < expressions->length(); i++) { + // The variable statement visiting code may pass NULL expressions + // to this code. Maybe this should be handled by introducing an + // undefined expression or literal? Revisit this code if this + // changes + if (expressions->at(i) != nullptr) { + AST_REWRITE_LIST_ELEMENT(Expression, expressions, i); + } + } +} + + +void AstExpressionRewriter::VisitVariableDeclaration( + VariableDeclaration* node) { + // Not visiting `proxy_`. + NOTHING(); +} + + +void AstExpressionRewriter::VisitFunctionDeclaration( + FunctionDeclaration* node) { + // Not visiting `proxy_`. + AST_REWRITE_PROPERTY(FunctionLiteral, node, fun); +} + + +void AstExpressionRewriter::VisitImportDeclaration(ImportDeclaration* node) { + // Not visiting `proxy_`. + NOTHING(); +} + + +void AstExpressionRewriter::VisitExportDeclaration(ExportDeclaration* node) { + // Not visiting `proxy_`. + NOTHING(); +} + + +void AstExpressionRewriter::VisitBlock(Block* node) { + VisitStatements(node->statements()); +} + + +void AstExpressionRewriter::VisitExpressionStatement( + ExpressionStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, expression); +} + + +void AstExpressionRewriter::VisitEmptyStatement(EmptyStatement* node) { + NOTHING(); +} + + +void AstExpressionRewriter::VisitSloppyBlockFunctionStatement( + SloppyBlockFunctionStatement* node) { + AST_REWRITE_PROPERTY(Statement, node, statement); +} + + +void AstExpressionRewriter::VisitIfStatement(IfStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, condition); + AST_REWRITE_PROPERTY(Statement, node, then_statement); + AST_REWRITE_PROPERTY(Statement, node, else_statement); +} + + +void AstExpressionRewriter::VisitContinueStatement(ContinueStatement* node) { + NOTHING(); +} + + +void AstExpressionRewriter::VisitBreakStatement(BreakStatement* node) { + NOTHING(); +} + + +void AstExpressionRewriter::VisitReturnStatement(ReturnStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, expression); +} + + +void AstExpressionRewriter::VisitWithStatement(WithStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, expression); + AST_REWRITE_PROPERTY(Statement, node, statement); +} + + +void AstExpressionRewriter::VisitSwitchStatement(SwitchStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, tag); + ZoneList<CaseClause*>* clauses = node->cases(); + for (int i = 0; i < clauses->length(); i++) { + AST_REWRITE_LIST_ELEMENT(CaseClause, clauses, i); + } +} + + +void AstExpressionRewriter::VisitDoWhileStatement(DoWhileStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, cond); + AST_REWRITE_PROPERTY(Statement, node, body); +} + + +void AstExpressionRewriter::VisitWhileStatement(WhileStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, cond); + AST_REWRITE_PROPERTY(Statement, node, body); +} + + +void AstExpressionRewriter::VisitForStatement(ForStatement* node) { + if (node->init() != nullptr) { + AST_REWRITE_PROPERTY(Statement, node, init); + } + if (node->cond() != nullptr) { + AST_REWRITE_PROPERTY(Expression, node, cond); + } + if (node->next() != nullptr) { + AST_REWRITE_PROPERTY(Statement, node, next); + } + AST_REWRITE_PROPERTY(Statement, node, body); +} + + +void AstExpressionRewriter::VisitForInStatement(ForInStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, each); + AST_REWRITE_PROPERTY(Expression, node, subject); + AST_REWRITE_PROPERTY(Statement, node, body); +} + + +void AstExpressionRewriter::VisitForOfStatement(ForOfStatement* node) { + AST_REWRITE_PROPERTY(Expression, node, each); + AST_REWRITE_PROPERTY(Expression, node, assign_iterator); + AST_REWRITE_PROPERTY(Expression, node, next_result); + AST_REWRITE_PROPERTY(Expression, node, result_done); + AST_REWRITE_PROPERTY(Expression, node, assign_each); + AST_REWRITE_PROPERTY(Expression, node, subject); + AST_REWRITE_PROPERTY(Statement, node, body); +} + + +void AstExpressionRewriter::VisitTryCatchStatement(TryCatchStatement* node) { + AST_REWRITE_PROPERTY(Block, node, try_block); + // Not visiting the variable. + AST_REWRITE_PROPERTY(Block, node, catch_block); +} + + +void AstExpressionRewriter::VisitTryFinallyStatement( + TryFinallyStatement* node) { + AST_REWRITE_PROPERTY(Block, node, try_block); + AST_REWRITE_PROPERTY(Block, node, finally_block); +} + + +void AstExpressionRewriter::VisitDebuggerStatement(DebuggerStatement* node) { + NOTHING(); +} + + +void AstExpressionRewriter::VisitFunctionLiteral(FunctionLiteral* node) { + REWRITE_THIS(node); + VisitDeclarations(node->scope()->declarations()); + ZoneList<Statement*>* body = node->body(); + if (body != nullptr) VisitStatements(body); +} + + +void AstExpressionRewriter::VisitClassLiteral(ClassLiteral* node) { + REWRITE_THIS(node); + // Not visiting `class_variable_proxy_`. + if (node->extends() != nullptr) { + AST_REWRITE_PROPERTY(Expression, node, extends); + } + AST_REWRITE_PROPERTY(FunctionLiteral, node, constructor); + ZoneList<typename ClassLiteral::Property*>* properties = node->properties(); + for (int i = 0; i < properties->length(); i++) { + VisitObjectLiteralProperty(properties->at(i)); + } +} + + +void AstExpressionRewriter::VisitNativeFunctionLiteral( + NativeFunctionLiteral* node) { + REWRITE_THIS(node); + NOTHING(); +} + + +void AstExpressionRewriter::VisitConditional(Conditional* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, condition); + AST_REWRITE_PROPERTY(Expression, node, then_expression); + AST_REWRITE_PROPERTY(Expression, node, else_expression); +} + + +void AstExpressionRewriter::VisitVariableProxy(VariableProxy* node) { + REWRITE_THIS(node); + NOTHING(); +} + + +void AstExpressionRewriter::VisitLiteral(Literal* node) { + REWRITE_THIS(node); + NOTHING(); +} + + +void AstExpressionRewriter::VisitRegExpLiteral(RegExpLiteral* node) { + REWRITE_THIS(node); + NOTHING(); +} + + +void AstExpressionRewriter::VisitObjectLiteral(ObjectLiteral* node) { + REWRITE_THIS(node); + ZoneList<typename ObjectLiteral::Property*>* properties = node->properties(); + for (int i = 0; i < properties->length(); i++) { + VisitObjectLiteralProperty(properties->at(i)); + } +} + + +void AstExpressionRewriter::VisitObjectLiteralProperty( + ObjectLiteralProperty* property) { + if (property == nullptr) return; + AST_REWRITE_PROPERTY(Expression, property, key); + AST_REWRITE_PROPERTY(Expression, property, value); +} + + +void AstExpressionRewriter::VisitArrayLiteral(ArrayLiteral* node) { + REWRITE_THIS(node); + VisitExpressions(node->values()); +} + + +void AstExpressionRewriter::VisitAssignment(Assignment* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, target); + AST_REWRITE_PROPERTY(Expression, node, value); +} + + +void AstExpressionRewriter::VisitYield(Yield* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, generator_object); + AST_REWRITE_PROPERTY(Expression, node, expression); +} + + +void AstExpressionRewriter::VisitThrow(Throw* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, exception); +} + + +void AstExpressionRewriter::VisitProperty(Property* node) { + REWRITE_THIS(node); + if (node == nullptr) return; + AST_REWRITE_PROPERTY(Expression, node, obj); + AST_REWRITE_PROPERTY(Expression, node, key); +} + + +void AstExpressionRewriter::VisitCall(Call* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, expression); + VisitExpressions(node->arguments()); +} + + +void AstExpressionRewriter::VisitCallNew(CallNew* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, expression); + VisitExpressions(node->arguments()); +} + + +void AstExpressionRewriter::VisitCallRuntime(CallRuntime* node) { + REWRITE_THIS(node); + VisitExpressions(node->arguments()); +} + + +void AstExpressionRewriter::VisitUnaryOperation(UnaryOperation* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, expression); +} + + +void AstExpressionRewriter::VisitCountOperation(CountOperation* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, expression); +} + + +void AstExpressionRewriter::VisitBinaryOperation(BinaryOperation* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, left); + AST_REWRITE_PROPERTY(Expression, node, right); +} + + +void AstExpressionRewriter::VisitCompareOperation(CompareOperation* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, left); + AST_REWRITE_PROPERTY(Expression, node, right); +} + + +void AstExpressionRewriter::VisitSpread(Spread* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, expression); +} + + +void AstExpressionRewriter::VisitThisFunction(ThisFunction* node) { + REWRITE_THIS(node); + NOTHING(); +} + + +void AstExpressionRewriter::VisitSuperPropertyReference( + SuperPropertyReference* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(VariableProxy, node, this_var); + AST_REWRITE_PROPERTY(Expression, node, home_object); +} + + +void AstExpressionRewriter::VisitSuperCallReference(SuperCallReference* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(VariableProxy, node, this_var); + AST_REWRITE_PROPERTY(VariableProxy, node, new_target_var); + AST_REWRITE_PROPERTY(VariableProxy, node, this_function_var); +} + + +void AstExpressionRewriter::VisitCaseClause(CaseClause* node) { + if (!node->is_default()) { + AST_REWRITE_PROPERTY(Expression, node, label); + } + VisitStatements(node->statements()); +} + + +void AstExpressionRewriter::VisitEmptyParentheses(EmptyParentheses* node) { + NOTHING(); +} + + +void AstExpressionRewriter::VisitDoExpression(DoExpression* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Block, node, block); + AST_REWRITE_PROPERTY(VariableProxy, node, result); +} + + +void AstExpressionRewriter::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* node) { + REWRITE_THIS(node); + AST_REWRITE_PROPERTY(Expression, node, expression); +} + + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/ast-expression-rewriter.h b/deps/v8/src/ast/ast-expression-rewriter.h new file mode 100644 index 0000000000..916842ab20 --- /dev/null +++ b/deps/v8/src/ast/ast-expression-rewriter.h @@ -0,0 +1,54 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_AST_EXPRESSION_REWRITER_H_ +#define V8_AST_AST_EXPRESSION_REWRITER_H_ + +#include "src/allocation.h" +#include "src/ast/ast.h" +#include "src/ast/scopes.h" +#include "src/effects.h" +#include "src/type-info.h" +#include "src/types.h" +#include "src/zone.h" + +namespace v8 { +namespace internal { + +// A rewriting Visitor over a CompilationInfo's AST that invokes +// VisitExpression on each expression node. + +class AstExpressionRewriter : public AstVisitor { + public: + explicit AstExpressionRewriter(Isolate* isolate) : AstVisitor() { + InitializeAstRewriter(isolate); + } + explicit AstExpressionRewriter(uintptr_t stack_limit) : AstVisitor() { + InitializeAstRewriter(stack_limit); + } + ~AstExpressionRewriter() override {} + + void VisitDeclarations(ZoneList<Declaration*>* declarations) override; + void VisitStatements(ZoneList<Statement*>* statements) override; + void VisitExpressions(ZoneList<Expression*>* expressions) override; + + virtual void VisitObjectLiteralProperty(ObjectLiteralProperty* property); + + protected: + virtual bool RewriteExpression(Expression* expr) = 0; + + private: + DEFINE_AST_REWRITER_SUBCLASS_MEMBERS(); + +#define DECLARE_VISIT(type) void Visit##type(type* node) override; + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + DISALLOW_COPY_AND_ASSIGN(AstExpressionRewriter); +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_AST_AST_EXPRESSION_REWRITER_H_ diff --git a/deps/v8/src/ast/ast-expression-visitor.cc b/deps/v8/src/ast/ast-expression-visitor.cc new file mode 100644 index 0000000000..6b2550c541 --- /dev/null +++ b/deps/v8/src/ast/ast-expression-visitor.cc @@ -0,0 +1,410 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/v8.h" + +#include "src/ast/ast-expression-visitor.h" + +#include "src/ast/ast.h" +#include "src/ast/scopes.h" +#include "src/codegen.h" + +namespace v8 { +namespace internal { + + +#define RECURSE(call) \ + do { \ + DCHECK(!HasStackOverflow()); \ + call; \ + if (HasStackOverflow()) return; \ + } while (false) + + +#define RECURSE_EXPRESSION(call) \ + do { \ + DCHECK(!HasStackOverflow()); \ + ++depth_; \ + call; \ + --depth_; \ + if (HasStackOverflow()) return; \ + } while (false) + + +AstExpressionVisitor::AstExpressionVisitor(Isolate* isolate, Expression* root) + : root_(root), depth_(0) { + InitializeAstVisitor(isolate); +} + + +AstExpressionVisitor::AstExpressionVisitor(uintptr_t stack_limit, + Expression* root) + : root_(root), depth_(0) { + InitializeAstVisitor(stack_limit); +} + + +void AstExpressionVisitor::Run() { RECURSE(Visit(root_)); } + + +void AstExpressionVisitor::VisitVariableDeclaration(VariableDeclaration* decl) { +} + + +void AstExpressionVisitor::VisitFunctionDeclaration(FunctionDeclaration* decl) { + RECURSE(Visit(decl->fun())); +} + + +void AstExpressionVisitor::VisitImportDeclaration(ImportDeclaration* decl) {} + + +void AstExpressionVisitor::VisitExportDeclaration(ExportDeclaration* decl) {} + + +void AstExpressionVisitor::VisitStatements(ZoneList<Statement*>* stmts) { + for (int i = 0; i < stmts->length(); ++i) { + Statement* stmt = stmts->at(i); + RECURSE(Visit(stmt)); + if (stmt->IsJump()) break; + } +} + + +void AstExpressionVisitor::VisitBlock(Block* stmt) { + RECURSE(VisitStatements(stmt->statements())); +} + + +void AstExpressionVisitor::VisitExpressionStatement(ExpressionStatement* stmt) { + RECURSE(Visit(stmt->expression())); +} + + +void AstExpressionVisitor::VisitEmptyStatement(EmptyStatement* stmt) {} + + +void AstExpressionVisitor::VisitSloppyBlockFunctionStatement( + SloppyBlockFunctionStatement* stmt) { + RECURSE(Visit(stmt->statement())); +} + + +void AstExpressionVisitor::VisitIfStatement(IfStatement* stmt) { + RECURSE(Visit(stmt->condition())); + RECURSE(Visit(stmt->then_statement())); + RECURSE(Visit(stmt->else_statement())); +} + + +void AstExpressionVisitor::VisitContinueStatement(ContinueStatement* stmt) {} + + +void AstExpressionVisitor::VisitBreakStatement(BreakStatement* stmt) {} + + +void AstExpressionVisitor::VisitReturnStatement(ReturnStatement* stmt) { + RECURSE(Visit(stmt->expression())); +} + + +void AstExpressionVisitor::VisitWithStatement(WithStatement* stmt) { + RECURSE(stmt->expression()); + RECURSE(stmt->statement()); +} + + +void AstExpressionVisitor::VisitSwitchStatement(SwitchStatement* stmt) { + RECURSE(Visit(stmt->tag())); + + ZoneList<CaseClause*>* clauses = stmt->cases(); + + for (int i = 0; i < clauses->length(); ++i) { + CaseClause* clause = clauses->at(i); + if (!clause->is_default()) { + Expression* label = clause->label(); + RECURSE(Visit(label)); + } + ZoneList<Statement*>* stmts = clause->statements(); + RECURSE(VisitStatements(stmts)); + } +} + + +void AstExpressionVisitor::VisitCaseClause(CaseClause* clause) { + UNREACHABLE(); +} + + +void AstExpressionVisitor::VisitDoWhileStatement(DoWhileStatement* stmt) { + RECURSE(Visit(stmt->body())); + RECURSE(Visit(stmt->cond())); +} + + +void AstExpressionVisitor::VisitWhileStatement(WhileStatement* stmt) { + RECURSE(Visit(stmt->cond())); + RECURSE(Visit(stmt->body())); +} + + +void AstExpressionVisitor::VisitForStatement(ForStatement* stmt) { + if (stmt->init() != NULL) { + RECURSE(Visit(stmt->init())); + } + if (stmt->cond() != NULL) { + RECURSE(Visit(stmt->cond())); + } + if (stmt->next() != NULL) { + RECURSE(Visit(stmt->next())); + } + RECURSE(Visit(stmt->body())); +} + + +void AstExpressionVisitor::VisitForInStatement(ForInStatement* stmt) { + RECURSE(Visit(stmt->enumerable())); + RECURSE(Visit(stmt->body())); +} + + +void AstExpressionVisitor::VisitForOfStatement(ForOfStatement* stmt) { + RECURSE(Visit(stmt->iterable())); + RECURSE(Visit(stmt->each())); + RECURSE(Visit(stmt->assign_iterator())); + RECURSE(Visit(stmt->next_result())); + RECURSE(Visit(stmt->result_done())); + RECURSE(Visit(stmt->assign_each())); + RECURSE(Visit(stmt->body())); +} + + +void AstExpressionVisitor::VisitTryCatchStatement(TryCatchStatement* stmt) { + RECURSE(Visit(stmt->try_block())); + RECURSE(Visit(stmt->catch_block())); +} + + +void AstExpressionVisitor::VisitTryFinallyStatement(TryFinallyStatement* stmt) { + RECURSE(Visit(stmt->try_block())); + RECURSE(Visit(stmt->finally_block())); +} + + +void AstExpressionVisitor::VisitDebuggerStatement(DebuggerStatement* stmt) {} + + +void AstExpressionVisitor::VisitFunctionLiteral(FunctionLiteral* expr) { + Scope* scope = expr->scope(); + VisitExpression(expr); + RECURSE_EXPRESSION(VisitDeclarations(scope->declarations())); + RECURSE_EXPRESSION(VisitStatements(expr->body())); +} + + +void AstExpressionVisitor::VisitNativeFunctionLiteral( + NativeFunctionLiteral* expr) {} + + +void AstExpressionVisitor::VisitDoExpression(DoExpression* expr) { + RECURSE(VisitBlock(expr->block())); + RECURSE(VisitVariableProxy(expr->result())); +} + + +void AstExpressionVisitor::VisitConditional(Conditional* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->condition())); + RECURSE_EXPRESSION(Visit(expr->then_expression())); + RECURSE_EXPRESSION(Visit(expr->else_expression())); +} + + +void AstExpressionVisitor::VisitVariableProxy(VariableProxy* expr) { + VisitExpression(expr); +} + + +void AstExpressionVisitor::VisitLiteral(Literal* expr) { + VisitExpression(expr); +} + + +void AstExpressionVisitor::VisitRegExpLiteral(RegExpLiteral* expr) { + VisitExpression(expr); +} + + +void AstExpressionVisitor::VisitObjectLiteral(ObjectLiteral* expr) { + VisitExpression(expr); + ZoneList<ObjectLiteralProperty*>* props = expr->properties(); + for (int i = 0; i < props->length(); ++i) { + ObjectLiteralProperty* prop = props->at(i); + if (!prop->key()->IsLiteral()) { + RECURSE_EXPRESSION(Visit(prop->key())); + } + RECURSE_EXPRESSION(Visit(prop->value())); + } +} + + +void AstExpressionVisitor::VisitArrayLiteral(ArrayLiteral* expr) { + VisitExpression(expr); + ZoneList<Expression*>* values = expr->values(); + for (int i = 0; i < values->length(); ++i) { + Expression* value = values->at(i); + RECURSE_EXPRESSION(Visit(value)); + } +} + + +void AstExpressionVisitor::VisitAssignment(Assignment* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->target())); + RECURSE_EXPRESSION(Visit(expr->value())); +} + + +void AstExpressionVisitor::VisitYield(Yield* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->generator_object())); + RECURSE_EXPRESSION(Visit(expr->expression())); +} + + +void AstExpressionVisitor::VisitThrow(Throw* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->exception())); +} + + +void AstExpressionVisitor::VisitProperty(Property* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->obj())); + RECURSE_EXPRESSION(Visit(expr->key())); +} + + +void AstExpressionVisitor::VisitCall(Call* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->expression())); + ZoneList<Expression*>* args = expr->arguments(); + for (int i = 0; i < args->length(); ++i) { + Expression* arg = args->at(i); + RECURSE_EXPRESSION(Visit(arg)); + } +} + + +void AstExpressionVisitor::VisitCallNew(CallNew* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->expression())); + ZoneList<Expression*>* args = expr->arguments(); + for (int i = 0; i < args->length(); ++i) { + Expression* arg = args->at(i); + RECURSE_EXPRESSION(Visit(arg)); + } +} + + +void AstExpressionVisitor::VisitCallRuntime(CallRuntime* expr) { + VisitExpression(expr); + ZoneList<Expression*>* args = expr->arguments(); + for (int i = 0; i < args->length(); ++i) { + Expression* arg = args->at(i); + RECURSE_EXPRESSION(Visit(arg)); + } +} + + +void AstExpressionVisitor::VisitUnaryOperation(UnaryOperation* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->expression())); +} + + +void AstExpressionVisitor::VisitCountOperation(CountOperation* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->expression())); +} + + +void AstExpressionVisitor::VisitBinaryOperation(BinaryOperation* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->left())); + RECURSE_EXPRESSION(Visit(expr->right())); +} + + +void AstExpressionVisitor::VisitCompareOperation(CompareOperation* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->left())); + RECURSE_EXPRESSION(Visit(expr->right())); +} + + +void AstExpressionVisitor::VisitThisFunction(ThisFunction* expr) { + VisitExpression(expr); +} + + +void AstExpressionVisitor::VisitDeclarations(ZoneList<Declaration*>* decls) { + for (int i = 0; i < decls->length(); ++i) { + Declaration* decl = decls->at(i); + RECURSE(Visit(decl)); + } +} + + +void AstExpressionVisitor::VisitClassLiteral(ClassLiteral* expr) { + VisitExpression(expr); + if (expr->extends() != nullptr) { + RECURSE_EXPRESSION(Visit(expr->extends())); + } + RECURSE_EXPRESSION(Visit(expr->constructor())); + ZoneList<ObjectLiteralProperty*>* props = expr->properties(); + for (int i = 0; i < props->length(); ++i) { + ObjectLiteralProperty* prop = props->at(i); + if (!prop->key()->IsLiteral()) { + RECURSE_EXPRESSION(Visit(prop->key())); + } + RECURSE_EXPRESSION(Visit(prop->value())); + } +} + + +void AstExpressionVisitor::VisitSpread(Spread* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(Visit(expr->expression())); +} + + +void AstExpressionVisitor::VisitEmptyParentheses(EmptyParentheses* expr) {} + + +void AstExpressionVisitor::VisitSuperPropertyReference( + SuperPropertyReference* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(VisitVariableProxy(expr->this_var())); + RECURSE_EXPRESSION(Visit(expr->home_object())); +} + + +void AstExpressionVisitor::VisitSuperCallReference(SuperCallReference* expr) { + VisitExpression(expr); + RECURSE_EXPRESSION(VisitVariableProxy(expr->this_var())); + RECURSE_EXPRESSION(VisitVariableProxy(expr->new_target_var())); + RECURSE_EXPRESSION(VisitVariableProxy(expr->this_function_var())); +} + + +void AstExpressionVisitor::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* expr) { + VisitExpression(expr); + RECURSE(Visit(expr->expression())); +} + + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/ast-expression-visitor.h b/deps/v8/src/ast/ast-expression-visitor.h new file mode 100644 index 0000000000..cda624d5b7 --- /dev/null +++ b/deps/v8/src/ast/ast-expression-visitor.h @@ -0,0 +1,50 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_AST_EXPRESSION_VISITOR_H_ +#define V8_AST_AST_EXPRESSION_VISITOR_H_ + +#include "src/allocation.h" +#include "src/ast/ast.h" +#include "src/ast/scopes.h" +#include "src/effects.h" +#include "src/type-info.h" +#include "src/types.h" +#include "src/zone.h" + +namespace v8 { +namespace internal { + +// A Visitor over a CompilationInfo's AST that invokes +// VisitExpression on each expression node. + +class AstExpressionVisitor : public AstVisitor { + public: + AstExpressionVisitor(Isolate* isolate, Expression* root); + AstExpressionVisitor(uintptr_t stack_limit, Expression* root); + void Run(); + + protected: + virtual void VisitExpression(Expression* expression) = 0; + int depth() { return depth_; } + + private: + void VisitDeclarations(ZoneList<Declaration*>* d) override; + void VisitStatements(ZoneList<Statement*>* s) override; + + DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); + +#define DECLARE_VISIT(type) void Visit##type(type* node) override; + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + Expression* root_; + int depth_; + + DISALLOW_COPY_AND_ASSIGN(AstExpressionVisitor); +}; +} // namespace internal +} // namespace v8 + +#endif // V8_AST_AST_EXPRESSION_VISITOR_H_ diff --git a/deps/v8/src/ast/ast-literal-reindexer.cc b/deps/v8/src/ast/ast-literal-reindexer.cc new file mode 100644 index 0000000000..fce33e70b8 --- /dev/null +++ b/deps/v8/src/ast/ast-literal-reindexer.cc @@ -0,0 +1,331 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/ast-literal-reindexer.h" + +#include "src/ast/ast.h" +#include "src/ast/scopes.h" + +namespace v8 { +namespace internal { + + +void AstLiteralReindexer::VisitVariableDeclaration(VariableDeclaration* node) { + VisitVariableProxy(node->proxy()); +} + + +void AstLiteralReindexer::VisitExportDeclaration(ExportDeclaration* node) { + VisitVariableProxy(node->proxy()); +} + + +void AstLiteralReindexer::VisitEmptyStatement(EmptyStatement* node) {} + + +void AstLiteralReindexer::VisitSloppyBlockFunctionStatement( + SloppyBlockFunctionStatement* node) { + Visit(node->statement()); +} + + +void AstLiteralReindexer::VisitContinueStatement(ContinueStatement* node) {} + + +void AstLiteralReindexer::VisitBreakStatement(BreakStatement* node) {} + + +void AstLiteralReindexer::VisitDebuggerStatement(DebuggerStatement* node) {} + + +void AstLiteralReindexer::VisitNativeFunctionLiteral( + NativeFunctionLiteral* node) {} + + +void AstLiteralReindexer::VisitDoExpression(DoExpression* node) { + // TODO(caitp): literals in do expressions need re-indexing too. +} + + +void AstLiteralReindexer::VisitLiteral(Literal* node) {} + + +void AstLiteralReindexer::VisitRegExpLiteral(RegExpLiteral* node) { + UpdateIndex(node); +} + + +void AstLiteralReindexer::VisitVariableProxy(VariableProxy* node) {} + + +void AstLiteralReindexer::VisitThisFunction(ThisFunction* node) {} + + +void AstLiteralReindexer::VisitSuperPropertyReference( + SuperPropertyReference* node) { + Visit(node->this_var()); + Visit(node->home_object()); +} + + +void AstLiteralReindexer::VisitSuperCallReference(SuperCallReference* node) { + Visit(node->this_var()); + Visit(node->new_target_var()); + Visit(node->this_function_var()); +} + + +void AstLiteralReindexer::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* node) { + Visit(node->expression()); +} + + +void AstLiteralReindexer::VisitImportDeclaration(ImportDeclaration* node) { + VisitVariableProxy(node->proxy()); +} + + +void AstLiteralReindexer::VisitExpressionStatement(ExpressionStatement* node) { + Visit(node->expression()); +} + + +void AstLiteralReindexer::VisitReturnStatement(ReturnStatement* node) { + Visit(node->expression()); +} + + +void AstLiteralReindexer::VisitYield(Yield* node) { + Visit(node->generator_object()); + Visit(node->expression()); +} + + +void AstLiteralReindexer::VisitThrow(Throw* node) { Visit(node->exception()); } + + +void AstLiteralReindexer::VisitUnaryOperation(UnaryOperation* node) { + Visit(node->expression()); +} + + +void AstLiteralReindexer::VisitCountOperation(CountOperation* node) { + Visit(node->expression()); +} + + +void AstLiteralReindexer::VisitBlock(Block* node) { + VisitStatements(node->statements()); +} + + +void AstLiteralReindexer::VisitFunctionDeclaration(FunctionDeclaration* node) { + VisitVariableProxy(node->proxy()); + VisitFunctionLiteral(node->fun()); +} + + +void AstLiteralReindexer::VisitCallRuntime(CallRuntime* node) { + VisitArguments(node->arguments()); +} + + +void AstLiteralReindexer::VisitWithStatement(WithStatement* node) { + Visit(node->expression()); + Visit(node->statement()); +} + + +void AstLiteralReindexer::VisitDoWhileStatement(DoWhileStatement* node) { + Visit(node->body()); + Visit(node->cond()); +} + + +void AstLiteralReindexer::VisitWhileStatement(WhileStatement* node) { + Visit(node->cond()); + Visit(node->body()); +} + + +void AstLiteralReindexer::VisitTryCatchStatement(TryCatchStatement* node) { + Visit(node->try_block()); + Visit(node->catch_block()); +} + + +void AstLiteralReindexer::VisitTryFinallyStatement(TryFinallyStatement* node) { + Visit(node->try_block()); + Visit(node->finally_block()); +} + + +void AstLiteralReindexer::VisitProperty(Property* node) { + Visit(node->key()); + Visit(node->obj()); +} + + +void AstLiteralReindexer::VisitAssignment(Assignment* node) { + Visit(node->target()); + Visit(node->value()); +} + + +void AstLiteralReindexer::VisitBinaryOperation(BinaryOperation* node) { + Visit(node->left()); + Visit(node->right()); +} + + +void AstLiteralReindexer::VisitCompareOperation(CompareOperation* node) { + Visit(node->left()); + Visit(node->right()); +} + + +void AstLiteralReindexer::VisitSpread(Spread* node) { + Visit(node->expression()); +} + + +void AstLiteralReindexer::VisitEmptyParentheses(EmptyParentheses* node) {} + + +void AstLiteralReindexer::VisitForInStatement(ForInStatement* node) { + Visit(node->each()); + Visit(node->enumerable()); + Visit(node->body()); +} + + +void AstLiteralReindexer::VisitForOfStatement(ForOfStatement* node) { + Visit(node->assign_iterator()); + Visit(node->next_result()); + Visit(node->result_done()); + Visit(node->assign_each()); + Visit(node->body()); +} + + +void AstLiteralReindexer::VisitConditional(Conditional* node) { + Visit(node->condition()); + Visit(node->then_expression()); + Visit(node->else_expression()); +} + + +void AstLiteralReindexer::VisitIfStatement(IfStatement* node) { + Visit(node->condition()); + Visit(node->then_statement()); + if (node->HasElseStatement()) { + Visit(node->else_statement()); + } +} + + +void AstLiteralReindexer::VisitSwitchStatement(SwitchStatement* node) { + Visit(node->tag()); + ZoneList<CaseClause*>* cases = node->cases(); + for (int i = 0; i < cases->length(); i++) { + VisitCaseClause(cases->at(i)); + } +} + + +void AstLiteralReindexer::VisitCaseClause(CaseClause* node) { + if (!node->is_default()) Visit(node->label()); + VisitStatements(node->statements()); +} + + +void AstLiteralReindexer::VisitForStatement(ForStatement* node) { + if (node->init() != NULL) Visit(node->init()); + if (node->cond() != NULL) Visit(node->cond()); + if (node->next() != NULL) Visit(node->next()); + Visit(node->body()); +} + + +void AstLiteralReindexer::VisitClassLiteral(ClassLiteral* node) { + if (node->extends()) Visit(node->extends()); + if (node->constructor()) Visit(node->constructor()); + if (node->class_variable_proxy()) { + VisitVariableProxy(node->class_variable_proxy()); + } + for (int i = 0; i < node->properties()->length(); i++) { + VisitObjectLiteralProperty(node->properties()->at(i)); + } +} + + +void AstLiteralReindexer::VisitObjectLiteral(ObjectLiteral* node) { + UpdateIndex(node); + for (int i = 0; i < node->properties()->length(); i++) { + VisitObjectLiteralProperty(node->properties()->at(i)); + } +} + + +void AstLiteralReindexer::VisitObjectLiteralProperty( + ObjectLiteralProperty* node) { + Visit(node->key()); + Visit(node->value()); +} + + +void AstLiteralReindexer::VisitArrayLiteral(ArrayLiteral* node) { + UpdateIndex(node); + for (int i = 0; i < node->values()->length(); i++) { + Visit(node->values()->at(i)); + } +} + + +void AstLiteralReindexer::VisitCall(Call* node) { + Visit(node->expression()); + VisitArguments(node->arguments()); +} + + +void AstLiteralReindexer::VisitCallNew(CallNew* node) { + Visit(node->expression()); + VisitArguments(node->arguments()); +} + + +void AstLiteralReindexer::VisitStatements(ZoneList<Statement*>* statements) { + if (statements == NULL) return; + for (int i = 0; i < statements->length(); i++) { + Visit(statements->at(i)); + } +} + + +void AstLiteralReindexer::VisitDeclarations( + ZoneList<Declaration*>* declarations) { + for (int i = 0; i < declarations->length(); i++) { + Visit(declarations->at(i)); + } +} + + +void AstLiteralReindexer::VisitArguments(ZoneList<Expression*>* arguments) { + for (int i = 0; i < arguments->length(); i++) { + Visit(arguments->at(i)); + } +} + + +void AstLiteralReindexer::VisitFunctionLiteral(FunctionLiteral* node) { + // We don't recurse into the declarations or body of the function literal: +} + + +void AstLiteralReindexer::Reindex(Expression* pattern) { + pattern->Accept(this); +} +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/ast-literal-reindexer.h b/deps/v8/src/ast/ast-literal-reindexer.h new file mode 100644 index 0000000000..e2a71d3c47 --- /dev/null +++ b/deps/v8/src/ast/ast-literal-reindexer.h @@ -0,0 +1,44 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_AST_LITERAL_REINDEXER +#define V8_AST_AST_LITERAL_REINDEXER + +#include "src/ast/ast.h" +#include "src/ast/scopes.h" + +namespace v8 { +namespace internal { + +class AstLiteralReindexer final : public AstVisitor { + public: + AstLiteralReindexer() : AstVisitor(), next_index_(0) {} + + int count() const { return next_index_; } + void Reindex(Expression* pattern); + + private: +#define DEFINE_VISIT(type) void Visit##type(type* node) override; + AST_NODE_LIST(DEFINE_VISIT) +#undef DEFINE_VISIT + + void VisitStatements(ZoneList<Statement*>* statements) override; + void VisitDeclarations(ZoneList<Declaration*>* declarations) override; + void VisitArguments(ZoneList<Expression*>* arguments); + void VisitObjectLiteralProperty(ObjectLiteralProperty* property); + + void UpdateIndex(MaterializedLiteral* literal) { + literal->literal_index_ = next_index_++; + } + + void Visit(AstNode* node) override { node->Accept(this); } + + int next_index_; + + DISALLOW_COPY_AND_ASSIGN(AstLiteralReindexer); +}; +} // namespace internal +} // namespace v8 + +#endif // V8_AST_AST_LITERAL_REINDEXER diff --git a/deps/v8/src/ast/ast-numbering.cc b/deps/v8/src/ast/ast-numbering.cc new file mode 100644 index 0000000000..6c2b696a5d --- /dev/null +++ b/deps/v8/src/ast/ast-numbering.cc @@ -0,0 +1,607 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/ast-numbering.h" + +#include "src/ast/ast.h" +#include "src/ast/scopes.h" + +namespace v8 { +namespace internal { + +class AstNumberingVisitor final : public AstVisitor { + public: + AstNumberingVisitor(Isolate* isolate, Zone* zone) + : AstVisitor(), + isolate_(isolate), + zone_(zone), + next_id_(BailoutId::FirstUsable().ToInt()), + properties_(zone), + slot_cache_(zone), + dont_optimize_reason_(kNoReason) { + InitializeAstVisitor(isolate); + } + + bool Renumber(FunctionLiteral* node); + + private: +// AST node visitor interface. +#define DEFINE_VISIT(type) void Visit##type(type* node) override; + AST_NODE_LIST(DEFINE_VISIT) +#undef DEFINE_VISIT + + bool Finish(FunctionLiteral* node); + + void VisitVariableProxyReference(VariableProxy* node); + void VisitPropertyReference(Property* node); + void VisitReference(Expression* expr); + + void VisitStatements(ZoneList<Statement*>* statements) override; + void VisitDeclarations(ZoneList<Declaration*>* declarations) override; + void VisitArguments(ZoneList<Expression*>* arguments); + void VisitObjectLiteralProperty(ObjectLiteralProperty* property); + + int ReserveIdRange(int n) { + int tmp = next_id_; + next_id_ += n; + return tmp; + } + + void IncrementNodeCount() { properties_.add_node_count(1); } + void DisableSelfOptimization() { + properties_.flags() |= AstProperties::kDontSelfOptimize; + } + void DisableOptimization(BailoutReason reason) { + dont_optimize_reason_ = reason; + DisableSelfOptimization(); + } + void DisableCrankshaft(BailoutReason reason) { + if (FLAG_turbo_shipping) { + properties_.flags() |= AstProperties::kDontCrankshaft; + } else { + dont_optimize_reason_ = reason; + DisableSelfOptimization(); + } + } + + template <typename Node> + void ReserveFeedbackSlots(Node* node) { + node->AssignFeedbackVectorSlots(isolate_, properties_.get_spec(), + &slot_cache_); + } + + BailoutReason dont_optimize_reason() const { return dont_optimize_reason_; } + + Isolate* isolate_; + Zone* zone_; + int next_id_; + AstProperties properties_; + // The slot cache allows us to reuse certain feedback vector slots. + FeedbackVectorSlotCache slot_cache_; + BailoutReason dont_optimize_reason_; + + DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); + DISALLOW_COPY_AND_ASSIGN(AstNumberingVisitor); +}; + + +void AstNumberingVisitor::VisitVariableDeclaration(VariableDeclaration* node) { + IncrementNodeCount(); + VisitVariableProxy(node->proxy()); +} + + +void AstNumberingVisitor::VisitExportDeclaration(ExportDeclaration* node) { + IncrementNodeCount(); + DisableOptimization(kExportDeclaration); + VisitVariableProxy(node->proxy()); +} + + +void AstNumberingVisitor::VisitEmptyStatement(EmptyStatement* node) { + IncrementNodeCount(); +} + + +void AstNumberingVisitor::VisitSloppyBlockFunctionStatement( + SloppyBlockFunctionStatement* node) { + IncrementNodeCount(); + Visit(node->statement()); +} + + +void AstNumberingVisitor::VisitContinueStatement(ContinueStatement* node) { + IncrementNodeCount(); +} + + +void AstNumberingVisitor::VisitBreakStatement(BreakStatement* node) { + IncrementNodeCount(); +} + + +void AstNumberingVisitor::VisitDebuggerStatement(DebuggerStatement* node) { + IncrementNodeCount(); + DisableOptimization(kDebuggerStatement); + node->set_base_id(ReserveIdRange(DebuggerStatement::num_ids())); +} + + +void AstNumberingVisitor::VisitNativeFunctionLiteral( + NativeFunctionLiteral* node) { + IncrementNodeCount(); + DisableOptimization(kNativeFunctionLiteral); + node->set_base_id(ReserveIdRange(NativeFunctionLiteral::num_ids())); +} + + +void AstNumberingVisitor::VisitDoExpression(DoExpression* node) { + IncrementNodeCount(); + DisableCrankshaft(kDoExpression); + node->set_base_id(ReserveIdRange(DoExpression::num_ids())); + Visit(node->block()); + Visit(node->result()); +} + + +void AstNumberingVisitor::VisitLiteral(Literal* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(Literal::num_ids())); +} + + +void AstNumberingVisitor::VisitRegExpLiteral(RegExpLiteral* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(RegExpLiteral::num_ids())); +} + + +void AstNumberingVisitor::VisitVariableProxyReference(VariableProxy* node) { + IncrementNodeCount(); + if (node->var()->IsLookupSlot()) { + DisableCrankshaft(kReferenceToAVariableWhichRequiresDynamicLookup); + } + node->set_base_id(ReserveIdRange(VariableProxy::num_ids())); +} + + +void AstNumberingVisitor::VisitVariableProxy(VariableProxy* node) { + VisitVariableProxyReference(node); + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitThisFunction(ThisFunction* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(ThisFunction::num_ids())); +} + + +void AstNumberingVisitor::VisitSuperPropertyReference( + SuperPropertyReference* node) { + IncrementNodeCount(); + DisableCrankshaft(kSuperReference); + node->set_base_id(ReserveIdRange(SuperPropertyReference::num_ids())); + Visit(node->this_var()); + Visit(node->home_object()); +} + + +void AstNumberingVisitor::VisitSuperCallReference(SuperCallReference* node) { + IncrementNodeCount(); + DisableCrankshaft(kSuperReference); + node->set_base_id(ReserveIdRange(SuperCallReference::num_ids())); + Visit(node->this_var()); + Visit(node->new_target_var()); + Visit(node->this_function_var()); +} + + +void AstNumberingVisitor::VisitImportDeclaration(ImportDeclaration* node) { + IncrementNodeCount(); + DisableOptimization(kImportDeclaration); + VisitVariableProxy(node->proxy()); +} + + +void AstNumberingVisitor::VisitExpressionStatement(ExpressionStatement* node) { + IncrementNodeCount(); + Visit(node->expression()); +} + + +void AstNumberingVisitor::VisitReturnStatement(ReturnStatement* node) { + IncrementNodeCount(); + Visit(node->expression()); +} + + +void AstNumberingVisitor::VisitYield(Yield* node) { + IncrementNodeCount(); + DisableOptimization(kYield); + ReserveFeedbackSlots(node); + node->set_base_id(ReserveIdRange(Yield::num_ids())); + Visit(node->generator_object()); + Visit(node->expression()); +} + + +void AstNumberingVisitor::VisitThrow(Throw* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(Throw::num_ids())); + Visit(node->exception()); +} + + +void AstNumberingVisitor::VisitUnaryOperation(UnaryOperation* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(UnaryOperation::num_ids())); + Visit(node->expression()); +} + + +void AstNumberingVisitor::VisitCountOperation(CountOperation* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(CountOperation::num_ids())); + Visit(node->expression()); + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitBlock(Block* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(Block::num_ids())); + if (node->scope() != NULL) VisitDeclarations(node->scope()->declarations()); + VisitStatements(node->statements()); +} + + +void AstNumberingVisitor::VisitFunctionDeclaration(FunctionDeclaration* node) { + IncrementNodeCount(); + VisitVariableProxy(node->proxy()); + VisitFunctionLiteral(node->fun()); +} + + +void AstNumberingVisitor::VisitCallRuntime(CallRuntime* node) { + IncrementNodeCount(); + ReserveFeedbackSlots(node); + if (node->is_jsruntime()) { + // Don't try to optimize JS runtime calls because we bailout on them. + DisableOptimization(kCallToAJavaScriptRuntimeFunction); + } + node->set_base_id(ReserveIdRange(CallRuntime::num_ids())); + VisitArguments(node->arguments()); +} + + +void AstNumberingVisitor::VisitWithStatement(WithStatement* node) { + IncrementNodeCount(); + DisableCrankshaft(kWithStatement); + node->set_base_id(ReserveIdRange(WithStatement::num_ids())); + Visit(node->expression()); + Visit(node->statement()); +} + + +void AstNumberingVisitor::VisitDoWhileStatement(DoWhileStatement* node) { + IncrementNodeCount(); + DisableSelfOptimization(); + node->set_base_id(ReserveIdRange(DoWhileStatement::num_ids())); + Visit(node->body()); + Visit(node->cond()); +} + + +void AstNumberingVisitor::VisitWhileStatement(WhileStatement* node) { + IncrementNodeCount(); + DisableSelfOptimization(); + node->set_base_id(ReserveIdRange(WhileStatement::num_ids())); + Visit(node->cond()); + Visit(node->body()); +} + + +void AstNumberingVisitor::VisitTryCatchStatement(TryCatchStatement* node) { + IncrementNodeCount(); + DisableOptimization(kTryCatchStatement); + node->set_base_id(ReserveIdRange(TryCatchStatement::num_ids())); + Visit(node->try_block()); + Visit(node->catch_block()); +} + + +void AstNumberingVisitor::VisitTryFinallyStatement(TryFinallyStatement* node) { + IncrementNodeCount(); + DisableOptimization(kTryFinallyStatement); + node->set_base_id(ReserveIdRange(TryFinallyStatement::num_ids())); + Visit(node->try_block()); + Visit(node->finally_block()); +} + + +void AstNumberingVisitor::VisitPropertyReference(Property* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(Property::num_ids())); + Visit(node->key()); + Visit(node->obj()); +} + + +void AstNumberingVisitor::VisitReference(Expression* expr) { + DCHECK(expr->IsProperty() || expr->IsVariableProxy()); + if (expr->IsProperty()) { + VisitPropertyReference(expr->AsProperty()); + } else { + VisitVariableProxyReference(expr->AsVariableProxy()); + } +} + + +void AstNumberingVisitor::VisitProperty(Property* node) { + VisitPropertyReference(node); + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitAssignment(Assignment* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(Assignment::num_ids())); + + if (node->is_compound()) VisitBinaryOperation(node->binary_operation()); + VisitReference(node->target()); + Visit(node->value()); + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitBinaryOperation(BinaryOperation* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(BinaryOperation::num_ids())); + Visit(node->left()); + Visit(node->right()); +} + + +void AstNumberingVisitor::VisitCompareOperation(CompareOperation* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(CompareOperation::num_ids())); + Visit(node->left()); + Visit(node->right()); +} + + +void AstNumberingVisitor::VisitSpread(Spread* node) { + IncrementNodeCount(); + DisableCrankshaft(kSpread); + Visit(node->expression()); +} + + +void AstNumberingVisitor::VisitEmptyParentheses(EmptyParentheses* node) { + UNREACHABLE(); +} + + +void AstNumberingVisitor::VisitForInStatement(ForInStatement* node) { + IncrementNodeCount(); + DisableSelfOptimization(); + node->set_base_id(ReserveIdRange(ForInStatement::num_ids())); + Visit(node->each()); + Visit(node->enumerable()); + Visit(node->body()); + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitForOfStatement(ForOfStatement* node) { + IncrementNodeCount(); + DisableCrankshaft(kForOfStatement); + node->set_base_id(ReserveIdRange(ForOfStatement::num_ids())); + Visit(node->assign_iterator()); + Visit(node->next_result()); + Visit(node->result_done()); + Visit(node->assign_each()); + Visit(node->body()); + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitConditional(Conditional* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(Conditional::num_ids())); + Visit(node->condition()); + Visit(node->then_expression()); + Visit(node->else_expression()); +} + + +void AstNumberingVisitor::VisitIfStatement(IfStatement* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(IfStatement::num_ids())); + Visit(node->condition()); + Visit(node->then_statement()); + if (node->HasElseStatement()) { + Visit(node->else_statement()); + } +} + + +void AstNumberingVisitor::VisitSwitchStatement(SwitchStatement* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(SwitchStatement::num_ids())); + Visit(node->tag()); + ZoneList<CaseClause*>* cases = node->cases(); + for (int i = 0; i < cases->length(); i++) { + VisitCaseClause(cases->at(i)); + } +} + + +void AstNumberingVisitor::VisitCaseClause(CaseClause* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(CaseClause::num_ids())); + if (!node->is_default()) Visit(node->label()); + VisitStatements(node->statements()); +} + + +void AstNumberingVisitor::VisitForStatement(ForStatement* node) { + IncrementNodeCount(); + DisableSelfOptimization(); + node->set_base_id(ReserveIdRange(ForStatement::num_ids())); + if (node->init() != NULL) Visit(node->init()); + if (node->cond() != NULL) Visit(node->cond()); + if (node->next() != NULL) Visit(node->next()); + Visit(node->body()); +} + + +void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) { + IncrementNodeCount(); + DisableCrankshaft(kClassLiteral); + node->set_base_id(ReserveIdRange(node->num_ids())); + if (node->extends()) Visit(node->extends()); + if (node->constructor()) Visit(node->constructor()); + if (node->class_variable_proxy()) { + VisitVariableProxy(node->class_variable_proxy()); + } + for (int i = 0; i < node->properties()->length(); i++) { + VisitObjectLiteralProperty(node->properties()->at(i)); + } + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitObjectLiteral(ObjectLiteral* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(node->num_ids())); + for (int i = 0; i < node->properties()->length(); i++) { + VisitObjectLiteralProperty(node->properties()->at(i)); + } + node->BuildConstantProperties(isolate_); + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code will be is emitted. + node->CalculateEmitStore(zone_); + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitObjectLiteralProperty( + ObjectLiteralProperty* node) { + if (node->is_computed_name()) DisableCrankshaft(kComputedPropertyName); + Visit(node->key()); + Visit(node->value()); +} + + +void AstNumberingVisitor::VisitArrayLiteral(ArrayLiteral* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(node->num_ids())); + for (int i = 0; i < node->values()->length(); i++) { + Visit(node->values()->at(i)); + } + node->BuildConstantElements(isolate_); + ReserveFeedbackSlots(node); +} + + +void AstNumberingVisitor::VisitCall(Call* node) { + IncrementNodeCount(); + ReserveFeedbackSlots(node); + node->set_base_id(ReserveIdRange(Call::num_ids())); + Visit(node->expression()); + VisitArguments(node->arguments()); +} + + +void AstNumberingVisitor::VisitCallNew(CallNew* node) { + IncrementNodeCount(); + ReserveFeedbackSlots(node); + node->set_base_id(ReserveIdRange(CallNew::num_ids())); + Visit(node->expression()); + VisitArguments(node->arguments()); +} + + +void AstNumberingVisitor::VisitStatements(ZoneList<Statement*>* statements) { + if (statements == NULL) return; + for (int i = 0; i < statements->length(); i++) { + Visit(statements->at(i)); + } +} + + +void AstNumberingVisitor::VisitDeclarations( + ZoneList<Declaration*>* declarations) { + for (int i = 0; i < declarations->length(); i++) { + Visit(declarations->at(i)); + } +} + + +void AstNumberingVisitor::VisitArguments(ZoneList<Expression*>* arguments) { + for (int i = 0; i < arguments->length(); i++) { + Visit(arguments->at(i)); + } +} + + +void AstNumberingVisitor::VisitFunctionLiteral(FunctionLiteral* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(FunctionLiteral::num_ids())); + // We don't recurse into the declarations or body of the function literal: + // you have to separately Renumber() each FunctionLiteral that you compile. +} + + +void AstNumberingVisitor::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* node) { + IncrementNodeCount(); + node->set_base_id(ReserveIdRange(RewritableAssignmentExpression::num_ids())); + Visit(node->expression()); +} + + +bool AstNumberingVisitor::Finish(FunctionLiteral* node) { + node->set_ast_properties(&properties_); + node->set_dont_optimize_reason(dont_optimize_reason()); + return !HasStackOverflow(); +} + + +bool AstNumberingVisitor::Renumber(FunctionLiteral* node) { + Scope* scope = node->scope(); + + if (scope->HasIllegalRedeclaration()) { + Visit(scope->GetIllegalRedeclaration()); + DisableOptimization(kFunctionWithIllegalRedeclaration); + return Finish(node); + } + if (scope->new_target_var()) DisableCrankshaft(kSuperReference); + if (scope->calls_eval()) DisableOptimization(kFunctionCallsEval); + if (scope->arguments() != NULL && !scope->arguments()->IsStackAllocated()) { + DisableCrankshaft(kContextAllocatedArguments); + } + + int rest_index; + if (scope->rest_parameter(&rest_index)) { + DisableCrankshaft(kRestParameter); + } + + VisitDeclarations(scope->declarations()); + VisitStatements(node->body()); + + return Finish(node); +} + + +bool AstNumbering::Renumber(Isolate* isolate, Zone* zone, + FunctionLiteral* function) { + AstNumberingVisitor visitor(isolate, zone); + return visitor.Renumber(function); +} +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/ast-numbering.h b/deps/v8/src/ast/ast-numbering.h new file mode 100644 index 0000000000..0ac1ef2134 --- /dev/null +++ b/deps/v8/src/ast/ast-numbering.h @@ -0,0 +1,25 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_AST_NUMBERING_H_ +#define V8_AST_AST_NUMBERING_H_ + +namespace v8 { +namespace internal { + +// Forward declarations. +class FunctionLiteral; +class Isolate; +class Zone; + +namespace AstNumbering { +// Assign type feedback IDs and bailout IDs to an AST node tree. +// +bool Renumber(Isolate* isolate, Zone* zone, FunctionLiteral* function); +} + +} // namespace internal +} // namespace v8 + +#endif // V8_AST_AST_NUMBERING_H_ diff --git a/deps/v8/src/ast/ast-value-factory.cc b/deps/v8/src/ast/ast-value-factory.cc new file mode 100644 index 0000000000..2e17fbcfaf --- /dev/null +++ b/deps/v8/src/ast/ast-value-factory.cc @@ -0,0 +1,411 @@ +// Copyright 2014 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 "src/ast/ast-value-factory.h" + +#include "src/api.h" +#include "src/objects.h" +#include "src/utils.h" + +namespace v8 { +namespace internal { + +namespace { + +// For using StringToArrayIndex. +class OneByteStringStream { + public: + explicit OneByteStringStream(Vector<const byte> lb) : + literal_bytes_(lb), pos_(0) {} + + bool HasMore() { return pos_ < literal_bytes_.length(); } + uint16_t GetNext() { return literal_bytes_[pos_++]; } + + private: + Vector<const byte> literal_bytes_; + int pos_; +}; + +} // namespace + +class AstRawStringInternalizationKey : public HashTableKey { + public: + explicit AstRawStringInternalizationKey(const AstRawString* string) + : string_(string) {} + + bool IsMatch(Object* other) override { + if (string_->is_one_byte_) + return String::cast(other)->IsOneByteEqualTo(string_->literal_bytes_); + return String::cast(other)->IsTwoByteEqualTo( + Vector<const uint16_t>::cast(string_->literal_bytes_)); + } + + uint32_t Hash() override { return string_->hash() >> Name::kHashShift; } + + uint32_t HashForObject(Object* key) override { + return String::cast(key)->Hash(); + } + + Handle<Object> AsHandle(Isolate* isolate) override { + if (string_->is_one_byte_) + return isolate->factory()->NewOneByteInternalizedString( + string_->literal_bytes_, string_->hash()); + return isolate->factory()->NewTwoByteInternalizedString( + Vector<const uint16_t>::cast(string_->literal_bytes_), string_->hash()); + } + + private: + const AstRawString* string_; +}; + + +void AstRawString::Internalize(Isolate* isolate) { + if (!string_.is_null()) return; + if (literal_bytes_.length() == 0) { + string_ = isolate->factory()->empty_string(); + } else { + AstRawStringInternalizationKey key(this); + string_ = StringTable::LookupKey(isolate, &key); + } +} + + +bool AstRawString::AsArrayIndex(uint32_t* index) const { + if (!string_.is_null()) + return string_->AsArrayIndex(index); + if (!is_one_byte_ || literal_bytes_.length() == 0 || + literal_bytes_.length() > String::kMaxArrayIndexSize) + return false; + OneByteStringStream stream(literal_bytes_); + return StringToArrayIndex(&stream, index); +} + + +bool AstRawString::IsOneByteEqualTo(const char* data) const { + int length = static_cast<int>(strlen(data)); + if (is_one_byte_ && literal_bytes_.length() == length) { + const char* token = reinterpret_cast<const char*>(literal_bytes_.start()); + return !strncmp(token, data, length); + } + return false; +} + + +void AstConsString::Internalize(Isolate* isolate) { + // AstRawStrings are internalized before AstConsStrings so left and right are + // already internalized. + string_ = isolate->factory() + ->NewConsString(left_->string(), right_->string()) + .ToHandleChecked(); +} + + +bool AstValue::IsPropertyName() const { + if (type_ == STRING) { + uint32_t index; + return !string_->AsArrayIndex(&index); + } + return false; +} + + +bool AstValue::BooleanValue() const { + switch (type_) { + case STRING: + DCHECK(string_ != NULL); + return !string_->IsEmpty(); + case SYMBOL: + UNREACHABLE(); + break; + case NUMBER_WITH_DOT: + case NUMBER: + return DoubleToBoolean(number_); + case SMI: + return smi_ != 0; + case BOOLEAN: + return bool_; + case NULL_TYPE: + return false; + case THE_HOLE: + UNREACHABLE(); + break; + case UNDEFINED: + return false; + } + UNREACHABLE(); + return false; +} + + +void AstValue::Internalize(Isolate* isolate) { + switch (type_) { + case STRING: + DCHECK(string_ != NULL); + // Strings are already internalized. + DCHECK(!string_->string().is_null()); + break; + case SYMBOL: + if (symbol_name_[0] == 'i') { + DCHECK_EQ(0, strcmp(symbol_name_, "iterator_symbol")); + value_ = isolate->factory()->iterator_symbol(); + } else { + DCHECK_EQ(0, strcmp(symbol_name_, "home_object_symbol")); + value_ = isolate->factory()->home_object_symbol(); + } + break; + case NUMBER_WITH_DOT: + case NUMBER: + value_ = isolate->factory()->NewNumber(number_, TENURED); + break; + case SMI: + value_ = handle(Smi::FromInt(smi_), isolate); + break; + case BOOLEAN: + if (bool_) { + value_ = isolate->factory()->true_value(); + } else { + value_ = isolate->factory()->false_value(); + } + break; + case NULL_TYPE: + value_ = isolate->factory()->null_value(); + break; + case THE_HOLE: + value_ = isolate->factory()->the_hole_value(); + break; + case UNDEFINED: + value_ = isolate->factory()->undefined_value(); + break; + } +} + + +AstRawString* AstValueFactory::GetOneByteStringInternal( + Vector<const uint8_t> literal) { + uint32_t hash = StringHasher::HashSequentialString<uint8_t>( + literal.start(), literal.length(), hash_seed_); + return GetString(hash, true, literal); +} + + +AstRawString* AstValueFactory::GetTwoByteStringInternal( + Vector<const uint16_t> literal) { + uint32_t hash = StringHasher::HashSequentialString<uint16_t>( + literal.start(), literal.length(), hash_seed_); + return GetString(hash, false, Vector<const byte>::cast(literal)); +} + + +const AstRawString* AstValueFactory::GetString(Handle<String> literal) { + // For the FlatContent to stay valid, we shouldn't do any heap + // allocation. Make sure we won't try to internalize the string in GetString. + AstRawString* result = NULL; + Isolate* saved_isolate = isolate_; + isolate_ = NULL; + { + DisallowHeapAllocation no_gc; + String::FlatContent content = literal->GetFlatContent(); + if (content.IsOneByte()) { + result = GetOneByteStringInternal(content.ToOneByteVector()); + } else { + DCHECK(content.IsTwoByte()); + result = GetTwoByteStringInternal(content.ToUC16Vector()); + } + } + isolate_ = saved_isolate; + if (isolate_) result->Internalize(isolate_); + return result; +} + + +const AstConsString* AstValueFactory::NewConsString( + const AstString* left, const AstString* right) { + // This Vector will be valid as long as the Collector is alive (meaning that + // the AstRawString will not be moved). + AstConsString* new_string = new (zone_) AstConsString(left, right); + strings_.Add(new_string); + if (isolate_) { + new_string->Internalize(isolate_); + } + return new_string; +} + + +void AstValueFactory::Internalize(Isolate* isolate) { + if (isolate_) { + // Everything is already internalized. + return; + } + // Strings need to be internalized before values, because values refer to + // strings. + for (int i = 0; i < strings_.length(); ++i) { + strings_[i]->Internalize(isolate); + } + for (int i = 0; i < values_.length(); ++i) { + values_[i]->Internalize(isolate); + } + isolate_ = isolate; +} + + +const AstValue* AstValueFactory::NewString(const AstRawString* string) { + AstValue* value = new (zone_) AstValue(string); + DCHECK(string != NULL); + if (isolate_) { + value->Internalize(isolate_); + } + values_.Add(value); + return value; +} + + +const AstValue* AstValueFactory::NewSymbol(const char* name) { + AstValue* value = new (zone_) AstValue(name); + if (isolate_) { + value->Internalize(isolate_); + } + values_.Add(value); + return value; +} + + +const AstValue* AstValueFactory::NewNumber(double number, bool with_dot) { + AstValue* value = new (zone_) AstValue(number, with_dot); + if (isolate_) { + value->Internalize(isolate_); + } + values_.Add(value); + return value; +} + + +const AstValue* AstValueFactory::NewSmi(int number) { + AstValue* value = + new (zone_) AstValue(AstValue::SMI, number); + if (isolate_) { + value->Internalize(isolate_); + } + values_.Add(value); + return value; +} + + +#define GENERATE_VALUE_GETTER(value, initializer) \ + if (!value) { \ + value = new (zone_) AstValue(initializer); \ + if (isolate_) { \ + value->Internalize(isolate_); \ + } \ + values_.Add(value); \ + } \ + return value; + + +const AstValue* AstValueFactory::NewBoolean(bool b) { + if (b) { + GENERATE_VALUE_GETTER(true_value_, true); + } else { + GENERATE_VALUE_GETTER(false_value_, false); + } +} + + +const AstValue* AstValueFactory::NewNull() { + GENERATE_VALUE_GETTER(null_value_, AstValue::NULL_TYPE); +} + + +const AstValue* AstValueFactory::NewUndefined() { + GENERATE_VALUE_GETTER(undefined_value_, AstValue::UNDEFINED); +} + + +const AstValue* AstValueFactory::NewTheHole() { + GENERATE_VALUE_GETTER(the_hole_value_, AstValue::THE_HOLE); +} + + +#undef GENERATE_VALUE_GETTER + +AstRawString* AstValueFactory::GetString(uint32_t hash, bool is_one_byte, + Vector<const byte> literal_bytes) { + // literal_bytes here points to whatever the user passed, and this is OK + // because we use vector_compare (which checks the contents) to compare + // against the AstRawStrings which are in the string_table_. We should not + // return this AstRawString. + AstRawString key(is_one_byte, literal_bytes, hash); + HashMap::Entry* entry = string_table_.LookupOrInsert(&key, hash); + if (entry->value == NULL) { + // Copy literal contents for later comparison. + int length = literal_bytes.length(); + byte* new_literal_bytes = zone_->NewArray<byte>(length); + memcpy(new_literal_bytes, literal_bytes.start(), length); + AstRawString* new_string = new (zone_) AstRawString( + is_one_byte, Vector<const byte>(new_literal_bytes, length), hash); + entry->key = new_string; + strings_.Add(new_string); + if (isolate_) { + new_string->Internalize(isolate_); + } + entry->value = reinterpret_cast<void*>(1); + } + return reinterpret_cast<AstRawString*>(entry->key); +} + + +bool AstValueFactory::AstRawStringCompare(void* a, void* b) { + const AstRawString* lhs = static_cast<AstRawString*>(a); + const AstRawString* rhs = static_cast<AstRawString*>(b); + if (lhs->length() != rhs->length()) return false; + if (lhs->hash() != rhs->hash()) return false; + const unsigned char* l = lhs->raw_data(); + const unsigned char* r = rhs->raw_data(); + size_t length = rhs->length(); + if (lhs->is_one_byte()) { + if (rhs->is_one_byte()) { + return CompareCharsUnsigned(reinterpret_cast<const uint8_t*>(l), + reinterpret_cast<const uint8_t*>(r), + length) == 0; + } else { + return CompareCharsUnsigned(reinterpret_cast<const uint8_t*>(l), + reinterpret_cast<const uint16_t*>(r), + length) == 0; + } + } else { + if (rhs->is_one_byte()) { + return CompareCharsUnsigned(reinterpret_cast<const uint16_t*>(l), + reinterpret_cast<const uint8_t*>(r), + length) == 0; + } else { + return CompareCharsUnsigned(reinterpret_cast<const uint16_t*>(l), + reinterpret_cast<const uint16_t*>(r), + length) == 0; + } + } +} +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/ast-value-factory.h b/deps/v8/src/ast/ast-value-factory.h new file mode 100644 index 0000000000..4ae912ea82 --- /dev/null +++ b/deps/v8/src/ast/ast-value-factory.h @@ -0,0 +1,375 @@ +// Copyright 2014 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. + +#ifndef V8_AST_AST_VALUE_FACTORY_H_ +#define V8_AST_AST_VALUE_FACTORY_H_ + +#include "src/api.h" +#include "src/hashmap.h" +#include "src/utils.h" + +// AstString, AstValue and AstValueFactory are for storing strings and values +// independent of the V8 heap and internalizing them later. During parsing, +// AstStrings and AstValues are created and stored outside the heap, in +// AstValueFactory. After parsing, the strings and values are internalized +// (moved into the V8 heap). +namespace v8 { +namespace internal { + +class AstString : public ZoneObject { + public: + virtual ~AstString() {} + + virtual int length() const = 0; + bool IsEmpty() const { return length() == 0; } + + // Puts the string into the V8 heap. + virtual void Internalize(Isolate* isolate) = 0; + + // This function can be called after internalizing. + V8_INLINE Handle<String> string() const { + DCHECK(!string_.is_null()); + return string_; + } + + protected: + // This is null until the string is internalized. + Handle<String> string_; +}; + + +class AstRawString final : public AstString { + public: + int length() const override { + if (is_one_byte_) + return literal_bytes_.length(); + return literal_bytes_.length() / 2; + } + + int byte_length() const { return literal_bytes_.length(); } + + void Internalize(Isolate* isolate) override; + + bool AsArrayIndex(uint32_t* index) const; + + // The string is not null-terminated, use length() to find out the length. + const unsigned char* raw_data() const { + return literal_bytes_.start(); + } + bool is_one_byte() const { return is_one_byte_; } + bool IsOneByteEqualTo(const char* data) const; + uint16_t FirstCharacter() const { + if (is_one_byte_) + return literal_bytes_[0]; + const uint16_t* c = + reinterpret_cast<const uint16_t*>(literal_bytes_.start()); + return *c; + } + + // For storing AstRawStrings in a hash map. + uint32_t hash() const { + return hash_; + } + + private: + friend class AstValueFactory; + friend class AstRawStringInternalizationKey; + + AstRawString(bool is_one_byte, const Vector<const byte>& literal_bytes, + uint32_t hash) + : is_one_byte_(is_one_byte), literal_bytes_(literal_bytes), hash_(hash) {} + + AstRawString() + : is_one_byte_(true), + hash_(0) {} + + bool is_one_byte_; + + // Points to memory owned by Zone. + Vector<const byte> literal_bytes_; + uint32_t hash_; +}; + + +class AstConsString final : public AstString { + public: + AstConsString(const AstString* left, const AstString* right) + : length_(left->length() + right->length()), left_(left), right_(right) {} + + int length() const override { return length_; } + + void Internalize(Isolate* isolate) override; + + private: + const int length_; + const AstString* left_; + const AstString* right_; +}; + + +// AstValue is either a string, a number, a string array, a boolean, or a +// special value (null, undefined, the hole). +class AstValue : public ZoneObject { + public: + bool IsString() const { + return type_ == STRING; + } + + bool IsNumber() const { + return type_ == NUMBER || type_ == NUMBER_WITH_DOT || type_ == SMI; + } + + bool ContainsDot() const { return type_ == NUMBER_WITH_DOT; } + + const AstRawString* AsString() const { + if (type_ == STRING) + return string_; + UNREACHABLE(); + return 0; + } + + double AsNumber() const { + if (type_ == NUMBER || type_ == NUMBER_WITH_DOT) + return number_; + if (type_ == SMI) + return smi_; + UNREACHABLE(); + return 0; + } + + bool EqualsString(const AstRawString* string) const { + return type_ == STRING && string_ == string; + } + + bool IsPropertyName() const; + + bool BooleanValue() const; + + bool IsTheHole() const { return type_ == THE_HOLE; } + + void Internalize(Isolate* isolate); + + // Can be called after Internalize has been called. + V8_INLINE Handle<Object> value() const { + if (type_ == STRING) { + return string_->string(); + } + DCHECK(!value_.is_null()); + return value_; + } + + private: + friend class AstValueFactory; + + enum Type { + STRING, + SYMBOL, + NUMBER, + NUMBER_WITH_DOT, + SMI, + BOOLEAN, + NULL_TYPE, + UNDEFINED, + THE_HOLE + }; + + explicit AstValue(const AstRawString* s) : type_(STRING) { string_ = s; } + + explicit AstValue(const char* name) : type_(SYMBOL) { symbol_name_ = name; } + + explicit AstValue(double n, bool with_dot) { + if (with_dot) { + type_ = NUMBER_WITH_DOT; + } else { + type_ = NUMBER; + } + number_ = n; + } + + AstValue(Type t, int i) : type_(t) { + DCHECK(type_ == SMI); + smi_ = i; + } + + explicit AstValue(bool b) : type_(BOOLEAN) { bool_ = b; } + + explicit AstValue(Type t) : type_(t) { + DCHECK(t == NULL_TYPE || t == UNDEFINED || t == THE_HOLE); + } + + Type type_; + + // Uninternalized value. + union { + const AstRawString* string_; + double number_; + int smi_; + bool bool_; + ZoneList<const AstRawString*>* strings_; + const char* symbol_name_; + }; + + // Internalized value (empty before internalized). + Handle<Object> value_; +}; + + +// For generating constants. +#define STRING_CONSTANTS(F) \ + F(anonymous_function, "(anonymous function)") \ + F(arguments, "arguments") \ + F(constructor, "constructor") \ + F(default, "default") \ + F(done, "done") \ + F(dot, ".") \ + F(dot_for, ".for") \ + F(dot_generator, ".generator") \ + F(dot_generator_object, ".generator_object") \ + F(dot_iterator, ".iterator") \ + F(dot_result, ".result") \ + F(dot_switch_tag, ".switch_tag") \ + F(dot_catch, ".catch") \ + F(empty, "") \ + F(eval, "eval") \ + F(get_space, "get ") \ + F(let, "let") \ + F(native, "native") \ + F(new_target, ".new.target") \ + F(next, "next") \ + F(proto, "__proto__") \ + F(prototype, "prototype") \ + F(rest_parameter, ".rest_parameter") \ + F(set_space, "set ") \ + F(this, "this") \ + F(this_function, ".this_function") \ + F(undefined, "undefined") \ + F(use_asm, "use asm") \ + F(use_strong, "use strong") \ + F(use_strict, "use strict") \ + F(value, "value") + +#define OTHER_CONSTANTS(F) \ + F(true_value) \ + F(false_value) \ + F(null_value) \ + F(undefined_value) \ + F(the_hole_value) + +class AstValueFactory { + public: + AstValueFactory(Zone* zone, uint32_t hash_seed) + : string_table_(AstRawStringCompare), + zone_(zone), + isolate_(NULL), + hash_seed_(hash_seed) { +#define F(name, str) name##_string_ = NULL; + STRING_CONSTANTS(F) +#undef F +#define F(name) name##_ = NULL; + OTHER_CONSTANTS(F) +#undef F + } + + Zone* zone() const { return zone_; } + + const AstRawString* GetOneByteString(Vector<const uint8_t> literal) { + return GetOneByteStringInternal(literal); + } + const AstRawString* GetOneByteString(const char* string) { + return GetOneByteString(Vector<const uint8_t>( + reinterpret_cast<const uint8_t*>(string), StrLength(string))); + } + const AstRawString* GetTwoByteString(Vector<const uint16_t> literal) { + return GetTwoByteStringInternal(literal); + } + const AstRawString* GetString(Handle<String> literal); + const AstConsString* NewConsString(const AstString* left, + const AstString* right); + + void Internalize(Isolate* isolate); + bool IsInternalized() { + return isolate_ != NULL; + } + +#define F(name, str) \ + const AstRawString* name##_string() { \ + if (name##_string_ == NULL) { \ + const char* data = str; \ + name##_string_ = GetOneByteString( \ + Vector<const uint8_t>(reinterpret_cast<const uint8_t*>(data), \ + static_cast<int>(strlen(data)))); \ + } \ + return name##_string_; \ + } + STRING_CONSTANTS(F) +#undef F + + const AstValue* NewString(const AstRawString* string); + // A JavaScript symbol (ECMA-262 edition 6). + const AstValue* NewSymbol(const char* name); + const AstValue* NewNumber(double number, bool with_dot = false); + const AstValue* NewSmi(int number); + const AstValue* NewBoolean(bool b); + const AstValue* NewStringList(ZoneList<const AstRawString*>* strings); + const AstValue* NewNull(); + const AstValue* NewUndefined(); + const AstValue* NewTheHole(); + + private: + AstRawString* GetOneByteStringInternal(Vector<const uint8_t> literal); + AstRawString* GetTwoByteStringInternal(Vector<const uint16_t> literal); + AstRawString* GetString(uint32_t hash, bool is_one_byte, + Vector<const byte> literal_bytes); + + static bool AstRawStringCompare(void* a, void* b); + + // All strings are copied here, one after another (no NULLs inbetween). + HashMap string_table_; + // For keeping track of all AstValues and AstRawStrings we've created (so that + // they can be internalized later). + List<AstValue*> values_; + List<AstString*> strings_; + Zone* zone_; + Isolate* isolate_; + + uint32_t hash_seed_; + +#define F(name, str) const AstRawString* name##_string_; + STRING_CONSTANTS(F) +#undef F + +#define F(name) AstValue* name##_; + OTHER_CONSTANTS(F) +#undef F +}; +} // namespace internal +} // namespace v8 + +#undef STRING_CONSTANTS +#undef OTHER_CONSTANTS + +#endif // V8_AST_AST_VALUE_FACTORY_H_ diff --git a/deps/v8/src/ast/ast.cc b/deps/v8/src/ast/ast.cc new file mode 100644 index 0000000000..69e7351a7d --- /dev/null +++ b/deps/v8/src/ast/ast.cc @@ -0,0 +1,826 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/ast.h" + +#include <cmath> // For isfinite. +#include "src/ast/scopes.h" +#include "src/builtins.h" +#include "src/code-stubs.h" +#include "src/contexts.h" +#include "src/conversions.h" +#include "src/hashmap.h" +#include "src/parsing/parser.h" +#include "src/property.h" +#include "src/property-details.h" +#include "src/string-stream.h" +#include "src/type-info.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// All the Accept member functions for each syntax tree node type. + +#define DECL_ACCEPT(type) \ + void type::Accept(AstVisitor* v) { v->Visit##type(this); } +AST_NODE_LIST(DECL_ACCEPT) +#undef DECL_ACCEPT + + +// ---------------------------------------------------------------------------- +// Implementation of other node functionality. + + +bool Expression::IsSmiLiteral() const { + return IsLiteral() && AsLiteral()->value()->IsSmi(); +} + + +bool Expression::IsStringLiteral() const { + return IsLiteral() && AsLiteral()->value()->IsString(); +} + + +bool Expression::IsNullLiteral() const { + return IsLiteral() && AsLiteral()->value()->IsNull(); +} + + +bool Expression::IsUndefinedLiteral(Isolate* isolate) const { + const VariableProxy* var_proxy = AsVariableProxy(); + if (var_proxy == NULL) return false; + Variable* var = var_proxy->var(); + // The global identifier "undefined" is immutable. Everything + // else could be reassigned. + return var != NULL && var->IsUnallocatedOrGlobalSlot() && + var_proxy->raw_name()->IsOneByteEqualTo("undefined"); +} + + +bool Expression::IsValidReferenceExpressionOrThis() const { + return IsValidReferenceExpression() || + (IsVariableProxy() && AsVariableProxy()->is_this()); +} + + +VariableProxy::VariableProxy(Zone* zone, Variable* var, int start_position, + int end_position) + : Expression(zone, start_position), + bit_field_(IsThisField::encode(var->is_this()) | + IsAssignedField::encode(false) | + IsResolvedField::encode(false)), + raw_name_(var->raw_name()), + end_position_(end_position) { + BindTo(var); +} + + +VariableProxy::VariableProxy(Zone* zone, const AstRawString* name, + Variable::Kind variable_kind, int start_position, + int end_position) + : Expression(zone, start_position), + bit_field_(IsThisField::encode(variable_kind == Variable::THIS) | + IsAssignedField::encode(false) | + IsResolvedField::encode(false)), + raw_name_(name), + end_position_(end_position) {} + + +void VariableProxy::BindTo(Variable* var) { + DCHECK((is_this() && var->is_this()) || raw_name() == var->raw_name()); + set_var(var); + set_is_resolved(); + var->set_is_used(); +} + + +void VariableProxy::AssignFeedbackVectorSlots(Isolate* isolate, + FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) { + if (UsesVariableFeedbackSlot()) { + // VariableProxies that point to the same Variable within a function can + // make their loads from the same IC slot. + if (var()->IsUnallocated()) { + ZoneHashMap::Entry* entry = cache->Get(var()); + if (entry != NULL) { + variable_feedback_slot_ = FeedbackVectorSlot( + static_cast<int>(reinterpret_cast<intptr_t>(entry->value))); + return; + } + } + variable_feedback_slot_ = spec->AddLoadICSlot(); + if (var()->IsUnallocated()) { + cache->Put(var(), variable_feedback_slot_); + } + } +} + + +static void AssignVectorSlots(Expression* expr, FeedbackVectorSpec* spec, + FeedbackVectorSlot* out_slot) { + Property* property = expr->AsProperty(); + LhsKind assign_type = Property::GetAssignType(property); + if ((assign_type == VARIABLE && + expr->AsVariableProxy()->var()->IsUnallocated()) || + assign_type == NAMED_PROPERTY || assign_type == KEYED_PROPERTY) { + // TODO(ishell): consider using ICSlotCache for variables here. + FeedbackVectorSlotKind kind = assign_type == KEYED_PROPERTY + ? FeedbackVectorSlotKind::KEYED_STORE_IC + : FeedbackVectorSlotKind::STORE_IC; + *out_slot = spec->AddSlot(kind); + } +} + + +void ForEachStatement::AssignFeedbackVectorSlots( + Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) { + // TODO(adamk): for-of statements do not make use of this feedback slot. + // The each_slot_ should be specific to ForInStatement, and this work moved + // there. + if (IsForOfStatement()) return; + AssignVectorSlots(each(), spec, &each_slot_); +} + + +Assignment::Assignment(Zone* zone, Token::Value op, Expression* target, + Expression* value, int pos) + : Expression(zone, pos), + bit_field_( + IsUninitializedField::encode(false) | KeyTypeField::encode(ELEMENT) | + StoreModeField::encode(STANDARD_STORE) | TokenField::encode(op)), + target_(target), + value_(value), + binary_operation_(NULL) {} + + +void Assignment::AssignFeedbackVectorSlots(Isolate* isolate, + FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) { + AssignVectorSlots(target(), spec, &slot_); +} + + +void CountOperation::AssignFeedbackVectorSlots(Isolate* isolate, + FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) { + AssignVectorSlots(expression(), spec, &slot_); +} + + +Token::Value Assignment::binary_op() const { + switch (op()) { + case Token::ASSIGN_BIT_OR: return Token::BIT_OR; + case Token::ASSIGN_BIT_XOR: return Token::BIT_XOR; + case Token::ASSIGN_BIT_AND: return Token::BIT_AND; + case Token::ASSIGN_SHL: return Token::SHL; + case Token::ASSIGN_SAR: return Token::SAR; + case Token::ASSIGN_SHR: return Token::SHR; + case Token::ASSIGN_ADD: return Token::ADD; + case Token::ASSIGN_SUB: return Token::SUB; + case Token::ASSIGN_MUL: return Token::MUL; + case Token::ASSIGN_DIV: return Token::DIV; + case Token::ASSIGN_MOD: return Token::MOD; + default: UNREACHABLE(); + } + return Token::ILLEGAL; +} + + +bool FunctionLiteral::AllowsLazyCompilation() { + return scope()->AllowsLazyCompilation(); +} + + +bool FunctionLiteral::AllowsLazyCompilationWithoutContext() { + return scope()->AllowsLazyCompilationWithoutContext(); +} + + +int FunctionLiteral::start_position() const { + return scope()->start_position(); +} + + +int FunctionLiteral::end_position() const { + return scope()->end_position(); +} + + +LanguageMode FunctionLiteral::language_mode() const { + return scope()->language_mode(); +} + + +bool FunctionLiteral::NeedsHomeObject(Expression* expr) { + if (expr == nullptr || !expr->IsFunctionLiteral()) return false; + DCHECK_NOT_NULL(expr->AsFunctionLiteral()->scope()); + return expr->AsFunctionLiteral()->scope()->NeedsHomeObject(); +} + + +ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value, + Kind kind, bool is_static, + bool is_computed_name) + : key_(key), + value_(value), + kind_(kind), + emit_store_(true), + is_static_(is_static), + is_computed_name_(is_computed_name) {} + + +ObjectLiteralProperty::ObjectLiteralProperty(AstValueFactory* ast_value_factory, + Expression* key, Expression* value, + bool is_static, + bool is_computed_name) + : key_(key), + value_(value), + emit_store_(true), + is_static_(is_static), + is_computed_name_(is_computed_name) { + if (!is_computed_name && + key->AsLiteral()->raw_value()->EqualsString( + ast_value_factory->proto_string())) { + kind_ = PROTOTYPE; + } else if (value_->AsMaterializedLiteral() != NULL) { + kind_ = MATERIALIZED_LITERAL; + } else if (value_->IsLiteral()) { + kind_ = CONSTANT; + } else { + kind_ = COMPUTED; + } +} + + +void ClassLiteral::AssignFeedbackVectorSlots(Isolate* isolate, + FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) { + // This logic that computes the number of slots needed for vector store + // ICs must mirror FullCodeGenerator::VisitClassLiteral. + if (NeedsProxySlot()) { + slot_ = spec->AddStoreICSlot(); + } + + for (int i = 0; i < properties()->length(); i++) { + ObjectLiteral::Property* property = properties()->at(i); + Expression* value = property->value(); + if (FunctionLiteral::NeedsHomeObject(value)) { + property->SetSlot(spec->AddStoreICSlot()); + } + } +} + + +bool ObjectLiteral::Property::IsCompileTimeValue() { + return kind_ == CONSTANT || + (kind_ == MATERIALIZED_LITERAL && + CompileTimeValue::IsCompileTimeValue(value_)); +} + + +void ObjectLiteral::Property::set_emit_store(bool emit_store) { + emit_store_ = emit_store; +} + + +bool ObjectLiteral::Property::emit_store() { + return emit_store_; +} + + +void ObjectLiteral::AssignFeedbackVectorSlots(Isolate* isolate, + FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) { + // This logic that computes the number of slots needed for vector store + // ics must mirror FullCodeGenerator::VisitObjectLiteral. + int property_index = 0; + for (; property_index < properties()->length(); property_index++) { + ObjectLiteral::Property* property = properties()->at(property_index); + if (property->is_computed_name()) break; + if (property->IsCompileTimeValue()) continue; + + Literal* key = property->key()->AsLiteral(); + Expression* value = property->value(); + switch (property->kind()) { + case ObjectLiteral::Property::CONSTANT: + UNREACHABLE(); + case ObjectLiteral::Property::MATERIALIZED_LITERAL: + // Fall through. + case ObjectLiteral::Property::COMPUTED: + // It is safe to use [[Put]] here because the boilerplate already + // contains computed properties with an uninitialized value. + if (key->value()->IsInternalizedString()) { + if (property->emit_store()) { + property->SetSlot(spec->AddStoreICSlot()); + if (FunctionLiteral::NeedsHomeObject(value)) { + property->SetSlot(spec->AddStoreICSlot(), 1); + } + } + break; + } + if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) { + property->SetSlot(spec->AddStoreICSlot()); + } + break; + case ObjectLiteral::Property::PROTOTYPE: + break; + case ObjectLiteral::Property::GETTER: + if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) { + property->SetSlot(spec->AddStoreICSlot()); + } + break; + case ObjectLiteral::Property::SETTER: + if (property->emit_store() && FunctionLiteral::NeedsHomeObject(value)) { + property->SetSlot(spec->AddStoreICSlot()); + } + break; + } + } + + for (; property_index < properties()->length(); property_index++) { + ObjectLiteral::Property* property = properties()->at(property_index); + + Expression* value = property->value(); + if (property->kind() != ObjectLiteral::Property::PROTOTYPE) { + if (FunctionLiteral::NeedsHomeObject(value)) { + property->SetSlot(spec->AddStoreICSlot()); + } + } + } +} + + +void ObjectLiteral::CalculateEmitStore(Zone* zone) { + const auto GETTER = ObjectLiteral::Property::GETTER; + const auto SETTER = ObjectLiteral::Property::SETTER; + + ZoneAllocationPolicy allocator(zone); + + ZoneHashMap table(Literal::Match, ZoneHashMap::kDefaultHashMapCapacity, + allocator); + for (int i = properties()->length() - 1; i >= 0; i--) { + ObjectLiteral::Property* property = properties()->at(i); + if (property->is_computed_name()) continue; + if (property->kind() == ObjectLiteral::Property::PROTOTYPE) continue; + Literal* literal = property->key()->AsLiteral(); + DCHECK(!literal->value()->IsNull()); + + // If there is an existing entry do not emit a store unless the previous + // entry was also an accessor. + uint32_t hash = literal->Hash(); + ZoneHashMap::Entry* entry = table.LookupOrInsert(literal, hash, allocator); + if (entry->value != NULL) { + auto previous_kind = + static_cast<ObjectLiteral::Property*>(entry->value)->kind(); + if (!((property->kind() == GETTER && previous_kind == SETTER) || + (property->kind() == SETTER && previous_kind == GETTER))) { + property->set_emit_store(false); + } + } + entry->value = property; + } +} + + +bool ObjectLiteral::IsBoilerplateProperty(ObjectLiteral::Property* property) { + return property != NULL && + property->kind() != ObjectLiteral::Property::PROTOTYPE; +} + + +void ObjectLiteral::BuildConstantProperties(Isolate* isolate) { + if (!constant_properties_.is_null()) return; + + // Allocate a fixed array to hold all the constant properties. + Handle<FixedArray> constant_properties = isolate->factory()->NewFixedArray( + boilerplate_properties_ * 2, TENURED); + + int position = 0; + // Accumulate the value in local variables and store it at the end. + bool is_simple = true; + int depth_acc = 1; + uint32_t max_element_index = 0; + uint32_t elements = 0; + for (int i = 0; i < properties()->length(); i++) { + ObjectLiteral::Property* property = properties()->at(i); + if (!IsBoilerplateProperty(property)) { + is_simple = false; + continue; + } + + if (position == boilerplate_properties_ * 2) { + DCHECK(property->is_computed_name()); + is_simple = false; + break; + } + DCHECK(!property->is_computed_name()); + + MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral(); + if (m_literal != NULL) { + m_literal->BuildConstants(isolate); + if (m_literal->depth() >= depth_acc) depth_acc = m_literal->depth() + 1; + } + + // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined + // value for COMPUTED properties, the real value is filled in at + // runtime. The enumeration order is maintained. + Handle<Object> key = property->key()->AsLiteral()->value(); + Handle<Object> value = GetBoilerplateValue(property->value(), isolate); + + // Ensure objects that may, at any point in time, contain fields with double + // representation are always treated as nested objects. This is true for + // computed fields (value is undefined), and smi and double literals + // (value->IsNumber()). + // TODO(verwaest): Remove once we can store them inline. + if (FLAG_track_double_fields && + (value->IsNumber() || value->IsUninitialized())) { + may_store_doubles_ = true; + } + + is_simple = is_simple && !value->IsUninitialized(); + + // Keep track of the number of elements in the object literal and + // the largest element index. If the largest element index is + // much larger than the number of elements, creating an object + // literal with fast elements will be a waste of space. + uint32_t element_index = 0; + if (key->IsString() + && Handle<String>::cast(key)->AsArrayIndex(&element_index) + && element_index > max_element_index) { + max_element_index = element_index; + elements++; + } else if (key->IsSmi()) { + int key_value = Smi::cast(*key)->value(); + if (key_value > 0 + && static_cast<uint32_t>(key_value) > max_element_index) { + max_element_index = key_value; + } + elements++; + } + + // Add name, value pair to the fixed array. + constant_properties->set(position++, *key); + constant_properties->set(position++, *value); + } + + constant_properties_ = constant_properties; + fast_elements_ = + (max_element_index <= 32) || ((2 * elements) >= max_element_index); + has_elements_ = elements > 0; + set_is_simple(is_simple); + set_depth(depth_acc); +} + + +void ArrayLiteral::BuildConstantElements(Isolate* isolate) { + if (!constant_elements_.is_null()) return; + + int constants_length = + first_spread_index_ >= 0 ? first_spread_index_ : values()->length(); + + // Allocate a fixed array to hold all the object literals. + Handle<JSArray> array = isolate->factory()->NewJSArray( + FAST_HOLEY_SMI_ELEMENTS, constants_length, constants_length, + Strength::WEAK, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); + + // Fill in the literals. + bool is_simple = (first_spread_index_ < 0); + int depth_acc = 1; + bool is_holey = false; + int array_index = 0; + for (; array_index < constants_length; array_index++) { + Expression* element = values()->at(array_index); + DCHECK(!element->IsSpread()); + MaterializedLiteral* m_literal = element->AsMaterializedLiteral(); + if (m_literal != NULL) { + m_literal->BuildConstants(isolate); + if (m_literal->depth() + 1 > depth_acc) { + depth_acc = m_literal->depth() + 1; + } + } + + // New handle scope here, needs to be after BuildContants(). + HandleScope scope(isolate); + Handle<Object> boilerplate_value = GetBoilerplateValue(element, isolate); + if (boilerplate_value->IsTheHole()) { + is_holey = true; + continue; + } + + if (boilerplate_value->IsUninitialized()) { + boilerplate_value = handle(Smi::FromInt(0), isolate); + is_simple = false; + } + + JSObject::AddDataElement(array, array_index, boilerplate_value, NONE) + .Assert(); + } + + JSObject::ValidateElements(array); + Handle<FixedArrayBase> element_values(array->elements()); + + // Simple and shallow arrays can be lazily copied, we transform the + // elements array to a copy-on-write array. + if (is_simple && depth_acc == 1 && array_index > 0 && + array->HasFastSmiOrObjectElements()) { + element_values->set_map(isolate->heap()->fixed_cow_array_map()); + } + + // Remember both the literal's constant values as well as the ElementsKind + // in a 2-element FixedArray. + Handle<FixedArray> literals = isolate->factory()->NewFixedArray(2, TENURED); + + ElementsKind kind = array->GetElementsKind(); + kind = is_holey ? GetHoleyElementsKind(kind) : GetPackedElementsKind(kind); + + literals->set(0, Smi::FromInt(kind)); + literals->set(1, *element_values); + + constant_elements_ = literals; + set_is_simple(is_simple); + set_depth(depth_acc); +} + + +void ArrayLiteral::AssignFeedbackVectorSlots(Isolate* isolate, + FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) { + // This logic that computes the number of slots needed for vector store + // ics must mirror FullCodeGenerator::VisitArrayLiteral. + int array_index = 0; + for (; array_index < values()->length(); array_index++) { + Expression* subexpr = values()->at(array_index); + if (subexpr->IsSpread()) break; + if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; + + // We'll reuse the same literal slot for all of the non-constant + // subexpressions that use a keyed store IC. + literal_slot_ = spec->AddKeyedStoreICSlot(); + return; + } +} + + +Handle<Object> MaterializedLiteral::GetBoilerplateValue(Expression* expression, + Isolate* isolate) { + if (expression->IsLiteral()) { + return expression->AsLiteral()->value(); + } + if (CompileTimeValue::IsCompileTimeValue(expression)) { + return CompileTimeValue::GetValue(isolate, expression); + } + return isolate->factory()->uninitialized_value(); +} + + +void MaterializedLiteral::BuildConstants(Isolate* isolate) { + if (IsArrayLiteral()) { + return AsArrayLiteral()->BuildConstantElements(isolate); + } + if (IsObjectLiteral()) { + return AsObjectLiteral()->BuildConstantProperties(isolate); + } + DCHECK(IsRegExpLiteral()); + DCHECK(depth() >= 1); // Depth should be initialized. +} + + +void UnaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) { + // TODO(olivf) If this Operation is used in a test context, then the + // expression has a ToBoolean stub and we want to collect the type + // information. However the GraphBuilder expects it to be on the instruction + // corresponding to the TestContext, therefore we have to store it here and + // not on the operand. + set_to_boolean_types(oracle->ToBooleanTypes(expression()->test_id())); +} + + +void BinaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) { + // TODO(olivf) If this Operation is used in a test context, then the right + // hand side has a ToBoolean stub and we want to collect the type information. + // However the GraphBuilder expects it to be on the instruction corresponding + // to the TestContext, therefore we have to store it here and not on the + // right hand operand. + set_to_boolean_types(oracle->ToBooleanTypes(right()->test_id())); +} + + +static bool IsTypeof(Expression* expr) { + UnaryOperation* maybe_unary = expr->AsUnaryOperation(); + return maybe_unary != NULL && maybe_unary->op() == Token::TYPEOF; +} + + +// Check for the pattern: typeof <expression> equals <string literal>. +static bool MatchLiteralCompareTypeof(Expression* left, + Token::Value op, + Expression* right, + Expression** expr, + Handle<String>* check) { + if (IsTypeof(left) && right->IsStringLiteral() && Token::IsEqualityOp(op)) { + *expr = left->AsUnaryOperation()->expression(); + *check = Handle<String>::cast(right->AsLiteral()->value()); + return true; + } + return false; +} + + +bool CompareOperation::IsLiteralCompareTypeof(Expression** expr, + Handle<String>* check) { + return MatchLiteralCompareTypeof(left_, op_, right_, expr, check) || + MatchLiteralCompareTypeof(right_, op_, left_, expr, check); +} + + +static bool IsVoidOfLiteral(Expression* expr) { + UnaryOperation* maybe_unary = expr->AsUnaryOperation(); + return maybe_unary != NULL && + maybe_unary->op() == Token::VOID && + maybe_unary->expression()->IsLiteral(); +} + + +// Check for the pattern: void <literal> equals <expression> or +// undefined equals <expression> +static bool MatchLiteralCompareUndefined(Expression* left, + Token::Value op, + Expression* right, + Expression** expr, + Isolate* isolate) { + if (IsVoidOfLiteral(left) && Token::IsEqualityOp(op)) { + *expr = right; + return true; + } + if (left->IsUndefinedLiteral(isolate) && Token::IsEqualityOp(op)) { + *expr = right; + return true; + } + return false; +} + + +bool CompareOperation::IsLiteralCompareUndefined( + Expression** expr, Isolate* isolate) { + return MatchLiteralCompareUndefined(left_, op_, right_, expr, isolate) || + MatchLiteralCompareUndefined(right_, op_, left_, expr, isolate); +} + + +// Check for the pattern: null equals <expression> +static bool MatchLiteralCompareNull(Expression* left, + Token::Value op, + Expression* right, + Expression** expr) { + if (left->IsNullLiteral() && Token::IsEqualityOp(op)) { + *expr = right; + return true; + } + return false; +} + + +bool CompareOperation::IsLiteralCompareNull(Expression** expr) { + return MatchLiteralCompareNull(left_, op_, right_, expr) || + MatchLiteralCompareNull(right_, op_, left_, expr); +} + + +// ---------------------------------------------------------------------------- +// Inlining support + +bool Declaration::IsInlineable() const { + return proxy()->var()->IsStackAllocated(); +} + +bool FunctionDeclaration::IsInlineable() const { + return false; +} + + +// ---------------------------------------------------------------------------- +// Recording of type feedback + +// TODO(rossberg): all RecordTypeFeedback functions should disappear +// once we use the common type field in the AST consistently. + +void Expression::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) { + set_to_boolean_types(oracle->ToBooleanTypes(test_id())); +} + + +bool Call::IsUsingCallFeedbackICSlot(Isolate* isolate) const { + CallType call_type = GetCallType(isolate); + if (call_type == POSSIBLY_EVAL_CALL) { + return false; + } + return true; +} + + +bool Call::IsUsingCallFeedbackSlot(Isolate* isolate) const { + // SuperConstructorCall uses a CallConstructStub, which wants + // a Slot, in addition to any IC slots requested elsewhere. + return GetCallType(isolate) == SUPER_CALL; +} + + +void Call::AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) { + if (IsUsingCallFeedbackICSlot(isolate)) { + ic_slot_ = spec->AddCallICSlot(); + } + if (IsUsingCallFeedbackSlot(isolate)) { + stub_slot_ = spec->AddGeneralSlot(); + } +} + + +Call::CallType Call::GetCallType(Isolate* isolate) const { + VariableProxy* proxy = expression()->AsVariableProxy(); + if (proxy != NULL) { + if (proxy->var()->is_possibly_eval(isolate)) { + return POSSIBLY_EVAL_CALL; + } else if (proxy->var()->IsUnallocatedOrGlobalSlot()) { + return GLOBAL_CALL; + } else if (proxy->var()->IsLookupSlot()) { + return LOOKUP_SLOT_CALL; + } + } + + if (expression()->IsSuperCallReference()) return SUPER_CALL; + + Property* property = expression()->AsProperty(); + if (property != nullptr) { + bool is_super = property->IsSuperAccess(); + if (property->key()->IsPropertyName()) { + return is_super ? NAMED_SUPER_PROPERTY_CALL : NAMED_PROPERTY_CALL; + } else { + return is_super ? KEYED_SUPER_PROPERTY_CALL : KEYED_PROPERTY_CALL; + } + } + + return OTHER_CALL; +} + + +// ---------------------------------------------------------------------------- +// Implementation of AstVisitor + +void AstVisitor::VisitDeclarations(ZoneList<Declaration*>* declarations) { + for (int i = 0; i < declarations->length(); i++) { + Visit(declarations->at(i)); + } +} + + +void AstVisitor::VisitStatements(ZoneList<Statement*>* statements) { + for (int i = 0; i < statements->length(); i++) { + Statement* stmt = statements->at(i); + Visit(stmt); + if (stmt->IsJump()) break; + } +} + + +void AstVisitor::VisitExpressions(ZoneList<Expression*>* expressions) { + for (int i = 0; i < expressions->length(); i++) { + // The variable statement visiting code may pass NULL expressions + // to this code. Maybe this should be handled by introducing an + // undefined expression or literal? Revisit this code if this + // changes + Expression* expression = expressions->at(i); + if (expression != NULL) Visit(expression); + } +} + + +CaseClause::CaseClause(Zone* zone, Expression* label, + ZoneList<Statement*>* statements, int pos) + : Expression(zone, pos), + label_(label), + statements_(statements), + compare_type_(Type::None(zone)) {} + + +uint32_t Literal::Hash() { + return raw_value()->IsString() + ? raw_value()->AsString()->hash() + : ComputeLongHash(double_to_uint64(raw_value()->AsNumber())); +} + + +// static +bool Literal::Match(void* literal1, void* literal2) { + const AstValue* x = static_cast<Literal*>(literal1)->raw_value(); + const AstValue* y = static_cast<Literal*>(literal2)->raw_value(); + return (x->IsString() && y->IsString() && x->AsString() == y->AsString()) || + (x->IsNumber() && y->IsNumber() && x->AsNumber() == y->AsNumber()); +} + + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/ast.h b/deps/v8/src/ast/ast.h new file mode 100644 index 0000000000..7f00955a64 --- /dev/null +++ b/deps/v8/src/ast/ast.h @@ -0,0 +1,3535 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_AST_H_ +#define V8_AST_AST_H_ + +#include "src/assembler.h" +#include "src/ast/ast-value-factory.h" +#include "src/ast/modules.h" +#include "src/ast/variables.h" +#include "src/bailout-reason.h" +#include "src/base/flags.h" +#include "src/base/smart-pointers.h" +#include "src/factory.h" +#include "src/isolate.h" +#include "src/list.h" +#include "src/parsing/token.h" +#include "src/runtime/runtime.h" +#include "src/small-pointer-list.h" +#include "src/types.h" +#include "src/utils.h" + +namespace v8 { +namespace internal { + +// The abstract syntax tree is an intermediate, light-weight +// representation of the parsed JavaScript code suitable for +// compilation to native code. + +// Nodes are allocated in a separate zone, which allows faster +// allocation and constant-time deallocation of the entire syntax +// tree. + + +// ---------------------------------------------------------------------------- +// Nodes of the abstract syntax tree. Only concrete classes are +// enumerated here. + +#define DECLARATION_NODE_LIST(V) \ + V(VariableDeclaration) \ + V(FunctionDeclaration) \ + V(ImportDeclaration) \ + V(ExportDeclaration) + +#define STATEMENT_NODE_LIST(V) \ + V(Block) \ + V(ExpressionStatement) \ + V(EmptyStatement) \ + V(SloppyBlockFunctionStatement) \ + V(IfStatement) \ + V(ContinueStatement) \ + V(BreakStatement) \ + V(ReturnStatement) \ + V(WithStatement) \ + V(SwitchStatement) \ + V(DoWhileStatement) \ + V(WhileStatement) \ + V(ForStatement) \ + V(ForInStatement) \ + V(ForOfStatement) \ + V(TryCatchStatement) \ + V(TryFinallyStatement) \ + V(DebuggerStatement) + +#define EXPRESSION_NODE_LIST(V) \ + V(FunctionLiteral) \ + V(ClassLiteral) \ + V(NativeFunctionLiteral) \ + V(Conditional) \ + V(VariableProxy) \ + V(Literal) \ + V(RegExpLiteral) \ + V(ObjectLiteral) \ + V(ArrayLiteral) \ + V(Assignment) \ + V(Yield) \ + V(Throw) \ + V(Property) \ + V(Call) \ + V(CallNew) \ + V(CallRuntime) \ + V(UnaryOperation) \ + V(CountOperation) \ + V(BinaryOperation) \ + V(CompareOperation) \ + V(Spread) \ + V(ThisFunction) \ + V(SuperPropertyReference) \ + V(SuperCallReference) \ + V(CaseClause) \ + V(EmptyParentheses) \ + V(DoExpression) \ + V(RewritableAssignmentExpression) + +#define AST_NODE_LIST(V) \ + DECLARATION_NODE_LIST(V) \ + STATEMENT_NODE_LIST(V) \ + EXPRESSION_NODE_LIST(V) + +// Forward declarations +class AstNodeFactory; +class AstVisitor; +class Declaration; +class Module; +class BreakableStatement; +class Expression; +class IterationStatement; +class MaterializedLiteral; +class Statement; +class TypeFeedbackOracle; + +#define DEF_FORWARD_DECLARATION(type) class type; +AST_NODE_LIST(DEF_FORWARD_DECLARATION) +#undef DEF_FORWARD_DECLARATION + + +// Typedef only introduced to avoid unreadable code. +typedef ZoneList<Handle<String>> ZoneStringList; +typedef ZoneList<Handle<Object>> ZoneObjectList; + + +#define DECLARE_NODE_TYPE(type) \ + void Accept(AstVisitor* v) override; \ + AstNode::NodeType node_type() const final { return AstNode::k##type; } \ + friend class AstNodeFactory; + + +class FeedbackVectorSlotCache { + public: + explicit FeedbackVectorSlotCache(Zone* zone) + : zone_(zone), + hash_map_(HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity, + ZoneAllocationPolicy(zone)) {} + + void Put(Variable* variable, FeedbackVectorSlot slot) { + ZoneHashMap::Entry* entry = hash_map_.LookupOrInsert( + variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_)); + entry->value = reinterpret_cast<void*>(slot.ToInt()); + } + + ZoneHashMap::Entry* Get(Variable* variable) const { + return hash_map_.Lookup(variable, ComputePointerHash(variable)); + } + + private: + Zone* zone_; + ZoneHashMap hash_map_; +}; + + +class AstProperties final BASE_EMBEDDED { + public: + enum Flag { + kNoFlags = 0, + kDontSelfOptimize = 1 << 0, + kDontCrankshaft = 1 << 1 + }; + + typedef base::Flags<Flag> Flags; + + explicit AstProperties(Zone* zone) : node_count_(0), spec_(zone) {} + + Flags& flags() { return flags_; } + Flags flags() const { return flags_; } + int node_count() { return node_count_; } + void add_node_count(int count) { node_count_ += count; } + + const FeedbackVectorSpec* get_spec() const { return &spec_; } + FeedbackVectorSpec* get_spec() { return &spec_; } + + private: + Flags flags_; + int node_count_; + FeedbackVectorSpec spec_; +}; + +DEFINE_OPERATORS_FOR_FLAGS(AstProperties::Flags) + + +class AstNode: public ZoneObject { + public: +#define DECLARE_TYPE_ENUM(type) k##type, + enum NodeType { + AST_NODE_LIST(DECLARE_TYPE_ENUM) + kInvalid = -1 + }; +#undef DECLARE_TYPE_ENUM + + void* operator new(size_t size, Zone* zone) { return zone->New(size); } + + explicit AstNode(int position): position_(position) {} + virtual ~AstNode() {} + + virtual void Accept(AstVisitor* v) = 0; + virtual NodeType node_type() const = 0; + int position() const { return position_; } + + // Type testing & conversion functions overridden by concrete subclasses. +#define DECLARE_NODE_FUNCTIONS(type) \ + bool Is##type() const { return node_type() == AstNode::k##type; } \ + type* As##type() { \ + return Is##type() ? reinterpret_cast<type*>(this) : NULL; \ + } \ + const type* As##type() const { \ + return Is##type() ? reinterpret_cast<const type*>(this) : NULL; \ + } + AST_NODE_LIST(DECLARE_NODE_FUNCTIONS) +#undef DECLARE_NODE_FUNCTIONS + + virtual BreakableStatement* AsBreakableStatement() { return NULL; } + virtual IterationStatement* AsIterationStatement() { return NULL; } + virtual MaterializedLiteral* AsMaterializedLiteral() { return NULL; } + + // The interface for feedback slots, with default no-op implementations for + // node types which don't actually have this. Note that this is conceptually + // not really nice, but multiple inheritance would introduce yet another + // vtable entry per node, something we don't want for space reasons. + virtual void AssignFeedbackVectorSlots(Isolate* isolate, + FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) {} + + private: + // Hidden to prevent accidental usage. It would have to load the + // current zone from the TLS. + void* operator new(size_t size); + + friend class CaseClause; // Generates AST IDs. + + int position_; +}; + + +class Statement : public AstNode { + public: + explicit Statement(Zone* zone, int position) : AstNode(position) {} + + bool IsEmpty() { return AsEmptyStatement() != NULL; } + virtual bool IsJump() const { return false; } + virtual void MarkTail() {} +}; + + +class SmallMapList final { + public: + SmallMapList() {} + SmallMapList(int capacity, Zone* zone) : list_(capacity, zone) {} + + void Reserve(int capacity, Zone* zone) { list_.Reserve(capacity, zone); } + void Clear() { list_.Clear(); } + void Sort() { list_.Sort(); } + + bool is_empty() const { return list_.is_empty(); } + int length() const { return list_.length(); } + + void AddMapIfMissing(Handle<Map> map, Zone* zone) { + if (!Map::TryUpdate(map).ToHandle(&map)) return; + for (int i = 0; i < length(); ++i) { + if (at(i).is_identical_to(map)) return; + } + Add(map, zone); + } + + void FilterForPossibleTransitions(Map* root_map) { + for (int i = list_.length() - 1; i >= 0; i--) { + if (at(i)->FindRootMap() != root_map) { + list_.RemoveElement(list_.at(i)); + } + } + } + + void Add(Handle<Map> handle, Zone* zone) { + list_.Add(handle.location(), zone); + } + + Handle<Map> at(int i) const { + return Handle<Map>(list_.at(i)); + } + + Handle<Map> first() const { return at(0); } + Handle<Map> last() const { return at(length() - 1); } + + private: + // The list stores pointers to Map*, that is Map**, so it's GC safe. + SmallPointerList<Map*> list_; + + DISALLOW_COPY_AND_ASSIGN(SmallMapList); +}; + + +class Expression : public AstNode { + public: + enum Context { + // Not assigned a context yet, or else will not be visited during + // code generation. + kUninitialized, + // Evaluated for its side effects. + kEffect, + // Evaluated for its value (and side effects). + kValue, + // Evaluated for control flow (and side effects). + kTest + }; + + // Mark this expression as being in tail position. + virtual void MarkTail() {} + + // True iff the expression is a valid reference expression. + virtual bool IsValidReferenceExpression() const { return false; } + + // Helpers for ToBoolean conversion. + virtual bool ToBooleanIsTrue() const { return false; } + virtual bool ToBooleanIsFalse() const { return false; } + + // Symbols that cannot be parsed as array indices are considered property + // names. We do not treat symbols that can be array indexes as property + // names because [] for string objects is handled only by keyed ICs. + virtual bool IsPropertyName() const { return false; } + + // True iff the expression is a literal represented as a smi. + bool IsSmiLiteral() const; + + // True iff the expression is a string literal. + bool IsStringLiteral() const; + + // True iff the expression is the null literal. + bool IsNullLiteral() const; + + // True if we can prove that the expression is the undefined literal. + bool IsUndefinedLiteral(Isolate* isolate) const; + + // True iff the expression is a valid target for an assignment. + bool IsValidReferenceExpressionOrThis() const; + + // Expression type bounds + Bounds bounds() const { return bounds_; } + void set_bounds(Bounds bounds) { bounds_ = bounds; } + + // Type feedback information for assignments and properties. + virtual bool IsMonomorphic() { + UNREACHABLE(); + return false; + } + virtual SmallMapList* GetReceiverTypes() { + UNREACHABLE(); + return NULL; + } + virtual KeyedAccessStoreMode GetStoreMode() const { + UNREACHABLE(); + return STANDARD_STORE; + } + virtual IcCheckType GetKeyType() const { + UNREACHABLE(); + return ELEMENT; + } + + // TODO(rossberg): this should move to its own AST node eventually. + virtual void RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle); + uint16_t to_boolean_types() const { + return ToBooleanTypesField::decode(bit_field_); + } + + void set_base_id(int id) { base_id_ = id; } + static int num_ids() { return parent_num_ids() + 2; } + BailoutId id() const { return BailoutId(local_id(0)); } + TypeFeedbackId test_id() const { return TypeFeedbackId(local_id(1)); } + + // Parenthesized expressions in the form `( Expression )`. + void set_is_parenthesized() { + bit_field_ = ParenthesizedField::update(bit_field_, true); + } + bool is_parenthesized() const { + return ParenthesizedField::decode(bit_field_); + } + + protected: + Expression(Zone* zone, int pos) + : AstNode(pos), + base_id_(BailoutId::None().ToInt()), + bounds_(Bounds::Unbounded()), + bit_field_(0) {} + static int parent_num_ids() { return 0; } + void set_to_boolean_types(uint16_t types) { + bit_field_ = ToBooleanTypesField::update(bit_field_, types); + } + + int base_id() const { + DCHECK(!BailoutId(base_id_).IsNone()); + return base_id_; + } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + int base_id_; + Bounds bounds_; + class ToBooleanTypesField : public BitField16<uint16_t, 0, 9> {}; + class ParenthesizedField + : public BitField16<bool, ToBooleanTypesField::kNext, 1> {}; + uint16_t bit_field_; + // Ends with 16-bit field; deriving classes in turn begin with + // 16-bit fields for optimum packing efficiency. +}; + + +class BreakableStatement : public Statement { + public: + enum BreakableType { + TARGET_FOR_ANONYMOUS, + TARGET_FOR_NAMED_ONLY + }; + + // The labels associated with this statement. May be NULL; + // if it is != NULL, guaranteed to contain at least one entry. + ZoneList<const AstRawString*>* labels() const { return labels_; } + + // Type testing & conversion. + BreakableStatement* AsBreakableStatement() final { return this; } + + // Code generation + Label* break_target() { return &break_target_; } + + // Testers. + bool is_target_for_anonymous() const { + return breakable_type_ == TARGET_FOR_ANONYMOUS; + } + + void set_base_id(int id) { base_id_ = id; } + static int num_ids() { return parent_num_ids() + 2; } + BailoutId EntryId() const { return BailoutId(local_id(0)); } + BailoutId ExitId() const { return BailoutId(local_id(1)); } + + protected: + BreakableStatement(Zone* zone, ZoneList<const AstRawString*>* labels, + BreakableType breakable_type, int position) + : Statement(zone, position), + labels_(labels), + breakable_type_(breakable_type), + base_id_(BailoutId::None().ToInt()) { + DCHECK(labels == NULL || labels->length() > 0); + } + static int parent_num_ids() { return 0; } + + int base_id() const { + DCHECK(!BailoutId(base_id_).IsNone()); + return base_id_; + } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + ZoneList<const AstRawString*>* labels_; + BreakableType breakable_type_; + Label break_target_; + int base_id_; +}; + + +class Block final : public BreakableStatement { + public: + DECLARE_NODE_TYPE(Block) + + ZoneList<Statement*>* statements() { return &statements_; } + bool ignore_completion_value() const { return ignore_completion_value_; } + + static int num_ids() { return parent_num_ids() + 1; } + BailoutId DeclsId() const { return BailoutId(local_id(0)); } + + bool IsJump() const override { + return !statements_.is_empty() && statements_.last()->IsJump() + && labels() == NULL; // Good enough as an approximation... + } + + void MarkTail() override { + if (!statements_.is_empty()) statements_.last()->MarkTail(); + } + + Scope* scope() const { return scope_; } + void set_scope(Scope* scope) { scope_ = scope; } + + protected: + Block(Zone* zone, ZoneList<const AstRawString*>* labels, int capacity, + bool ignore_completion_value, int pos) + : BreakableStatement(zone, labels, TARGET_FOR_NAMED_ONLY, pos), + statements_(capacity, zone), + ignore_completion_value_(ignore_completion_value), + scope_(NULL) {} + static int parent_num_ids() { return BreakableStatement::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + ZoneList<Statement*> statements_; + bool ignore_completion_value_; + Scope* scope_; +}; + + +class DoExpression final : public Expression { + public: + DECLARE_NODE_TYPE(DoExpression) + + Block* block() { return block_; } + void set_block(Block* b) { block_ = b; } + VariableProxy* result() { return result_; } + void set_result(VariableProxy* v) { result_ = v; } + + void MarkTail() override { block_->MarkTail(); } + + protected: + DoExpression(Zone* zone, Block* block, VariableProxy* result, int pos) + : Expression(zone, pos), block_(block), result_(result) { + DCHECK_NOT_NULL(block_); + DCHECK_NOT_NULL(result_); + } + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Block* block_; + VariableProxy* result_; +}; + + +class Declaration : public AstNode { + public: + VariableProxy* proxy() const { return proxy_; } + VariableMode mode() const { return mode_; } + Scope* scope() const { return scope_; } + virtual InitializationFlag initialization() const = 0; + virtual bool IsInlineable() const; + + protected: + Declaration(Zone* zone, VariableProxy* proxy, VariableMode mode, Scope* scope, + int pos) + : AstNode(pos), mode_(mode), proxy_(proxy), scope_(scope) { + DCHECK(IsDeclaredVariableMode(mode)); + } + + private: + VariableMode mode_; + VariableProxy* proxy_; + + // Nested scope from which the declaration originated. + Scope* scope_; +}; + + +class VariableDeclaration final : public Declaration { + public: + DECLARE_NODE_TYPE(VariableDeclaration) + + InitializationFlag initialization() const override { + return mode() == VAR ? kCreatedInitialized : kNeedsInitialization; + } + + bool is_class_declaration() const { return is_class_declaration_; } + + // VariableDeclarations can be grouped into consecutive declaration + // groups. Each VariableDeclaration is associated with the start position of + // the group it belongs to. The positions are used for strong mode scope + // checks for classes and functions. + int declaration_group_start() const { return declaration_group_start_; } + + protected: + VariableDeclaration(Zone* zone, VariableProxy* proxy, VariableMode mode, + Scope* scope, int pos, bool is_class_declaration = false, + int declaration_group_start = -1) + : Declaration(zone, proxy, mode, scope, pos), + is_class_declaration_(is_class_declaration), + declaration_group_start_(declaration_group_start) {} + + bool is_class_declaration_; + int declaration_group_start_; +}; + + +class FunctionDeclaration final : public Declaration { + public: + DECLARE_NODE_TYPE(FunctionDeclaration) + + FunctionLiteral* fun() const { return fun_; } + void set_fun(FunctionLiteral* f) { fun_ = f; } + InitializationFlag initialization() const override { + return kCreatedInitialized; + } + bool IsInlineable() const override; + + protected: + FunctionDeclaration(Zone* zone, + VariableProxy* proxy, + VariableMode mode, + FunctionLiteral* fun, + Scope* scope, + int pos) + : Declaration(zone, proxy, mode, scope, pos), + fun_(fun) { + DCHECK(mode == VAR || mode == LET || mode == CONST); + DCHECK(fun != NULL); + } + + private: + FunctionLiteral* fun_; +}; + + +class ImportDeclaration final : public Declaration { + public: + DECLARE_NODE_TYPE(ImportDeclaration) + + const AstRawString* import_name() const { return import_name_; } + const AstRawString* module_specifier() const { return module_specifier_; } + void set_module_specifier(const AstRawString* module_specifier) { + DCHECK(module_specifier_ == NULL); + module_specifier_ = module_specifier; + } + InitializationFlag initialization() const override { + return kNeedsInitialization; + } + + protected: + ImportDeclaration(Zone* zone, VariableProxy* proxy, + const AstRawString* import_name, + const AstRawString* module_specifier, Scope* scope, int pos) + : Declaration(zone, proxy, IMPORT, scope, pos), + import_name_(import_name), + module_specifier_(module_specifier) {} + + private: + const AstRawString* import_name_; + const AstRawString* module_specifier_; +}; + + +class ExportDeclaration final : public Declaration { + public: + DECLARE_NODE_TYPE(ExportDeclaration) + + InitializationFlag initialization() const override { + return kCreatedInitialized; + } + + protected: + ExportDeclaration(Zone* zone, VariableProxy* proxy, Scope* scope, int pos) + : Declaration(zone, proxy, LET, scope, pos) {} +}; + + +class Module : public AstNode { + public: + ModuleDescriptor* descriptor() const { return descriptor_; } + Block* body() const { return body_; } + + protected: + Module(Zone* zone, int pos) + : AstNode(pos), descriptor_(ModuleDescriptor::New(zone)), body_(NULL) {} + Module(Zone* zone, ModuleDescriptor* descriptor, int pos, Block* body = NULL) + : AstNode(pos), descriptor_(descriptor), body_(body) {} + + private: + ModuleDescriptor* descriptor_; + Block* body_; +}; + + +class IterationStatement : public BreakableStatement { + public: + // Type testing & conversion. + IterationStatement* AsIterationStatement() final { return this; } + + Statement* body() const { return body_; } + void set_body(Statement* s) { body_ = s; } + + static int num_ids() { return parent_num_ids() + 1; } + BailoutId OsrEntryId() const { return BailoutId(local_id(0)); } + virtual BailoutId ContinueId() const = 0; + virtual BailoutId StackCheckId() const = 0; + + // Code generation + Label* continue_target() { return &continue_target_; } + + protected: + IterationStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) + : BreakableStatement(zone, labels, TARGET_FOR_ANONYMOUS, pos), + body_(NULL) {} + static int parent_num_ids() { return BreakableStatement::num_ids(); } + void Initialize(Statement* body) { body_ = body; } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Statement* body_; + Label continue_target_; +}; + + +class DoWhileStatement final : public IterationStatement { + public: + DECLARE_NODE_TYPE(DoWhileStatement) + + void Initialize(Expression* cond, Statement* body) { + IterationStatement::Initialize(body); + cond_ = cond; + } + + Expression* cond() const { return cond_; } + void set_cond(Expression* e) { cond_ = e; } + + static int num_ids() { return parent_num_ids() + 2; } + BailoutId ContinueId() const override { return BailoutId(local_id(0)); } + BailoutId StackCheckId() const override { return BackEdgeId(); } + BailoutId BackEdgeId() const { return BailoutId(local_id(1)); } + + protected: + DoWhileStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) + : IterationStatement(zone, labels, pos), cond_(NULL) {} + static int parent_num_ids() { return IterationStatement::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Expression* cond_; +}; + + +class WhileStatement final : public IterationStatement { + public: + DECLARE_NODE_TYPE(WhileStatement) + + void Initialize(Expression* cond, Statement* body) { + IterationStatement::Initialize(body); + cond_ = cond; + } + + Expression* cond() const { return cond_; } + void set_cond(Expression* e) { cond_ = e; } + + static int num_ids() { return parent_num_ids() + 1; } + BailoutId ContinueId() const override { return EntryId(); } + BailoutId StackCheckId() const override { return BodyId(); } + BailoutId BodyId() const { return BailoutId(local_id(0)); } + + protected: + WhileStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) + : IterationStatement(zone, labels, pos), cond_(NULL) {} + static int parent_num_ids() { return IterationStatement::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Expression* cond_; +}; + + +class ForStatement final : public IterationStatement { + public: + DECLARE_NODE_TYPE(ForStatement) + + void Initialize(Statement* init, + Expression* cond, + Statement* next, + Statement* body) { + IterationStatement::Initialize(body); + init_ = init; + cond_ = cond; + next_ = next; + } + + Statement* init() const { return init_; } + Expression* cond() const { return cond_; } + Statement* next() const { return next_; } + + void set_init(Statement* s) { init_ = s; } + void set_cond(Expression* e) { cond_ = e; } + void set_next(Statement* s) { next_ = s; } + + static int num_ids() { return parent_num_ids() + 2; } + BailoutId ContinueId() const override { return BailoutId(local_id(0)); } + BailoutId StackCheckId() const override { return BodyId(); } + BailoutId BodyId() const { return BailoutId(local_id(1)); } + + protected: + ForStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) + : IterationStatement(zone, labels, pos), + init_(NULL), + cond_(NULL), + next_(NULL) {} + static int parent_num_ids() { return IterationStatement::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Statement* init_; + Expression* cond_; + Statement* next_; +}; + + +class ForEachStatement : public IterationStatement { + public: + enum VisitMode { + ENUMERATE, // for (each in subject) body; + ITERATE // for (each of subject) body; + }; + + void Initialize(Expression* each, Expression* subject, Statement* body) { + IterationStatement::Initialize(body); + each_ = each; + subject_ = subject; + } + + Expression* each() const { return each_; } + Expression* subject() const { return subject_; } + + void set_each(Expression* e) { each_ = e; } + void set_subject(Expression* e) { subject_ = e; } + + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override; + FeedbackVectorSlot EachFeedbackSlot() const { return each_slot_; } + + protected: + ForEachStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) + : IterationStatement(zone, labels, pos), each_(NULL), subject_(NULL) {} + + private: + Expression* each_; + Expression* subject_; + FeedbackVectorSlot each_slot_; +}; + + +class ForInStatement final : public ForEachStatement { + public: + DECLARE_NODE_TYPE(ForInStatement) + + Expression* enumerable() const { + return subject(); + } + + // Type feedback information. + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override { + ForEachStatement::AssignFeedbackVectorSlots(isolate, spec, cache); + for_in_feedback_slot_ = spec->AddGeneralSlot(); + } + + FeedbackVectorSlot ForInFeedbackSlot() { + DCHECK(!for_in_feedback_slot_.IsInvalid()); + return for_in_feedback_slot_; + } + + enum ForInType { FAST_FOR_IN, SLOW_FOR_IN }; + ForInType for_in_type() const { return for_in_type_; } + void set_for_in_type(ForInType type) { for_in_type_ = type; } + + static int num_ids() { return parent_num_ids() + 6; } + BailoutId BodyId() const { return BailoutId(local_id(0)); } + BailoutId PrepareId() const { return BailoutId(local_id(1)); } + BailoutId EnumId() const { return BailoutId(local_id(2)); } + BailoutId ToObjectId() const { return BailoutId(local_id(3)); } + BailoutId FilterId() const { return BailoutId(local_id(4)); } + BailoutId AssignmentId() const { return BailoutId(local_id(5)); } + BailoutId ContinueId() const override { return EntryId(); } + BailoutId StackCheckId() const override { return BodyId(); } + + protected: + ForInStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) + : ForEachStatement(zone, labels, pos), for_in_type_(SLOW_FOR_IN) {} + static int parent_num_ids() { return ForEachStatement::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + ForInType for_in_type_; + FeedbackVectorSlot for_in_feedback_slot_; +}; + + +class ForOfStatement final : public ForEachStatement { + public: + DECLARE_NODE_TYPE(ForOfStatement) + + void Initialize(Expression* each, + Expression* subject, + Statement* body, + Expression* assign_iterator, + Expression* next_result, + Expression* result_done, + Expression* assign_each) { + ForEachStatement::Initialize(each, subject, body); + assign_iterator_ = assign_iterator; + next_result_ = next_result; + result_done_ = result_done; + assign_each_ = assign_each; + } + + Expression* iterable() const { + return subject(); + } + + // iterator = subject[Symbol.iterator]() + Expression* assign_iterator() const { + return assign_iterator_; + } + + // result = iterator.next() // with type check + Expression* next_result() const { + return next_result_; + } + + // result.done + Expression* result_done() const { + return result_done_; + } + + // each = result.value + Expression* assign_each() const { + return assign_each_; + } + + void set_assign_iterator(Expression* e) { assign_iterator_ = e; } + void set_next_result(Expression* e) { next_result_ = e; } + void set_result_done(Expression* e) { result_done_ = e; } + void set_assign_each(Expression* e) { assign_each_ = e; } + + BailoutId ContinueId() const override { return EntryId(); } + BailoutId StackCheckId() const override { return BackEdgeId(); } + + static int num_ids() { return parent_num_ids() + 1; } + BailoutId BackEdgeId() const { return BailoutId(local_id(0)); } + + protected: + ForOfStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) + : ForEachStatement(zone, labels, pos), + assign_iterator_(NULL), + next_result_(NULL), + result_done_(NULL), + assign_each_(NULL) {} + static int parent_num_ids() { return ForEachStatement::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Expression* assign_iterator_; + Expression* next_result_; + Expression* result_done_; + Expression* assign_each_; +}; + + +class ExpressionStatement final : public Statement { + public: + DECLARE_NODE_TYPE(ExpressionStatement) + + void set_expression(Expression* e) { expression_ = e; } + Expression* expression() const { return expression_; } + bool IsJump() const override { return expression_->IsThrow(); } + void MarkTail() override { expression_->MarkTail(); } + + protected: + ExpressionStatement(Zone* zone, Expression* expression, int pos) + : Statement(zone, pos), expression_(expression) { } + + private: + Expression* expression_; +}; + + +class JumpStatement : public Statement { + public: + bool IsJump() const final { return true; } + + protected: + explicit JumpStatement(Zone* zone, int pos) : Statement(zone, pos) {} +}; + + +class ContinueStatement final : public JumpStatement { + public: + DECLARE_NODE_TYPE(ContinueStatement) + + IterationStatement* target() const { return target_; } + + protected: + explicit ContinueStatement(Zone* zone, IterationStatement* target, int pos) + : JumpStatement(zone, pos), target_(target) { } + + private: + IterationStatement* target_; +}; + + +class BreakStatement final : public JumpStatement { + public: + DECLARE_NODE_TYPE(BreakStatement) + + BreakableStatement* target() const { return target_; } + + protected: + explicit BreakStatement(Zone* zone, BreakableStatement* target, int pos) + : JumpStatement(zone, pos), target_(target) { } + + private: + BreakableStatement* target_; +}; + + +class ReturnStatement final : public JumpStatement { + public: + DECLARE_NODE_TYPE(ReturnStatement) + + Expression* expression() const { return expression_; } + + void set_expression(Expression* e) { expression_ = e; } + + protected: + explicit ReturnStatement(Zone* zone, Expression* expression, int pos) + : JumpStatement(zone, pos), expression_(expression) { } + + private: + Expression* expression_; +}; + + +class WithStatement final : public Statement { + public: + DECLARE_NODE_TYPE(WithStatement) + + Scope* scope() { return scope_; } + Expression* expression() const { return expression_; } + void set_expression(Expression* e) { expression_ = e; } + Statement* statement() const { return statement_; } + void set_statement(Statement* s) { statement_ = s; } + + void set_base_id(int id) { base_id_ = id; } + static int num_ids() { return parent_num_ids() + 2; } + BailoutId ToObjectId() const { return BailoutId(local_id(0)); } + BailoutId EntryId() const { return BailoutId(local_id(1)); } + + void MarkTail() override { statement_->MarkTail(); } + + protected: + WithStatement(Zone* zone, Scope* scope, Expression* expression, + Statement* statement, int pos) + : Statement(zone, pos), + scope_(scope), + expression_(expression), + statement_(statement), + base_id_(BailoutId::None().ToInt()) {} + static int parent_num_ids() { return 0; } + + int base_id() const { + DCHECK(!BailoutId(base_id_).IsNone()); + return base_id_; + } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Scope* scope_; + Expression* expression_; + Statement* statement_; + int base_id_; +}; + + +class CaseClause final : public Expression { + public: + DECLARE_NODE_TYPE(CaseClause) + + bool is_default() const { return label_ == NULL; } + Expression* label() const { + CHECK(!is_default()); + return label_; + } + void set_label(Expression* e) { label_ = e; } + Label* body_target() { return &body_target_; } + ZoneList<Statement*>* statements() const { return statements_; } + + static int num_ids() { return parent_num_ids() + 2; } + BailoutId EntryId() const { return BailoutId(local_id(0)); } + TypeFeedbackId CompareId() { return TypeFeedbackId(local_id(1)); } + + void MarkTail() override { + if (!statements_->is_empty()) statements_->last()->MarkTail(); + } + + Type* compare_type() { return compare_type_; } + void set_compare_type(Type* type) { compare_type_ = type; } + + protected: + static int parent_num_ids() { return Expression::num_ids(); } + + private: + CaseClause(Zone* zone, Expression* label, ZoneList<Statement*>* statements, + int pos); + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Expression* label_; + Label body_target_; + ZoneList<Statement*>* statements_; + Type* compare_type_; +}; + + +class SwitchStatement final : public BreakableStatement { + public: + DECLARE_NODE_TYPE(SwitchStatement) + + void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) { + tag_ = tag; + cases_ = cases; + } + + Expression* tag() const { return tag_; } + ZoneList<CaseClause*>* cases() const { return cases_; } + + void set_tag(Expression* t) { tag_ = t; } + + void MarkTail() override { + if (!cases_->is_empty()) cases_->last()->MarkTail(); + } + + protected: + SwitchStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) + : BreakableStatement(zone, labels, TARGET_FOR_ANONYMOUS, pos), + tag_(NULL), + cases_(NULL) {} + + private: + Expression* tag_; + ZoneList<CaseClause*>* cases_; +}; + + +// If-statements always have non-null references to their then- and +// else-parts. When parsing if-statements with no explicit else-part, +// the parser implicitly creates an empty statement. Use the +// HasThenStatement() and HasElseStatement() functions to check if a +// given if-statement has a then- or an else-part containing code. +class IfStatement final : public Statement { + public: + DECLARE_NODE_TYPE(IfStatement) + + bool HasThenStatement() const { return !then_statement()->IsEmpty(); } + bool HasElseStatement() const { return !else_statement()->IsEmpty(); } + + Expression* condition() const { return condition_; } + Statement* then_statement() const { return then_statement_; } + Statement* else_statement() const { return else_statement_; } + + void set_condition(Expression* e) { condition_ = e; } + void set_then_statement(Statement* s) { then_statement_ = s; } + void set_else_statement(Statement* s) { else_statement_ = s; } + + bool IsJump() const override { + return HasThenStatement() && then_statement()->IsJump() + && HasElseStatement() && else_statement()->IsJump(); + } + + void MarkTail() override { + then_statement_->MarkTail(); + else_statement_->MarkTail(); + } + + void set_base_id(int id) { base_id_ = id; } + static int num_ids() { return parent_num_ids() + 3; } + BailoutId IfId() const { return BailoutId(local_id(0)); } + BailoutId ThenId() const { return BailoutId(local_id(1)); } + BailoutId ElseId() const { return BailoutId(local_id(2)); } + + protected: + IfStatement(Zone* zone, Expression* condition, Statement* then_statement, + Statement* else_statement, int pos) + : Statement(zone, pos), + condition_(condition), + then_statement_(then_statement), + else_statement_(else_statement), + base_id_(BailoutId::None().ToInt()) {} + static int parent_num_ids() { return 0; } + + int base_id() const { + DCHECK(!BailoutId(base_id_).IsNone()); + return base_id_; + } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Expression* condition_; + Statement* then_statement_; + Statement* else_statement_; + int base_id_; +}; + + +class TryStatement : public Statement { + public: + Block* try_block() const { return try_block_; } + void set_try_block(Block* b) { try_block_ = b; } + + void set_base_id(int id) { base_id_ = id; } + static int num_ids() { return parent_num_ids() + 1; } + BailoutId HandlerId() const { return BailoutId(local_id(0)); } + + protected: + TryStatement(Zone* zone, Block* try_block, int pos) + : Statement(zone, pos), + try_block_(try_block), + base_id_(BailoutId::None().ToInt()) {} + static int parent_num_ids() { return 0; } + + int base_id() const { + DCHECK(!BailoutId(base_id_).IsNone()); + return base_id_; + } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Block* try_block_; + int base_id_; +}; + + +class TryCatchStatement final : public TryStatement { + public: + DECLARE_NODE_TYPE(TryCatchStatement) + + Scope* scope() { return scope_; } + Variable* variable() { return variable_; } + Block* catch_block() const { return catch_block_; } + void set_catch_block(Block* b) { catch_block_ = b; } + + void MarkTail() override { catch_block_->MarkTail(); } + + protected: + TryCatchStatement(Zone* zone, Block* try_block, Scope* scope, + Variable* variable, Block* catch_block, int pos) + : TryStatement(zone, try_block, pos), + scope_(scope), + variable_(variable), + catch_block_(catch_block) {} + + private: + Scope* scope_; + Variable* variable_; + Block* catch_block_; +}; + + +class TryFinallyStatement final : public TryStatement { + public: + DECLARE_NODE_TYPE(TryFinallyStatement) + + Block* finally_block() const { return finally_block_; } + void set_finally_block(Block* b) { finally_block_ = b; } + + void MarkTail() override { finally_block_->MarkTail(); } + + protected: + TryFinallyStatement(Zone* zone, Block* try_block, Block* finally_block, + int pos) + : TryStatement(zone, try_block, pos), finally_block_(finally_block) {} + + private: + Block* finally_block_; +}; + + +class DebuggerStatement final : public Statement { + public: + DECLARE_NODE_TYPE(DebuggerStatement) + + void set_base_id(int id) { base_id_ = id; } + static int num_ids() { return parent_num_ids() + 1; } + BailoutId DebugBreakId() const { return BailoutId(local_id(0)); } + + protected: + explicit DebuggerStatement(Zone* zone, int pos) + : Statement(zone, pos), base_id_(BailoutId::None().ToInt()) {} + static int parent_num_ids() { return 0; } + + int base_id() const { + DCHECK(!BailoutId(base_id_).IsNone()); + return base_id_; + } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + int base_id_; +}; + + +class EmptyStatement final : public Statement { + public: + DECLARE_NODE_TYPE(EmptyStatement) + + protected: + explicit EmptyStatement(Zone* zone, int pos): Statement(zone, pos) {} +}; + + +// Delegates to another statement, which may be overwritten. +// This was introduced to implement ES2015 Annex B3.3 for conditionally making +// sloppy-mode block-scoped functions have a var binding, which is changed +// from one statement to another during parsing. +class SloppyBlockFunctionStatement final : public Statement { + public: + DECLARE_NODE_TYPE(SloppyBlockFunctionStatement) + + Statement* statement() const { return statement_; } + void set_statement(Statement* statement) { statement_ = statement; } + Scope* scope() const { return scope_; } + + private: + SloppyBlockFunctionStatement(Zone* zone, Statement* statement, Scope* scope) + : Statement(zone, RelocInfo::kNoPosition), + statement_(statement), + scope_(scope) {} + + Statement* statement_; + Scope* const scope_; +}; + + +class Literal final : public Expression { + public: + DECLARE_NODE_TYPE(Literal) + + bool IsPropertyName() const override { return value_->IsPropertyName(); } + + Handle<String> AsPropertyName() { + DCHECK(IsPropertyName()); + return Handle<String>::cast(value()); + } + + const AstRawString* AsRawPropertyName() { + DCHECK(IsPropertyName()); + return value_->AsString(); + } + + bool ToBooleanIsTrue() const override { return value()->BooleanValue(); } + bool ToBooleanIsFalse() const override { return !value()->BooleanValue(); } + + Handle<Object> value() const { return value_->value(); } + const AstValue* raw_value() const { return value_; } + + // Support for using Literal as a HashMap key. NOTE: Currently, this works + // only for string and number literals! + uint32_t Hash(); + static bool Match(void* literal1, void* literal2); + + static int num_ids() { return parent_num_ids() + 1; } + TypeFeedbackId LiteralFeedbackId() const { + return TypeFeedbackId(local_id(0)); + } + + protected: + Literal(Zone* zone, const AstValue* value, int position) + : Expression(zone, position), value_(value) {} + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + const AstValue* value_; +}; + + +class AstLiteralReindexer; + +// Base class for literals that needs space in the corresponding JSFunction. +class MaterializedLiteral : public Expression { + public: + MaterializedLiteral* AsMaterializedLiteral() final { return this; } + + int literal_index() { return literal_index_; } + + int depth() const { + // only callable after initialization. + DCHECK(depth_ >= 1); + return depth_; + } + + bool is_strong() const { return is_strong_; } + + protected: + MaterializedLiteral(Zone* zone, int literal_index, bool is_strong, int pos) + : Expression(zone, pos), + literal_index_(literal_index), + is_simple_(false), + is_strong_(is_strong), + depth_(0) {} + + // A materialized literal is simple if the values consist of only + // constants and simple object and array literals. + bool is_simple() const { return is_simple_; } + void set_is_simple(bool is_simple) { is_simple_ = is_simple; } + friend class CompileTimeValue; + + void set_depth(int depth) { + DCHECK(depth >= 1); + depth_ = depth; + } + + // Populate the constant properties/elements fixed array. + void BuildConstants(Isolate* isolate); + friend class ArrayLiteral; + friend class ObjectLiteral; + + // If the expression is a literal, return the literal value; + // if the expression is a materialized literal and is simple return a + // compile time value as encoded by CompileTimeValue::GetValue(). + // Otherwise, return undefined literal as the placeholder + // in the object literal boilerplate. + Handle<Object> GetBoilerplateValue(Expression* expression, Isolate* isolate); + + private: + int literal_index_; + bool is_simple_; + bool is_strong_; + int depth_; + + friend class AstLiteralReindexer; +}; + + +// Property is used for passing information +// about an object literal's properties from the parser +// to the code generator. +class ObjectLiteralProperty final : public ZoneObject { + public: + enum Kind { + CONSTANT, // Property with constant value (compile time). + COMPUTED, // Property with computed value (execution time). + MATERIALIZED_LITERAL, // Property value is a materialized literal. + GETTER, SETTER, // Property is an accessor function. + PROTOTYPE // Property is __proto__. + }; + + Expression* key() { return key_; } + Expression* value() { return value_; } + Kind kind() { return kind_; } + + void set_key(Expression* e) { key_ = e; } + void set_value(Expression* e) { value_ = e; } + + // Type feedback information. + bool IsMonomorphic() { return !receiver_type_.is_null(); } + Handle<Map> GetReceiverType() { return receiver_type_; } + + bool IsCompileTimeValue(); + + void set_emit_store(bool emit_store); + bool emit_store(); + + bool is_static() const { return is_static_; } + bool is_computed_name() const { return is_computed_name_; } + + FeedbackVectorSlot GetSlot(int offset = 0) const { + DCHECK_LT(offset, static_cast<int>(arraysize(slots_))); + return slots_[offset]; + } + void SetSlot(FeedbackVectorSlot slot, int offset = 0) { + DCHECK_LT(offset, static_cast<int>(arraysize(slots_))); + slots_[offset] = slot; + } + + void set_receiver_type(Handle<Map> map) { receiver_type_ = map; } + + protected: + friend class AstNodeFactory; + + ObjectLiteralProperty(Expression* key, Expression* value, Kind kind, + bool is_static, bool is_computed_name); + ObjectLiteralProperty(AstValueFactory* ast_value_factory, Expression* key, + Expression* value, bool is_static, + bool is_computed_name); + + private: + Expression* key_; + Expression* value_; + FeedbackVectorSlot slots_[2]; + Kind kind_; + bool emit_store_; + bool is_static_; + bool is_computed_name_; + Handle<Map> receiver_type_; +}; + + +// An object literal has a boilerplate object that is used +// for minimizing the work when constructing it at runtime. +class ObjectLiteral final : public MaterializedLiteral { + public: + typedef ObjectLiteralProperty Property; + + DECLARE_NODE_TYPE(ObjectLiteral) + + Handle<FixedArray> constant_properties() const { + return constant_properties_; + } + int properties_count() const { return constant_properties_->length() / 2; } + ZoneList<Property*>* properties() const { return properties_; } + bool fast_elements() const { return fast_elements_; } + bool may_store_doubles() const { return may_store_doubles_; } + bool has_function() const { return has_function_; } + bool has_elements() const { return has_elements_; } + + // Decide if a property should be in the object boilerplate. + static bool IsBoilerplateProperty(Property* property); + + // Populate the constant properties fixed array. + void BuildConstantProperties(Isolate* isolate); + + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code is emitted. + void CalculateEmitStore(Zone* zone); + + // Assemble bitfield of flags for the CreateObjectLiteral helper. + int ComputeFlags(bool disable_mementos = false) const { + int flags = fast_elements() ? kFastElements : kNoFlags; + flags |= has_function() ? kHasFunction : kNoFlags; + if (depth() == 1 && !has_elements() && !may_store_doubles()) { + flags |= kShallowProperties; + } + if (disable_mementos) { + flags |= kDisableMementos; + } + if (is_strong()) { + flags |= kIsStrong; + } + return flags; + } + + enum Flags { + kNoFlags = 0, + kFastElements = 1, + kHasFunction = 1 << 1, + kShallowProperties = 1 << 2, + kDisableMementos = 1 << 3, + kIsStrong = 1 << 4 + }; + + struct Accessors: public ZoneObject { + Accessors() : getter(NULL), setter(NULL) {} + ObjectLiteralProperty* getter; + ObjectLiteralProperty* setter; + }; + + BailoutId CreateLiteralId() const { return BailoutId(local_id(0)); } + + // Return an AST id for a property that is used in simulate instructions. + BailoutId GetIdForPropertyName(int i) { + return BailoutId(local_id(2 * i + 1)); + } + BailoutId GetIdForPropertySet(int i) { + return BailoutId(local_id(2 * i + 2)); + } + + // Unlike other AST nodes, this number of bailout IDs allocated for an + // ObjectLiteral can vary, so num_ids() is not a static method. + int num_ids() const { + return parent_num_ids() + 1 + 2 * properties()->length(); + } + + // Object literals need one feedback slot for each non-trivial value, as well + // as some slots for home objects. + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override; + + protected: + ObjectLiteral(Zone* zone, ZoneList<Property*>* properties, int literal_index, + int boilerplate_properties, bool has_function, bool is_strong, + int pos) + : MaterializedLiteral(zone, literal_index, is_strong, pos), + properties_(properties), + boilerplate_properties_(boilerplate_properties), + fast_elements_(false), + has_elements_(false), + may_store_doubles_(false), + has_function_(has_function) {} + static int parent_num_ids() { return MaterializedLiteral::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + Handle<FixedArray> constant_properties_; + ZoneList<Property*>* properties_; + int boilerplate_properties_; + bool fast_elements_; + bool has_elements_; + bool may_store_doubles_; + bool has_function_; + FeedbackVectorSlot slot_; +}; + + +// A map from property names to getter/setter pairs allocated in the zone. +class AccessorTable : public TemplateHashMap<Literal, ObjectLiteral::Accessors, + ZoneAllocationPolicy> { + public: + explicit AccessorTable(Zone* zone) + : TemplateHashMap<Literal, ObjectLiteral::Accessors, + ZoneAllocationPolicy>(Literal::Match, + ZoneAllocationPolicy(zone)), + zone_(zone) {} + + Iterator lookup(Literal* literal) { + Iterator it = find(literal, true, ZoneAllocationPolicy(zone_)); + if (it->second == NULL) it->second = new (zone_) ObjectLiteral::Accessors(); + return it; + } + + private: + Zone* zone_; +}; + + +// Node for capturing a regexp literal. +class RegExpLiteral final : public MaterializedLiteral { + public: + DECLARE_NODE_TYPE(RegExpLiteral) + + Handle<String> pattern() const { return pattern_->string(); } + int flags() const { return flags_; } + + protected: + RegExpLiteral(Zone* zone, const AstRawString* pattern, int flags, + int literal_index, bool is_strong, int pos) + : MaterializedLiteral(zone, literal_index, is_strong, pos), + pattern_(pattern), + flags_(flags) { + set_depth(1); + } + + private: + const AstRawString* const pattern_; + int const flags_; +}; + + +// An array literal has a literals object that is used +// for minimizing the work when constructing it at runtime. +class ArrayLiteral final : public MaterializedLiteral { + public: + DECLARE_NODE_TYPE(ArrayLiteral) + + Handle<FixedArray> constant_elements() const { return constant_elements_; } + ElementsKind constant_elements_kind() const { + DCHECK_EQ(2, constant_elements_->length()); + return static_cast<ElementsKind>( + Smi::cast(constant_elements_->get(0))->value()); + } + + ZoneList<Expression*>* values() const { return values_; } + + BailoutId CreateLiteralId() const { return BailoutId(local_id(0)); } + + // Return an AST id for an element that is used in simulate instructions. + BailoutId GetIdForElement(int i) { return BailoutId(local_id(i + 1)); } + + // Unlike other AST nodes, this number of bailout IDs allocated for an + // ArrayLiteral can vary, so num_ids() is not a static method. + int num_ids() const { return parent_num_ids() + 1 + values()->length(); } + + // Populate the constant elements fixed array. + void BuildConstantElements(Isolate* isolate); + + // Assemble bitfield of flags for the CreateArrayLiteral helper. + int ComputeFlags(bool disable_mementos = false) const { + int flags = depth() == 1 ? kShallowElements : kNoFlags; + if (disable_mementos) { + flags |= kDisableMementos; + } + if (is_strong()) { + flags |= kIsStrong; + } + return flags; + } + + enum Flags { + kNoFlags = 0, + kShallowElements = 1, + kDisableMementos = 1 << 1, + kIsStrong = 1 << 2 + }; + + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override; + FeedbackVectorSlot LiteralFeedbackSlot() const { return literal_slot_; } + + protected: + ArrayLiteral(Zone* zone, ZoneList<Expression*>* values, + int first_spread_index, int literal_index, bool is_strong, + int pos) + : MaterializedLiteral(zone, literal_index, is_strong, pos), + values_(values), + first_spread_index_(first_spread_index) {} + static int parent_num_ids() { return MaterializedLiteral::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Handle<FixedArray> constant_elements_; + ZoneList<Expression*>* values_; + int first_spread_index_; + FeedbackVectorSlot literal_slot_; +}; + + +class VariableProxy final : public Expression { + public: + DECLARE_NODE_TYPE(VariableProxy) + + bool IsValidReferenceExpression() const override { + return !is_this() && !is_new_target(); + } + + bool IsArguments() const { return is_resolved() && var()->is_arguments(); } + + Handle<String> name() const { return raw_name()->string(); } + const AstRawString* raw_name() const { + return is_resolved() ? var_->raw_name() : raw_name_; + } + + Variable* var() const { + DCHECK(is_resolved()); + return var_; + } + void set_var(Variable* v) { + DCHECK(!is_resolved()); + DCHECK_NOT_NULL(v); + var_ = v; + } + + bool is_this() const { return IsThisField::decode(bit_field_); } + + bool is_assigned() const { return IsAssignedField::decode(bit_field_); } + void set_is_assigned() { + bit_field_ = IsAssignedField::update(bit_field_, true); + } + + bool is_resolved() const { return IsResolvedField::decode(bit_field_); } + void set_is_resolved() { + bit_field_ = IsResolvedField::update(bit_field_, true); + } + + bool is_new_target() const { return IsNewTargetField::decode(bit_field_); } + void set_is_new_target() { + bit_field_ = IsNewTargetField::update(bit_field_, true); + } + + int end_position() const { return end_position_; } + + // Bind this proxy to the variable var. + void BindTo(Variable* var); + + bool UsesVariableFeedbackSlot() const { + return var()->IsUnallocated() || var()->IsLookupSlot(); + } + + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override; + + FeedbackVectorSlot VariableFeedbackSlot() { return variable_feedback_slot_; } + + static int num_ids() { return parent_num_ids() + 1; } + BailoutId BeforeId() const { return BailoutId(local_id(0)); } + + protected: + VariableProxy(Zone* zone, Variable* var, int start_position, + int end_position); + + VariableProxy(Zone* zone, const AstRawString* name, + Variable::Kind variable_kind, int start_position, + int end_position); + static int parent_num_ids() { return Expression::num_ids(); } + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + class IsThisField : public BitField8<bool, 0, 1> {}; + class IsAssignedField : public BitField8<bool, 1, 1> {}; + class IsResolvedField : public BitField8<bool, 2, 1> {}; + class IsNewTargetField : public BitField8<bool, 3, 1> {}; + + // Start with 16-bit (or smaller) field, which should get packed together + // with Expression's trailing 16-bit field. + uint8_t bit_field_; + FeedbackVectorSlot variable_feedback_slot_; + union { + const AstRawString* raw_name_; // if !is_resolved_ + Variable* var_; // if is_resolved_ + }; + // Position is stored in the AstNode superclass, but VariableProxy needs to + // know its end position too (for error messages). It cannot be inferred from + // the variable name length because it can contain escapes. + int end_position_; +}; + + +// Left-hand side can only be a property, a global or a (parameter or local) +// slot. +enum LhsKind { + VARIABLE, + NAMED_PROPERTY, + KEYED_PROPERTY, + NAMED_SUPER_PROPERTY, + KEYED_SUPER_PROPERTY +}; + + +class Property final : public Expression { + public: + DECLARE_NODE_TYPE(Property) + + bool IsValidReferenceExpression() const override { return true; } + + Expression* obj() const { return obj_; } + Expression* key() const { return key_; } + + void set_obj(Expression* e) { obj_ = e; } + void set_key(Expression* e) { key_ = e; } + + static int num_ids() { return parent_num_ids() + 1; } + BailoutId LoadId() const { return BailoutId(local_id(0)); } + + bool IsStringAccess() const { + return IsStringAccessField::decode(bit_field_); + } + + // Type feedback information. + bool IsMonomorphic() override { return receiver_types_.length() == 1; } + SmallMapList* GetReceiverTypes() override { return &receiver_types_; } + KeyedAccessStoreMode GetStoreMode() const override { return STANDARD_STORE; } + IcCheckType GetKeyType() const override { + return KeyTypeField::decode(bit_field_); + } + bool IsUninitialized() const { + return !is_for_call() && HasNoTypeInformation(); + } + bool HasNoTypeInformation() const { + return GetInlineCacheState() == UNINITIALIZED; + } + InlineCacheState GetInlineCacheState() const { + return InlineCacheStateField::decode(bit_field_); + } + void set_is_string_access(bool b) { + bit_field_ = IsStringAccessField::update(bit_field_, b); + } + void set_key_type(IcCheckType key_type) { + bit_field_ = KeyTypeField::update(bit_field_, key_type); + } + void set_inline_cache_state(InlineCacheState state) { + bit_field_ = InlineCacheStateField::update(bit_field_, state); + } + void mark_for_call() { + bit_field_ = IsForCallField::update(bit_field_, true); + } + bool is_for_call() const { return IsForCallField::decode(bit_field_); } + + bool IsSuperAccess() { return obj()->IsSuperPropertyReference(); } + + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override { + FeedbackVectorSlotKind kind = key()->IsPropertyName() + ? FeedbackVectorSlotKind::LOAD_IC + : FeedbackVectorSlotKind::KEYED_LOAD_IC; + property_feedback_slot_ = spec->AddSlot(kind); + } + + FeedbackVectorSlot PropertyFeedbackSlot() const { + return property_feedback_slot_; + } + + static LhsKind GetAssignType(Property* property) { + if (property == NULL) return VARIABLE; + bool super_access = property->IsSuperAccess(); + return (property->key()->IsPropertyName()) + ? (super_access ? NAMED_SUPER_PROPERTY : NAMED_PROPERTY) + : (super_access ? KEYED_SUPER_PROPERTY : KEYED_PROPERTY); + } + + protected: + Property(Zone* zone, Expression* obj, Expression* key, int pos) + : Expression(zone, pos), + bit_field_(IsForCallField::encode(false) | + IsStringAccessField::encode(false) | + InlineCacheStateField::encode(UNINITIALIZED)), + obj_(obj), + key_(key) {} + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + class IsForCallField : public BitField8<bool, 0, 1> {}; + class IsStringAccessField : public BitField8<bool, 1, 1> {}; + class KeyTypeField : public BitField8<IcCheckType, 2, 1> {}; + class InlineCacheStateField : public BitField8<InlineCacheState, 3, 4> {}; + uint8_t bit_field_; + FeedbackVectorSlot property_feedback_slot_; + Expression* obj_; + Expression* key_; + SmallMapList receiver_types_; +}; + + +class Call final : public Expression { + public: + DECLARE_NODE_TYPE(Call) + + Expression* expression() const { return expression_; } + ZoneList<Expression*>* arguments() const { return arguments_; } + + void set_expression(Expression* e) { expression_ = e; } + + // Type feedback information. + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override; + + FeedbackVectorSlot CallFeedbackSlot() const { return stub_slot_; } + + FeedbackVectorSlot CallFeedbackICSlot() const { return ic_slot_; } + + SmallMapList* GetReceiverTypes() override { + if (expression()->IsProperty()) { + return expression()->AsProperty()->GetReceiverTypes(); + } + return NULL; + } + + bool IsMonomorphic() override { + if (expression()->IsProperty()) { + return expression()->AsProperty()->IsMonomorphic(); + } + return !target_.is_null(); + } + + bool global_call() const { + VariableProxy* proxy = expression_->AsVariableProxy(); + return proxy != NULL && proxy->var()->IsUnallocatedOrGlobalSlot(); + } + + bool known_global_function() const { + return global_call() && !target_.is_null(); + } + + Handle<JSFunction> target() { return target_; } + + Handle<AllocationSite> allocation_site() { return allocation_site_; } + + void SetKnownGlobalTarget(Handle<JSFunction> target) { + target_ = target; + set_is_uninitialized(false); + } + void set_target(Handle<JSFunction> target) { target_ = target; } + void set_allocation_site(Handle<AllocationSite> site) { + allocation_site_ = site; + } + + static int num_ids() { return parent_num_ids() + 4; } + BailoutId ReturnId() const { return BailoutId(local_id(0)); } + BailoutId EvalId() const { return BailoutId(local_id(1)); } + BailoutId LookupId() const { return BailoutId(local_id(2)); } + BailoutId CallId() const { return BailoutId(local_id(3)); } + + bool is_uninitialized() const { + return IsUninitializedField::decode(bit_field_); + } + void set_is_uninitialized(bool b) { + bit_field_ = IsUninitializedField::update(bit_field_, b); + } + + bool is_tail() const { return IsTailField::decode(bit_field_); } + void MarkTail() override { + bit_field_ = IsTailField::update(bit_field_, true); + } + + enum CallType { + POSSIBLY_EVAL_CALL, + GLOBAL_CALL, + LOOKUP_SLOT_CALL, + NAMED_PROPERTY_CALL, + KEYED_PROPERTY_CALL, + NAMED_SUPER_PROPERTY_CALL, + KEYED_SUPER_PROPERTY_CALL, + SUPER_CALL, + OTHER_CALL + }; + + // Helpers to determine how to handle the call. + CallType GetCallType(Isolate* isolate) const; + bool IsUsingCallFeedbackSlot(Isolate* isolate) const; + bool IsUsingCallFeedbackICSlot(Isolate* isolate) const; + +#ifdef DEBUG + // Used to assert that the FullCodeGenerator records the return site. + bool return_is_recorded_; +#endif + + protected: + Call(Zone* zone, Expression* expression, ZoneList<Expression*>* arguments, + int pos) + : Expression(zone, pos), + expression_(expression), + arguments_(arguments), + bit_field_(IsUninitializedField::encode(false)) { + if (expression->IsProperty()) { + expression->AsProperty()->mark_for_call(); + } + } + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + FeedbackVectorSlot ic_slot_; + FeedbackVectorSlot stub_slot_; + Expression* expression_; + ZoneList<Expression*>* arguments_; + Handle<JSFunction> target_; + Handle<AllocationSite> allocation_site_; + class IsUninitializedField : public BitField8<bool, 0, 1> {}; + class IsTailField : public BitField8<bool, 1, 1> {}; + uint8_t bit_field_; +}; + + +class CallNew final : public Expression { + public: + DECLARE_NODE_TYPE(CallNew) + + Expression* expression() const { return expression_; } + ZoneList<Expression*>* arguments() const { return arguments_; } + + void set_expression(Expression* e) { expression_ = e; } + + // Type feedback information. + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override { + callnew_feedback_slot_ = spec->AddGeneralSlot(); + } + + FeedbackVectorSlot CallNewFeedbackSlot() { + DCHECK(!callnew_feedback_slot_.IsInvalid()); + return callnew_feedback_slot_; + } + + bool IsMonomorphic() override { return is_monomorphic_; } + Handle<JSFunction> target() const { return target_; } + Handle<AllocationSite> allocation_site() const { + return allocation_site_; + } + + static int num_ids() { return parent_num_ids() + 1; } + static int feedback_slots() { return 1; } + BailoutId ReturnId() const { return BailoutId(local_id(0)); } + + void set_allocation_site(Handle<AllocationSite> site) { + allocation_site_ = site; + } + void set_is_monomorphic(bool monomorphic) { is_monomorphic_ = monomorphic; } + void set_target(Handle<JSFunction> target) { target_ = target; } + void SetKnownGlobalTarget(Handle<JSFunction> target) { + target_ = target; + is_monomorphic_ = true; + } + + protected: + CallNew(Zone* zone, Expression* expression, ZoneList<Expression*>* arguments, + int pos) + : Expression(zone, pos), + expression_(expression), + arguments_(arguments), + is_monomorphic_(false) {} + + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Expression* expression_; + ZoneList<Expression*>* arguments_; + bool is_monomorphic_; + Handle<JSFunction> target_; + Handle<AllocationSite> allocation_site_; + FeedbackVectorSlot callnew_feedback_slot_; +}; + + +// The CallRuntime class does not represent any official JavaScript +// language construct. Instead it is used to call a C or JS function +// with a set of arguments. This is used from the builtins that are +// implemented in JavaScript (see "v8natives.js"). +class CallRuntime final : public Expression { + public: + DECLARE_NODE_TYPE(CallRuntime) + + ZoneList<Expression*>* arguments() const { return arguments_; } + bool is_jsruntime() const { return function_ == NULL; } + + int context_index() const { + DCHECK(is_jsruntime()); + return context_index_; + } + const Runtime::Function* function() const { + DCHECK(!is_jsruntime()); + return function_; + } + + static int num_ids() { return parent_num_ids() + 1; } + BailoutId CallId() { return BailoutId(local_id(0)); } + + const char* debug_name() { + return is_jsruntime() ? "(context function)" : function_->name; + } + + protected: + CallRuntime(Zone* zone, const Runtime::Function* function, + ZoneList<Expression*>* arguments, int pos) + : Expression(zone, pos), function_(function), arguments_(arguments) {} + + CallRuntime(Zone* zone, int context_index, ZoneList<Expression*>* arguments, + int pos) + : Expression(zone, pos), + function_(NULL), + context_index_(context_index), + arguments_(arguments) {} + + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + const Runtime::Function* function_; + int context_index_; + ZoneList<Expression*>* arguments_; +}; + + +class UnaryOperation final : public Expression { + public: + DECLARE_NODE_TYPE(UnaryOperation) + + Token::Value op() const { return op_; } + Expression* expression() const { return expression_; } + void set_expression(Expression* e) { expression_ = e; } + + // For unary not (Token::NOT), the AST ids where true and false will + // actually be materialized, respectively. + static int num_ids() { return parent_num_ids() + 2; } + BailoutId MaterializeTrueId() const { return BailoutId(local_id(0)); } + BailoutId MaterializeFalseId() const { return BailoutId(local_id(1)); } + + void RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) override; + + protected: + UnaryOperation(Zone* zone, Token::Value op, Expression* expression, int pos) + : Expression(zone, pos), op_(op), expression_(expression) { + DCHECK(Token::IsUnaryOp(op)); + } + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Token::Value op_; + Expression* expression_; +}; + + +class BinaryOperation final : public Expression { + public: + DECLARE_NODE_TYPE(BinaryOperation) + + Token::Value op() const { return static_cast<Token::Value>(op_); } + Expression* left() const { return left_; } + void set_left(Expression* e) { left_ = e; } + Expression* right() const { return right_; } + void set_right(Expression* e) { right_ = e; } + Handle<AllocationSite> allocation_site() const { return allocation_site_; } + void set_allocation_site(Handle<AllocationSite> allocation_site) { + allocation_site_ = allocation_site; + } + + void MarkTail() override { + switch (op()) { + case Token::COMMA: + case Token::AND: + case Token::OR: + right_->MarkTail(); + default: + break; + } + } + + // The short-circuit logical operations need an AST ID for their + // right-hand subexpression. + static int num_ids() { return parent_num_ids() + 2; } + BailoutId RightId() const { return BailoutId(local_id(0)); } + + TypeFeedbackId BinaryOperationFeedbackId() const { + return TypeFeedbackId(local_id(1)); + } + Maybe<int> fixed_right_arg() const { + return has_fixed_right_arg_ ? Just(fixed_right_arg_value_) : Nothing<int>(); + } + void set_fixed_right_arg(Maybe<int> arg) { + has_fixed_right_arg_ = arg.IsJust(); + if (arg.IsJust()) fixed_right_arg_value_ = arg.FromJust(); + } + + void RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) override; + + protected: + BinaryOperation(Zone* zone, Token::Value op, Expression* left, + Expression* right, int pos) + : Expression(zone, pos), + op_(static_cast<byte>(op)), + has_fixed_right_arg_(false), + fixed_right_arg_value_(0), + left_(left), + right_(right) { + DCHECK(Token::IsBinaryOp(op)); + } + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + const byte op_; // actually Token::Value + // TODO(rossberg): the fixed arg should probably be represented as a Constant + // type for the RHS. Currenty it's actually a Maybe<int> + bool has_fixed_right_arg_; + int fixed_right_arg_value_; + Expression* left_; + Expression* right_; + Handle<AllocationSite> allocation_site_; +}; + + +class CountOperation final : public Expression { + public: + DECLARE_NODE_TYPE(CountOperation) + + bool is_prefix() const { return IsPrefixField::decode(bit_field_); } + bool is_postfix() const { return !is_prefix(); } + + Token::Value op() const { return TokenField::decode(bit_field_); } + Token::Value binary_op() { + return (op() == Token::INC) ? Token::ADD : Token::SUB; + } + + Expression* expression() const { return expression_; } + void set_expression(Expression* e) { expression_ = e; } + + bool IsMonomorphic() override { return receiver_types_.length() == 1; } + SmallMapList* GetReceiverTypes() override { return &receiver_types_; } + IcCheckType GetKeyType() const override { + return KeyTypeField::decode(bit_field_); + } + KeyedAccessStoreMode GetStoreMode() const override { + return StoreModeField::decode(bit_field_); + } + Type* type() const { return type_; } + void set_key_type(IcCheckType type) { + bit_field_ = KeyTypeField::update(bit_field_, type); + } + void set_store_mode(KeyedAccessStoreMode mode) { + bit_field_ = StoreModeField::update(bit_field_, mode); + } + void set_type(Type* type) { type_ = type; } + + static int num_ids() { return parent_num_ids() + 4; } + BailoutId AssignmentId() const { return BailoutId(local_id(0)); } + BailoutId ToNumberId() const { return BailoutId(local_id(1)); } + TypeFeedbackId CountBinOpFeedbackId() const { + return TypeFeedbackId(local_id(2)); + } + TypeFeedbackId CountStoreFeedbackId() const { + return TypeFeedbackId(local_id(3)); + } + + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override; + FeedbackVectorSlot CountSlot() const { return slot_; } + + protected: + CountOperation(Zone* zone, Token::Value op, bool is_prefix, Expression* expr, + int pos) + : Expression(zone, pos), + bit_field_( + IsPrefixField::encode(is_prefix) | KeyTypeField::encode(ELEMENT) | + StoreModeField::encode(STANDARD_STORE) | TokenField::encode(op)), + type_(NULL), + expression_(expr) {} + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + class IsPrefixField : public BitField16<bool, 0, 1> {}; + class KeyTypeField : public BitField16<IcCheckType, 1, 1> {}; + class StoreModeField : public BitField16<KeyedAccessStoreMode, 2, 3> {}; + class TokenField : public BitField16<Token::Value, 5, 8> {}; + + // Starts with 16-bit field, which should get packed together with + // Expression's trailing 16-bit field. + uint16_t bit_field_; + Type* type_; + Expression* expression_; + SmallMapList receiver_types_; + FeedbackVectorSlot slot_; +}; + + +class CompareOperation final : public Expression { + public: + DECLARE_NODE_TYPE(CompareOperation) + + Token::Value op() const { return op_; } + Expression* left() const { return left_; } + Expression* right() const { return right_; } + + void set_left(Expression* e) { left_ = e; } + void set_right(Expression* e) { right_ = e; } + + // Type feedback information. + static int num_ids() { return parent_num_ids() + 1; } + TypeFeedbackId CompareOperationFeedbackId() const { + return TypeFeedbackId(local_id(0)); + } + Type* combined_type() const { return combined_type_; } + void set_combined_type(Type* type) { combined_type_ = type; } + + // Match special cases. + bool IsLiteralCompareTypeof(Expression** expr, Handle<String>* check); + bool IsLiteralCompareUndefined(Expression** expr, Isolate* isolate); + bool IsLiteralCompareNull(Expression** expr); + + protected: + CompareOperation(Zone* zone, Token::Value op, Expression* left, + Expression* right, int pos) + : Expression(zone, pos), + op_(op), + left_(left), + right_(right), + combined_type_(Type::None(zone)) { + DCHECK(Token::IsCompareOp(op)); + } + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Token::Value op_; + Expression* left_; + Expression* right_; + + Type* combined_type_; +}; + + +class Spread final : public Expression { + public: + DECLARE_NODE_TYPE(Spread) + + Expression* expression() const { return expression_; } + void set_expression(Expression* e) { expression_ = e; } + + static int num_ids() { return parent_num_ids(); } + + protected: + Spread(Zone* zone, Expression* expression, int pos) + : Expression(zone, pos), expression_(expression) {} + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Expression* expression_; +}; + + +class Conditional final : public Expression { + public: + DECLARE_NODE_TYPE(Conditional) + + Expression* condition() const { return condition_; } + Expression* then_expression() const { return then_expression_; } + Expression* else_expression() const { return else_expression_; } + + void set_condition(Expression* e) { condition_ = e; } + void set_then_expression(Expression* e) { then_expression_ = e; } + void set_else_expression(Expression* e) { else_expression_ = e; } + + void MarkTail() override { + then_expression_->MarkTail(); + else_expression_->MarkTail(); + } + + static int num_ids() { return parent_num_ids() + 2; } + BailoutId ThenId() const { return BailoutId(local_id(0)); } + BailoutId ElseId() const { return BailoutId(local_id(1)); } + + protected: + Conditional(Zone* zone, Expression* condition, Expression* then_expression, + Expression* else_expression, int position) + : Expression(zone, position), + condition_(condition), + then_expression_(then_expression), + else_expression_(else_expression) {} + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + Expression* condition_; + Expression* then_expression_; + Expression* else_expression_; +}; + + +class Assignment final : public Expression { + public: + DECLARE_NODE_TYPE(Assignment) + + Assignment* AsSimpleAssignment() { return !is_compound() ? this : NULL; } + + Token::Value binary_op() const; + + Token::Value op() const { return TokenField::decode(bit_field_); } + Expression* target() const { return target_; } + Expression* value() const { return value_; } + + void set_target(Expression* e) { target_ = e; } + void set_value(Expression* e) { value_ = e; } + + BinaryOperation* binary_operation() const { return binary_operation_; } + + // This check relies on the definition order of token in token.h. + bool is_compound() const { return op() > Token::ASSIGN; } + + static int num_ids() { return parent_num_ids() + 2; } + BailoutId AssignmentId() const { return BailoutId(local_id(0)); } + + // Type feedback information. + TypeFeedbackId AssignmentFeedbackId() { return TypeFeedbackId(local_id(1)); } + bool IsMonomorphic() override { return receiver_types_.length() == 1; } + bool IsUninitialized() const { + return IsUninitializedField::decode(bit_field_); + } + bool HasNoTypeInformation() { + return IsUninitializedField::decode(bit_field_); + } + SmallMapList* GetReceiverTypes() override { return &receiver_types_; } + IcCheckType GetKeyType() const override { + return KeyTypeField::decode(bit_field_); + } + KeyedAccessStoreMode GetStoreMode() const override { + return StoreModeField::decode(bit_field_); + } + void set_is_uninitialized(bool b) { + bit_field_ = IsUninitializedField::update(bit_field_, b); + } + void set_key_type(IcCheckType key_type) { + bit_field_ = KeyTypeField::update(bit_field_, key_type); + } + void set_store_mode(KeyedAccessStoreMode mode) { + bit_field_ = StoreModeField::update(bit_field_, mode); + } + + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override; + FeedbackVectorSlot AssignmentSlot() const { return slot_; } + + protected: + Assignment(Zone* zone, Token::Value op, Expression* target, Expression* value, + int pos); + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + class IsUninitializedField : public BitField16<bool, 0, 1> {}; + class KeyTypeField + : public BitField16<IcCheckType, IsUninitializedField::kNext, 1> {}; + class StoreModeField + : public BitField16<KeyedAccessStoreMode, KeyTypeField::kNext, 3> {}; + class TokenField : public BitField16<Token::Value, StoreModeField::kNext, 8> { + }; + + // Starts with 16-bit field, which should get packed together with + // Expression's trailing 16-bit field. + uint16_t bit_field_; + Expression* target_; + Expression* value_; + BinaryOperation* binary_operation_; + SmallMapList receiver_types_; + FeedbackVectorSlot slot_; +}; + + +class RewritableAssignmentExpression : public Expression { + public: + DECLARE_NODE_TYPE(RewritableAssignmentExpression) + + Expression* expression() { return expr_; } + bool is_rewritten() const { return is_rewritten_; } + + void set_expression(Expression* e) { expr_ = e; } + + void Rewrite(Expression* new_expression) { + DCHECK(!is_rewritten()); + DCHECK_NOT_NULL(new_expression); + expr_ = new_expression; + is_rewritten_ = true; + } + + static int num_ids() { return parent_num_ids(); } + + protected: + RewritableAssignmentExpression(Zone* zone, Expression* expression) + : Expression(zone, expression->position()), + is_rewritten_(false), + expr_(expression) {} + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + bool is_rewritten_; + Expression* expr_; +}; + + +class Yield final : public Expression { + public: + DECLARE_NODE_TYPE(Yield) + + enum Kind { + kInitial, // The initial yield that returns the unboxed generator object. + kSuspend, // A normal yield: { value: EXPRESSION, done: false } + kDelegating, // A yield*. + kFinal // A return: { value: EXPRESSION, done: true } + }; + + Expression* generator_object() const { return generator_object_; } + Expression* expression() const { return expression_; } + Kind yield_kind() const { return yield_kind_; } + + void set_generator_object(Expression* e) { generator_object_ = e; } + void set_expression(Expression* e) { expression_ = e; } + + // Type feedback information. + bool HasFeedbackSlots() const { return yield_kind() == kDelegating; } + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override { + if (HasFeedbackSlots()) { + yield_first_feedback_slot_ = spec->AddKeyedLoadICSlot(); + keyed_load_feedback_slot_ = spec->AddLoadICSlot(); + done_feedback_slot_ = spec->AddLoadICSlot(); + } + } + + FeedbackVectorSlot KeyedLoadFeedbackSlot() { + DCHECK(!HasFeedbackSlots() || !yield_first_feedback_slot_.IsInvalid()); + return yield_first_feedback_slot_; + } + + FeedbackVectorSlot DoneFeedbackSlot() { return keyed_load_feedback_slot_; } + + FeedbackVectorSlot ValueFeedbackSlot() { return done_feedback_slot_; } + + protected: + Yield(Zone* zone, Expression* generator_object, Expression* expression, + Kind yield_kind, int pos) + : Expression(zone, pos), + generator_object_(generator_object), + expression_(expression), + yield_kind_(yield_kind) {} + + private: + Expression* generator_object_; + Expression* expression_; + Kind yield_kind_; + FeedbackVectorSlot yield_first_feedback_slot_; + FeedbackVectorSlot keyed_load_feedback_slot_; + FeedbackVectorSlot done_feedback_slot_; +}; + + +class Throw final : public Expression { + public: + DECLARE_NODE_TYPE(Throw) + + Expression* exception() const { return exception_; } + void set_exception(Expression* e) { exception_ = e; } + + protected: + Throw(Zone* zone, Expression* exception, int pos) + : Expression(zone, pos), exception_(exception) {} + + private: + Expression* exception_; +}; + + +class FunctionLiteral final : public Expression { + public: + enum FunctionType { + kAnonymousExpression, + kNamedExpression, + kDeclaration, + kGlobalOrEval + }; + + enum ParameterFlag { kNoDuplicateParameters, kHasDuplicateParameters }; + + enum EagerCompileHint { kShouldEagerCompile, kShouldLazyCompile }; + + enum ArityRestriction { kNormalArity, kGetterArity, kSetterArity }; + + DECLARE_NODE_TYPE(FunctionLiteral) + + Handle<String> name() const { return raw_name_->string(); } + const AstString* raw_name() const { return raw_name_; } + void set_raw_name(const AstString* name) { raw_name_ = name; } + Scope* scope() const { return scope_; } + ZoneList<Statement*>* body() const { return body_; } + void set_function_token_position(int pos) { function_token_position_ = pos; } + int function_token_position() const { return function_token_position_; } + int start_position() const; + int end_position() const; + int SourceSize() const { return end_position() - start_position(); } + bool is_expression() const { return IsExpression::decode(bitfield_); } + bool is_anonymous() const { return IsAnonymous::decode(bitfield_); } + LanguageMode language_mode() const; + + static bool NeedsHomeObject(Expression* expr); + + int materialized_literal_count() { return materialized_literal_count_; } + int expected_property_count() { return expected_property_count_; } + int parameter_count() { return parameter_count_; } + + bool AllowsLazyCompilation(); + bool AllowsLazyCompilationWithoutContext(); + + Handle<String> debug_name() const { + if (raw_name_ != NULL && !raw_name_->IsEmpty()) { + return raw_name_->string(); + } + return inferred_name(); + } + + Handle<String> inferred_name() const { + if (!inferred_name_.is_null()) { + DCHECK(raw_inferred_name_ == NULL); + return inferred_name_; + } + if (raw_inferred_name_ != NULL) { + return raw_inferred_name_->string(); + } + UNREACHABLE(); + return Handle<String>(); + } + + // Only one of {set_inferred_name, set_raw_inferred_name} should be called. + void set_inferred_name(Handle<String> inferred_name) { + DCHECK(!inferred_name.is_null()); + inferred_name_ = inferred_name; + DCHECK(raw_inferred_name_== NULL || raw_inferred_name_->IsEmpty()); + raw_inferred_name_ = NULL; + } + + void set_raw_inferred_name(const AstString* raw_inferred_name) { + DCHECK(raw_inferred_name != NULL); + raw_inferred_name_ = raw_inferred_name; + DCHECK(inferred_name_.is_null()); + inferred_name_ = Handle<String>(); + } + + bool pretenure() const { return Pretenure::decode(bitfield_); } + void set_pretenure() { bitfield_ = Pretenure::update(bitfield_, true); } + + bool has_duplicate_parameters() const { + return HasDuplicateParameters::decode(bitfield_); + } + + bool is_function() const { return IsFunction::decode(bitfield_); } + + // This is used as a heuristic on when to eagerly compile a function + // literal. We consider the following constructs as hints that the + // function will be called immediately: + // - (function() { ... })(); + // - var x = function() { ... }(); + bool should_eager_compile() const { + return ShouldEagerCompile::decode(bitfield_); + } + void set_should_eager_compile() { + bitfield_ = ShouldEagerCompile::update(bitfield_, true); + } + + // A hint that we expect this function to be called (exactly) once, + // i.e. we suspect it's an initialization function. + bool should_be_used_once_hint() const { + return ShouldBeUsedOnceHint::decode(bitfield_); + } + void set_should_be_used_once_hint() { + bitfield_ = ShouldBeUsedOnceHint::update(bitfield_, true); + } + + FunctionKind kind() const { return FunctionKindBits::decode(bitfield_); } + + int ast_node_count() { return ast_properties_.node_count(); } + AstProperties::Flags flags() const { return ast_properties_.flags(); } + void set_ast_properties(AstProperties* ast_properties) { + ast_properties_ = *ast_properties; + } + const FeedbackVectorSpec* feedback_vector_spec() const { + return ast_properties_.get_spec(); + } + bool dont_optimize() { return dont_optimize_reason_ != kNoReason; } + BailoutReason dont_optimize_reason() { return dont_optimize_reason_; } + void set_dont_optimize_reason(BailoutReason reason) { + dont_optimize_reason_ = reason; + } + + protected: + FunctionLiteral(Zone* zone, const AstString* name, + AstValueFactory* ast_value_factory, Scope* scope, + ZoneList<Statement*>* body, int materialized_literal_count, + int expected_property_count, int parameter_count, + FunctionType function_type, + ParameterFlag has_duplicate_parameters, + EagerCompileHint eager_compile_hint, FunctionKind kind, + int position) + : Expression(zone, position), + raw_name_(name), + scope_(scope), + body_(body), + raw_inferred_name_(ast_value_factory->empty_string()), + ast_properties_(zone), + dont_optimize_reason_(kNoReason), + materialized_literal_count_(materialized_literal_count), + expected_property_count_(expected_property_count), + parameter_count_(parameter_count), + function_token_position_(RelocInfo::kNoPosition) { + bitfield_ = + IsExpression::encode(function_type != kDeclaration) | + IsAnonymous::encode(function_type == kAnonymousExpression) | + Pretenure::encode(false) | + HasDuplicateParameters::encode(has_duplicate_parameters == + kHasDuplicateParameters) | + IsFunction::encode(function_type != kGlobalOrEval) | + ShouldEagerCompile::encode(eager_compile_hint == kShouldEagerCompile) | + FunctionKindBits::encode(kind) | ShouldBeUsedOnceHint::encode(false); + DCHECK(IsValidFunctionKind(kind)); + } + + private: + class IsExpression : public BitField16<bool, 0, 1> {}; + class IsAnonymous : public BitField16<bool, 1, 1> {}; + class Pretenure : public BitField16<bool, 2, 1> {}; + class HasDuplicateParameters : public BitField16<bool, 3, 1> {}; + class IsFunction : public BitField16<bool, 4, 1> {}; + class ShouldEagerCompile : public BitField16<bool, 5, 1> {}; + class FunctionKindBits : public BitField16<FunctionKind, 6, 8> {}; + class ShouldBeUsedOnceHint : public BitField16<bool, 15, 1> {}; + + // Start with 16-bit field, which should get packed together + // with Expression's trailing 16-bit field. + uint16_t bitfield_; + + const AstString* raw_name_; + Scope* scope_; + ZoneList<Statement*>* body_; + const AstString* raw_inferred_name_; + Handle<String> inferred_name_; + AstProperties ast_properties_; + BailoutReason dont_optimize_reason_; + + int materialized_literal_count_; + int expected_property_count_; + int parameter_count_; + int function_token_position_; +}; + + +class ClassLiteral final : public Expression { + public: + typedef ObjectLiteralProperty Property; + + DECLARE_NODE_TYPE(ClassLiteral) + + Handle<String> name() const { return raw_name_->string(); } + const AstRawString* raw_name() const { return raw_name_; } + void set_raw_name(const AstRawString* name) { + DCHECK_NULL(raw_name_); + raw_name_ = name; + } + + Scope* scope() const { return scope_; } + VariableProxy* class_variable_proxy() const { return class_variable_proxy_; } + Expression* extends() const { return extends_; } + void set_extends(Expression* e) { extends_ = e; } + FunctionLiteral* constructor() const { return constructor_; } + void set_constructor(FunctionLiteral* f) { constructor_ = f; } + ZoneList<Property*>* properties() const { return properties_; } + int start_position() const { return position(); } + int end_position() const { return end_position_; } + + BailoutId EntryId() const { return BailoutId(local_id(0)); } + BailoutId DeclsId() const { return BailoutId(local_id(1)); } + BailoutId ExitId() { return BailoutId(local_id(2)); } + BailoutId CreateLiteralId() const { return BailoutId(local_id(3)); } + + // Return an AST id for a property that is used in simulate instructions. + BailoutId GetIdForProperty(int i) { return BailoutId(local_id(i + 4)); } + + // Unlike other AST nodes, this number of bailout IDs allocated for an + // ClassLiteral can vary, so num_ids() is not a static method. + int num_ids() const { return parent_num_ids() + 4 + properties()->length(); } + + // Object literals need one feedback slot for each non-trivial value, as well + // as some slots for home objects. + void AssignFeedbackVectorSlots(Isolate* isolate, FeedbackVectorSpec* spec, + FeedbackVectorSlotCache* cache) override; + + bool NeedsProxySlot() const { + return class_variable_proxy() != nullptr && + class_variable_proxy()->var()->IsUnallocated(); + } + + FeedbackVectorSlot ProxySlot() const { return slot_; } + + protected: + ClassLiteral(Zone* zone, const AstRawString* name, Scope* scope, + VariableProxy* class_variable_proxy, Expression* extends, + FunctionLiteral* constructor, ZoneList<Property*>* properties, + int start_position, int end_position) + : Expression(zone, start_position), + raw_name_(name), + scope_(scope), + class_variable_proxy_(class_variable_proxy), + extends_(extends), + constructor_(constructor), + properties_(properties), + end_position_(end_position) {} + + static int parent_num_ids() { return Expression::num_ids(); } + + private: + int local_id(int n) const { return base_id() + parent_num_ids() + n; } + + const AstRawString* raw_name_; + Scope* scope_; + VariableProxy* class_variable_proxy_; + Expression* extends_; + FunctionLiteral* constructor_; + ZoneList<Property*>* properties_; + int end_position_; + FeedbackVectorSlot slot_; +}; + + +class NativeFunctionLiteral final : public Expression { + public: + DECLARE_NODE_TYPE(NativeFunctionLiteral) + + Handle<String> name() const { return name_->string(); } + v8::Extension* extension() const { return extension_; } + + protected: + NativeFunctionLiteral(Zone* zone, const AstRawString* name, + v8::Extension* extension, int pos) + : Expression(zone, pos), name_(name), extension_(extension) {} + + private: + const AstRawString* name_; + v8::Extension* extension_; +}; + + +class ThisFunction final : public Expression { + public: + DECLARE_NODE_TYPE(ThisFunction) + + protected: + ThisFunction(Zone* zone, int pos) : Expression(zone, pos) {} +}; + + +class SuperPropertyReference final : public Expression { + public: + DECLARE_NODE_TYPE(SuperPropertyReference) + + VariableProxy* this_var() const { return this_var_; } + void set_this_var(VariableProxy* v) { this_var_ = v; } + Expression* home_object() const { return home_object_; } + void set_home_object(Expression* e) { home_object_ = e; } + + protected: + SuperPropertyReference(Zone* zone, VariableProxy* this_var, + Expression* home_object, int pos) + : Expression(zone, pos), this_var_(this_var), home_object_(home_object) { + DCHECK(this_var->is_this()); + DCHECK(home_object->IsProperty()); + } + + private: + VariableProxy* this_var_; + Expression* home_object_; +}; + + +class SuperCallReference final : public Expression { + public: + DECLARE_NODE_TYPE(SuperCallReference) + + VariableProxy* this_var() const { return this_var_; } + void set_this_var(VariableProxy* v) { this_var_ = v; } + VariableProxy* new_target_var() const { return new_target_var_; } + void set_new_target_var(VariableProxy* v) { new_target_var_ = v; } + VariableProxy* this_function_var() const { return this_function_var_; } + void set_this_function_var(VariableProxy* v) { this_function_var_ = v; } + + protected: + SuperCallReference(Zone* zone, VariableProxy* this_var, + VariableProxy* new_target_var, + VariableProxy* this_function_var, int pos) + : Expression(zone, pos), + this_var_(this_var), + new_target_var_(new_target_var), + this_function_var_(this_function_var) { + DCHECK(this_var->is_this()); + DCHECK(new_target_var->raw_name()->IsOneByteEqualTo(".new.target")); + DCHECK(this_function_var->raw_name()->IsOneByteEqualTo(".this_function")); + } + + private: + VariableProxy* this_var_; + VariableProxy* new_target_var_; + VariableProxy* this_function_var_; +}; + + +// This class is produced when parsing the () in arrow functions without any +// arguments and is not actually a valid expression. +class EmptyParentheses final : public Expression { + public: + DECLARE_NODE_TYPE(EmptyParentheses) + + private: + EmptyParentheses(Zone* zone, int pos) : Expression(zone, pos) {} +}; + + +#undef DECLARE_NODE_TYPE + + +// ---------------------------------------------------------------------------- +// Basic visitor +// - leaf node visitors are abstract. + +class AstVisitor BASE_EMBEDDED { + public: + AstVisitor() {} + virtual ~AstVisitor() {} + + // Stack overflow check and dynamic dispatch. + virtual void Visit(AstNode* node) = 0; + + // Iteration left-to-right. + virtual void VisitDeclarations(ZoneList<Declaration*>* declarations); + virtual void VisitStatements(ZoneList<Statement*>* statements); + virtual void VisitExpressions(ZoneList<Expression*>* expressions); + + // Individual AST nodes. +#define DEF_VISIT(type) \ + virtual void Visit##type(type* node) = 0; + AST_NODE_LIST(DEF_VISIT) +#undef DEF_VISIT +}; + +#define DEFINE_AST_VISITOR_SUBCLASS_MEMBERS() \ + public: \ + void Visit(AstNode* node) final { \ + if (!CheckStackOverflow()) node->Accept(this); \ + } \ + \ + void SetStackOverflow() { stack_overflow_ = true; } \ + void ClearStackOverflow() { stack_overflow_ = false; } \ + bool HasStackOverflow() const { return stack_overflow_; } \ + \ + bool CheckStackOverflow() { \ + if (stack_overflow_) return true; \ + if (GetCurrentStackPosition() < stack_limit_) { \ + stack_overflow_ = true; \ + return true; \ + } \ + return false; \ + } \ + \ + private: \ + void InitializeAstVisitor(Isolate* isolate) { \ + stack_limit_ = isolate->stack_guard()->real_climit(); \ + stack_overflow_ = false; \ + } \ + \ + void InitializeAstVisitor(uintptr_t stack_limit) { \ + stack_limit_ = stack_limit; \ + stack_overflow_ = false; \ + } \ + \ + uintptr_t stack_limit_; \ + bool stack_overflow_ + +#define DEFINE_AST_REWRITER_SUBCLASS_MEMBERS() \ + public: \ + AstNode* Rewrite(AstNode* node) { \ + DCHECK_NULL(replacement_); \ + DCHECK_NOT_NULL(node); \ + Visit(node); \ + if (HasStackOverflow()) return node; \ + if (replacement_ == nullptr) return node; \ + AstNode* result = replacement_; \ + replacement_ = nullptr; \ + return result; \ + } \ + \ + private: \ + void InitializeAstRewriter(Isolate* isolate) { \ + InitializeAstVisitor(isolate); \ + replacement_ = nullptr; \ + } \ + \ + void InitializeAstRewriter(uintptr_t stack_limit) { \ + InitializeAstVisitor(stack_limit); \ + replacement_ = nullptr; \ + } \ + \ + DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); \ + \ + protected: \ + AstNode* replacement_ + +// Generic macro for rewriting things; `GET` is the expression to be +// rewritten; `SET` is a command that should do the rewriting, i.e. +// something sensible with the variable called `replacement`. +#define AST_REWRITE(Type, GET, SET) \ + do { \ + DCHECK(!HasStackOverflow()); \ + DCHECK_NULL(replacement_); \ + Visit(GET); \ + if (HasStackOverflow()) return; \ + if (replacement_ == nullptr) break; \ + Type* replacement = reinterpret_cast<Type*>(replacement_); \ + do { \ + SET; \ + } while (false); \ + replacement_ = nullptr; \ + } while (false) + +// Macro for rewriting object properties; it assumes that `object` has +// `property` with a public getter and setter. +#define AST_REWRITE_PROPERTY(Type, object, property) \ + do { \ + auto _obj = (object); \ + AST_REWRITE(Type, _obj->property(), _obj->set_##property(replacement)); \ + } while (false) + +// Macro for rewriting list elements; it assumes that `list` has methods +// `at` and `Set`. +#define AST_REWRITE_LIST_ELEMENT(Type, list, index) \ + do { \ + auto _list = (list); \ + auto _index = (index); \ + AST_REWRITE(Type, _list->at(_index), _list->Set(_index, replacement)); \ + } while (false) + + +// ---------------------------------------------------------------------------- +// AstNode factory + +class AstNodeFactory final BASE_EMBEDDED { + public: + explicit AstNodeFactory(AstValueFactory* ast_value_factory) + : local_zone_(ast_value_factory->zone()), + parser_zone_(ast_value_factory->zone()), + ast_value_factory_(ast_value_factory) {} + + AstValueFactory* ast_value_factory() const { return ast_value_factory_; } + + VariableDeclaration* NewVariableDeclaration( + VariableProxy* proxy, VariableMode mode, Scope* scope, int pos, + bool is_class_declaration = false, int declaration_group_start = -1) { + return new (parser_zone_) + VariableDeclaration(parser_zone_, proxy, mode, scope, pos, + is_class_declaration, declaration_group_start); + } + + FunctionDeclaration* NewFunctionDeclaration(VariableProxy* proxy, + VariableMode mode, + FunctionLiteral* fun, + Scope* scope, + int pos) { + return new (parser_zone_) + FunctionDeclaration(parser_zone_, proxy, mode, fun, scope, pos); + } + + ImportDeclaration* NewImportDeclaration(VariableProxy* proxy, + const AstRawString* import_name, + const AstRawString* module_specifier, + Scope* scope, int pos) { + return new (parser_zone_) ImportDeclaration( + parser_zone_, proxy, import_name, module_specifier, scope, pos); + } + + ExportDeclaration* NewExportDeclaration(VariableProxy* proxy, + Scope* scope, + int pos) { + return new (parser_zone_) + ExportDeclaration(parser_zone_, proxy, scope, pos); + } + + Block* NewBlock(ZoneList<const AstRawString*>* labels, int capacity, + bool ignore_completion_value, int pos) { + return new (local_zone_) + Block(local_zone_, labels, capacity, ignore_completion_value, pos); + } + +#define STATEMENT_WITH_LABELS(NodeType) \ + NodeType* New##NodeType(ZoneList<const AstRawString*>* labels, int pos) { \ + return new (local_zone_) NodeType(local_zone_, labels, pos); \ + } + STATEMENT_WITH_LABELS(DoWhileStatement) + STATEMENT_WITH_LABELS(WhileStatement) + STATEMENT_WITH_LABELS(ForStatement) + STATEMENT_WITH_LABELS(SwitchStatement) +#undef STATEMENT_WITH_LABELS + + ForEachStatement* NewForEachStatement(ForEachStatement::VisitMode visit_mode, + ZoneList<const AstRawString*>* labels, + int pos) { + switch (visit_mode) { + case ForEachStatement::ENUMERATE: { + return new (local_zone_) ForInStatement(local_zone_, labels, pos); + } + case ForEachStatement::ITERATE: { + return new (local_zone_) ForOfStatement(local_zone_, labels, pos); + } + } + UNREACHABLE(); + return NULL; + } + + ExpressionStatement* NewExpressionStatement(Expression* expression, int pos) { + return new (local_zone_) ExpressionStatement(local_zone_, expression, pos); + } + + ContinueStatement* NewContinueStatement(IterationStatement* target, int pos) { + return new (local_zone_) ContinueStatement(local_zone_, target, pos); + } + + BreakStatement* NewBreakStatement(BreakableStatement* target, int pos) { + return new (local_zone_) BreakStatement(local_zone_, target, pos); + } + + ReturnStatement* NewReturnStatement(Expression* expression, int pos) { + return new (local_zone_) ReturnStatement(local_zone_, expression, pos); + } + + WithStatement* NewWithStatement(Scope* scope, + Expression* expression, + Statement* statement, + int pos) { + return new (local_zone_) + WithStatement(local_zone_, scope, expression, statement, pos); + } + + IfStatement* NewIfStatement(Expression* condition, + Statement* then_statement, + Statement* else_statement, + int pos) { + return new (local_zone_) IfStatement(local_zone_, condition, then_statement, + else_statement, pos); + } + + TryCatchStatement* NewTryCatchStatement(Block* try_block, Scope* scope, + Variable* variable, + Block* catch_block, int pos) { + return new (local_zone_) TryCatchStatement(local_zone_, try_block, scope, + variable, catch_block, pos); + } + + TryFinallyStatement* NewTryFinallyStatement(Block* try_block, + Block* finally_block, int pos) { + return new (local_zone_) + TryFinallyStatement(local_zone_, try_block, finally_block, pos); + } + + DebuggerStatement* NewDebuggerStatement(int pos) { + return new (local_zone_) DebuggerStatement(local_zone_, pos); + } + + EmptyStatement* NewEmptyStatement(int pos) { + return new (local_zone_) EmptyStatement(local_zone_, pos); + } + + SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement( + Statement* statement, Scope* scope) { + return new (parser_zone_) + SloppyBlockFunctionStatement(parser_zone_, statement, scope); + } + + CaseClause* NewCaseClause( + Expression* label, ZoneList<Statement*>* statements, int pos) { + return new (local_zone_) CaseClause(local_zone_, label, statements, pos); + } + + Literal* NewStringLiteral(const AstRawString* string, int pos) { + return new (local_zone_) + Literal(local_zone_, ast_value_factory_->NewString(string), pos); + } + + // A JavaScript symbol (ECMA-262 edition 6). + Literal* NewSymbolLiteral(const char* name, int pos) { + return new (local_zone_) + Literal(local_zone_, ast_value_factory_->NewSymbol(name), pos); + } + + Literal* NewNumberLiteral(double number, int pos, bool with_dot = false) { + return new (local_zone_) Literal( + local_zone_, ast_value_factory_->NewNumber(number, with_dot), pos); + } + + Literal* NewSmiLiteral(int number, int pos) { + return new (local_zone_) + Literal(local_zone_, ast_value_factory_->NewSmi(number), pos); + } + + Literal* NewBooleanLiteral(bool b, int pos) { + return new (local_zone_) + Literal(local_zone_, ast_value_factory_->NewBoolean(b), pos); + } + + Literal* NewNullLiteral(int pos) { + return new (local_zone_) + Literal(local_zone_, ast_value_factory_->NewNull(), pos); + } + + Literal* NewUndefinedLiteral(int pos) { + return new (local_zone_) + Literal(local_zone_, ast_value_factory_->NewUndefined(), pos); + } + + Literal* NewTheHoleLiteral(int pos) { + return new (local_zone_) + Literal(local_zone_, ast_value_factory_->NewTheHole(), pos); + } + + ObjectLiteral* NewObjectLiteral( + ZoneList<ObjectLiteral::Property*>* properties, + int literal_index, + int boilerplate_properties, + bool has_function, + bool is_strong, + int pos) { + return new (local_zone_) + ObjectLiteral(local_zone_, properties, literal_index, + boilerplate_properties, has_function, is_strong, pos); + } + + ObjectLiteral::Property* NewObjectLiteralProperty( + Expression* key, Expression* value, ObjectLiteralProperty::Kind kind, + bool is_static, bool is_computed_name) { + return new (local_zone_) + ObjectLiteral::Property(key, value, kind, is_static, is_computed_name); + } + + ObjectLiteral::Property* NewObjectLiteralProperty(Expression* key, + Expression* value, + bool is_static, + bool is_computed_name) { + return new (local_zone_) ObjectLiteral::Property( + ast_value_factory_, key, value, is_static, is_computed_name); + } + + RegExpLiteral* NewRegExpLiteral(const AstRawString* pattern, int flags, + int literal_index, bool is_strong, int pos) { + return new (local_zone_) RegExpLiteral(local_zone_, pattern, flags, + literal_index, is_strong, pos); + } + + ArrayLiteral* NewArrayLiteral(ZoneList<Expression*>* values, + int literal_index, + bool is_strong, + int pos) { + return new (local_zone_) + ArrayLiteral(local_zone_, values, -1, literal_index, is_strong, pos); + } + + ArrayLiteral* NewArrayLiteral(ZoneList<Expression*>* values, + int first_spread_index, int literal_index, + bool is_strong, int pos) { + return new (local_zone_) ArrayLiteral( + local_zone_, values, first_spread_index, literal_index, is_strong, pos); + } + + VariableProxy* NewVariableProxy(Variable* var, + int start_position = RelocInfo::kNoPosition, + int end_position = RelocInfo::kNoPosition) { + return new (parser_zone_) + VariableProxy(parser_zone_, var, start_position, end_position); + } + + VariableProxy* NewVariableProxy(const AstRawString* name, + Variable::Kind variable_kind, + int start_position = RelocInfo::kNoPosition, + int end_position = RelocInfo::kNoPosition) { + DCHECK_NOT_NULL(name); + return new (parser_zone_) VariableProxy(parser_zone_, name, variable_kind, + start_position, end_position); + } + + Property* NewProperty(Expression* obj, Expression* key, int pos) { + return new (local_zone_) Property(local_zone_, obj, key, pos); + } + + Call* NewCall(Expression* expression, + ZoneList<Expression*>* arguments, + int pos) { + return new (local_zone_) Call(local_zone_, expression, arguments, pos); + } + + CallNew* NewCallNew(Expression* expression, + ZoneList<Expression*>* arguments, + int pos) { + return new (local_zone_) CallNew(local_zone_, expression, arguments, pos); + } + + CallRuntime* NewCallRuntime(Runtime::FunctionId id, + ZoneList<Expression*>* arguments, int pos) { + return new (local_zone_) + CallRuntime(local_zone_, Runtime::FunctionForId(id), arguments, pos); + } + + CallRuntime* NewCallRuntime(const Runtime::Function* function, + ZoneList<Expression*>* arguments, int pos) { + return new (local_zone_) CallRuntime(local_zone_, function, arguments, pos); + } + + CallRuntime* NewCallRuntime(int context_index, + ZoneList<Expression*>* arguments, int pos) { + return new (local_zone_) + CallRuntime(local_zone_, context_index, arguments, pos); + } + + UnaryOperation* NewUnaryOperation(Token::Value op, + Expression* expression, + int pos) { + return new (local_zone_) UnaryOperation(local_zone_, op, expression, pos); + } + + BinaryOperation* NewBinaryOperation(Token::Value op, + Expression* left, + Expression* right, + int pos) { + return new (local_zone_) BinaryOperation(local_zone_, op, left, right, pos); + } + + CountOperation* NewCountOperation(Token::Value op, + bool is_prefix, + Expression* expr, + int pos) { + return new (local_zone_) + CountOperation(local_zone_, op, is_prefix, expr, pos); + } + + CompareOperation* NewCompareOperation(Token::Value op, + Expression* left, + Expression* right, + int pos) { + return new (local_zone_) + CompareOperation(local_zone_, op, left, right, pos); + } + + Spread* NewSpread(Expression* expression, int pos) { + return new (local_zone_) Spread(local_zone_, expression, pos); + } + + Conditional* NewConditional(Expression* condition, + Expression* then_expression, + Expression* else_expression, + int position) { + return new (local_zone_) Conditional( + local_zone_, condition, then_expression, else_expression, position); + } + + RewritableAssignmentExpression* NewRewritableAssignmentExpression( + Expression* expression) { + DCHECK_NOT_NULL(expression); + DCHECK(expression->IsAssignment()); + return new (local_zone_) + RewritableAssignmentExpression(local_zone_, expression); + } + + Assignment* NewAssignment(Token::Value op, + Expression* target, + Expression* value, + int pos) { + DCHECK(Token::IsAssignmentOp(op)); + Assignment* assign = + new (local_zone_) Assignment(local_zone_, op, target, value, pos); + if (assign->is_compound()) { + DCHECK(Token::IsAssignmentOp(op)); + assign->binary_operation_ = + NewBinaryOperation(assign->binary_op(), target, value, pos + 1); + } + return assign; + } + + Yield* NewYield(Expression *generator_object, + Expression* expression, + Yield::Kind yield_kind, + int pos) { + if (!expression) expression = NewUndefinedLiteral(pos); + return new (local_zone_) + Yield(local_zone_, generator_object, expression, yield_kind, pos); + } + + Throw* NewThrow(Expression* exception, int pos) { + return new (local_zone_) Throw(local_zone_, exception, pos); + } + + FunctionLiteral* NewFunctionLiteral( + const AstRawString* name, Scope* scope, ZoneList<Statement*>* body, + int materialized_literal_count, int expected_property_count, + int parameter_count, + FunctionLiteral::ParameterFlag has_duplicate_parameters, + FunctionLiteral::FunctionType function_type, + FunctionLiteral::EagerCompileHint eager_compile_hint, FunctionKind kind, + int position) { + return new (parser_zone_) FunctionLiteral( + parser_zone_, name, ast_value_factory_, scope, body, + materialized_literal_count, expected_property_count, parameter_count, + function_type, has_duplicate_parameters, eager_compile_hint, kind, + position); + } + + ClassLiteral* NewClassLiteral(const AstRawString* name, Scope* scope, + VariableProxy* proxy, Expression* extends, + FunctionLiteral* constructor, + ZoneList<ObjectLiteral::Property*>* properties, + int start_position, int end_position) { + return new (parser_zone_) + ClassLiteral(parser_zone_, name, scope, proxy, extends, constructor, + properties, start_position, end_position); + } + + NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name, + v8::Extension* extension, + int pos) { + return new (parser_zone_) + NativeFunctionLiteral(parser_zone_, name, extension, pos); + } + + DoExpression* NewDoExpression(Block* block, Variable* result_var, int pos) { + VariableProxy* result = NewVariableProxy(result_var, pos); + return new (parser_zone_) DoExpression(parser_zone_, block, result, pos); + } + + ThisFunction* NewThisFunction(int pos) { + return new (local_zone_) ThisFunction(local_zone_, pos); + } + + SuperPropertyReference* NewSuperPropertyReference(VariableProxy* this_var, + Expression* home_object, + int pos) { + return new (parser_zone_) + SuperPropertyReference(parser_zone_, this_var, home_object, pos); + } + + SuperCallReference* NewSuperCallReference(VariableProxy* this_var, + VariableProxy* new_target_var, + VariableProxy* this_function_var, + int pos) { + return new (parser_zone_) SuperCallReference( + parser_zone_, this_var, new_target_var, this_function_var, pos); + } + + EmptyParentheses* NewEmptyParentheses(int pos) { + return new (local_zone_) EmptyParentheses(local_zone_, pos); + } + + Zone* zone() const { return local_zone_; } + + // Handles use of temporary zones when parsing inner function bodies. + class BodyScope { + public: + BodyScope(AstNodeFactory* factory, Zone* temp_zone, bool use_temp_zone) + : factory_(factory), prev_zone_(factory->local_zone_) { + if (use_temp_zone) { + factory->local_zone_ = temp_zone; + } + } + + ~BodyScope() { factory_->local_zone_ = prev_zone_; } + + private: + AstNodeFactory* factory_; + Zone* prev_zone_; + }; + + private: + // This zone may be deallocated upon returning from parsing a function body + // which we can guarantee is not going to be compiled or have its AST + // inspected. + // See ParseFunctionLiteral in parser.cc for preconditions. + Zone* local_zone_; + // ZoneObjects which need to persist until scope analysis must be allocated in + // the parser-level zone. + Zone* parser_zone_; + AstValueFactory* ast_value_factory_; +}; + + +} // namespace internal +} // namespace v8 + +#endif // V8_AST_AST_H_ diff --git a/deps/v8/src/ast/modules.cc b/deps/v8/src/ast/modules.cc new file mode 100644 index 0000000000..225cd8d62c --- /dev/null +++ b/deps/v8/src/ast/modules.cc @@ -0,0 +1,59 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/modules.h" + +#include "src/ast/ast-value-factory.h" + +namespace v8 { +namespace internal { + + +void ModuleDescriptor::AddLocalExport(const AstRawString* export_name, + const AstRawString* local_name, + Zone* zone, bool* ok) { + DCHECK(!IsFrozen()); + void* key = const_cast<AstRawString*>(export_name); + + ZoneAllocationPolicy allocator(zone); + + if (exports_ == nullptr) { + exports_ = new (zone->New(sizeof(ZoneHashMap))) + ZoneHashMap(ZoneHashMap::PointersMatch, + ZoneHashMap::kDefaultHashMapCapacity, allocator); + } + + ZoneHashMap::Entry* p = + exports_->LookupOrInsert(key, export_name->hash(), allocator); + DCHECK_NOT_NULL(p); + if (p->value != nullptr) { + // Duplicate export. + *ok = false; + return; + } + + p->value = const_cast<AstRawString*>(local_name); +} + + +void ModuleDescriptor::AddModuleRequest(const AstRawString* module_specifier, + Zone* zone) { + // TODO(adamk): Avoid this O(N) operation on each insert by storing + // a HashMap, or by de-duping after parsing. + if (requested_modules_.Contains(module_specifier)) return; + requested_modules_.Add(module_specifier, zone); +} + + +const AstRawString* ModuleDescriptor::LookupLocalExport( + const AstRawString* export_name, Zone* zone) { + if (exports_ == nullptr) return nullptr; + ZoneHashMap::Entry* entry = exports_->Lookup( + const_cast<AstRawString*>(export_name), export_name->hash()); + if (entry == nullptr) return nullptr; + DCHECK_NOT_NULL(entry->value); + return static_cast<const AstRawString*>(entry->value); +} +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/modules.h b/deps/v8/src/ast/modules.h new file mode 100644 index 0000000000..e3c66dce94 --- /dev/null +++ b/deps/v8/src/ast/modules.h @@ -0,0 +1,121 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_MODULES_H_ +#define V8_AST_MODULES_H_ + +#include "src/zone.h" + +namespace v8 { +namespace internal { + + +class AstRawString; + + +class ModuleDescriptor : public ZoneObject { + public: + // --------------------------------------------------------------------------- + // Factory methods. + + static ModuleDescriptor* New(Zone* zone) { + return new (zone) ModuleDescriptor(zone); + } + + // --------------------------------------------------------------------------- + // Mutators. + + // Add a name to the list of exports. If it already exists, or this descriptor + // is frozen, that's an error. + void AddLocalExport(const AstRawString* export_name, + const AstRawString* local_name, Zone* zone, bool* ok); + + // Add module_specifier to the list of requested modules, + // if not already present. + void AddModuleRequest(const AstRawString* module_specifier, Zone* zone); + + // Do not allow any further refinements, directly or through unification. + void Freeze() { frozen_ = true; } + + // Assign an index. + void Allocate(int index) { + DCHECK(IsFrozen() && index_ == -1); + index_ = index; + } + + // --------------------------------------------------------------------------- + // Accessors. + + // Check whether this is closed (i.e. fully determined). + bool IsFrozen() { return frozen_; } + + int Length() { + DCHECK(IsFrozen()); + ZoneHashMap* exports = exports_; + return exports ? exports->occupancy() : 0; + } + + // The context slot in the hosting script context pointing to this module. + int Index() { + DCHECK(IsFrozen()); + return index_; + } + + const AstRawString* LookupLocalExport(const AstRawString* export_name, + Zone* zone); + + const ZoneList<const AstRawString*>& requested_modules() const { + return requested_modules_; + } + + // --------------------------------------------------------------------------- + // Iterators. + + // Use like: + // for (auto it = descriptor->iterator(); !it.done(); it.Advance()) { + // ... it.name() ... + // } + class Iterator { + public: + bool done() const { return entry_ == NULL; } + const AstRawString* export_name() const { + DCHECK(!done()); + return static_cast<const AstRawString*>(entry_->key); + } + const AstRawString* local_name() const { + DCHECK(!done()); + return static_cast<const AstRawString*>(entry_->value); + } + void Advance() { entry_ = exports_->Next(entry_); } + + private: + friend class ModuleDescriptor; + explicit Iterator(const ZoneHashMap* exports) + : exports_(exports), entry_(exports ? exports->Start() : NULL) {} + + const ZoneHashMap* exports_; + ZoneHashMap::Entry* entry_; + }; + + Iterator iterator() const { return Iterator(this->exports_); } + + // --------------------------------------------------------------------------- + // Implementation. + private: + explicit ModuleDescriptor(Zone* zone) + : frozen_(false), + exports_(NULL), + requested_modules_(1, zone), + index_(-1) {} + + bool frozen_; + ZoneHashMap* exports_; // Module exports and their types (allocated lazily) + ZoneList<const AstRawString*> requested_modules_; + int index_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_AST_MODULES_H_ diff --git a/deps/v8/src/ast/prettyprinter.cc b/deps/v8/src/ast/prettyprinter.cc new file mode 100644 index 0000000000..1f6b8c31de --- /dev/null +++ b/deps/v8/src/ast/prettyprinter.cc @@ -0,0 +1,1698 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/prettyprinter.h" + +#include <stdarg.h> + +#include "src/ast/ast-value-factory.h" +#include "src/ast/scopes.h" +#include "src/base/platform/platform.h" + +namespace v8 { +namespace internal { + +CallPrinter::CallPrinter(Isolate* isolate, bool is_builtin) { + output_ = NULL; + size_ = 0; + pos_ = 0; + position_ = 0; + found_ = false; + done_ = false; + is_builtin_ = is_builtin; + InitializeAstVisitor(isolate); +} + + +CallPrinter::~CallPrinter() { DeleteArray(output_); } + + +const char* CallPrinter::Print(FunctionLiteral* program, int position) { + Init(); + position_ = position; + Find(program); + return output_; +} + + +void CallPrinter::Find(AstNode* node, bool print) { + if (done_) return; + if (found_) { + if (print) { + int start = pos_; + Visit(node); + if (start != pos_) return; + } + Print("(intermediate value)"); + } else { + Visit(node); + } +} + + +void CallPrinter::Init() { + if (size_ == 0) { + DCHECK(output_ == NULL); + const int initial_size = 256; + output_ = NewArray<char>(initial_size); + size_ = initial_size; + } + output_[0] = '\0'; + pos_ = 0; +} + + +void CallPrinter::Print(const char* format, ...) { + if (!found_ || done_) return; + for (;;) { + va_list arguments; + va_start(arguments, format); + int n = VSNPrintF(Vector<char>(output_, size_) + pos_, format, arguments); + va_end(arguments); + + if (n >= 0) { + // there was enough space - we are done + pos_ += n; + return; + } else { + // there was not enough space - allocate more and try again + const int slack = 32; + int new_size = size_ + (size_ >> 1) + slack; + char* new_output = NewArray<char>(new_size); + MemCopy(new_output, output_, pos_); + DeleteArray(output_); + output_ = new_output; + size_ = new_size; + } + } +} + + +void CallPrinter::VisitBlock(Block* node) { + FindStatements(node->statements()); +} + + +void CallPrinter::VisitVariableDeclaration(VariableDeclaration* node) {} + + +void CallPrinter::VisitFunctionDeclaration(FunctionDeclaration* node) {} + + +void CallPrinter::VisitImportDeclaration(ImportDeclaration* node) { +} + + +void CallPrinter::VisitExportDeclaration(ExportDeclaration* node) {} + + +void CallPrinter::VisitExpressionStatement(ExpressionStatement* node) { + Find(node->expression()); +} + + +void CallPrinter::VisitEmptyStatement(EmptyStatement* node) {} + + +void CallPrinter::VisitSloppyBlockFunctionStatement( + SloppyBlockFunctionStatement* node) { + Find(node->statement()); +} + + +void CallPrinter::VisitIfStatement(IfStatement* node) { + Find(node->condition()); + Find(node->then_statement()); + if (node->HasElseStatement()) { + Find(node->else_statement()); + } +} + + +void CallPrinter::VisitContinueStatement(ContinueStatement* node) {} + + +void CallPrinter::VisitBreakStatement(BreakStatement* node) {} + + +void CallPrinter::VisitReturnStatement(ReturnStatement* node) { + Find(node->expression()); +} + + +void CallPrinter::VisitWithStatement(WithStatement* node) { + Find(node->expression()); + Find(node->statement()); +} + + +void CallPrinter::VisitSwitchStatement(SwitchStatement* node) { + Find(node->tag()); + ZoneList<CaseClause*>* cases = node->cases(); + for (int i = 0; i < cases->length(); i++) Find(cases->at(i)); +} + + +void CallPrinter::VisitCaseClause(CaseClause* clause) { + if (!clause->is_default()) { + Find(clause->label()); + } + FindStatements(clause->statements()); +} + + +void CallPrinter::VisitDoWhileStatement(DoWhileStatement* node) { + Find(node->body()); + Find(node->cond()); +} + + +void CallPrinter::VisitWhileStatement(WhileStatement* node) { + Find(node->cond()); + Find(node->body()); +} + + +void CallPrinter::VisitForStatement(ForStatement* node) { + if (node->init() != NULL) { + Find(node->init()); + } + if (node->cond() != NULL) Find(node->cond()); + if (node->next() != NULL) Find(node->next()); + Find(node->body()); +} + + +void CallPrinter::VisitForInStatement(ForInStatement* node) { + Find(node->each()); + Find(node->enumerable()); + Find(node->body()); +} + + +void CallPrinter::VisitForOfStatement(ForOfStatement* node) { + Find(node->each()); + Find(node->assign_iterator()); + Find(node->body()); + Find(node->next_result()); +} + + +void CallPrinter::VisitTryCatchStatement(TryCatchStatement* node) { + Find(node->try_block()); + Find(node->catch_block()); +} + + +void CallPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) { + Find(node->try_block()); + Find(node->finally_block()); +} + + +void CallPrinter::VisitDebuggerStatement(DebuggerStatement* node) {} + + +void CallPrinter::VisitFunctionLiteral(FunctionLiteral* node) { + FindStatements(node->body()); +} + + +void CallPrinter::VisitClassLiteral(ClassLiteral* node) { + if (node->extends()) Find(node->extends()); + for (int i = 0; i < node->properties()->length(); i++) { + Find(node->properties()->at(i)->value()); + } +} + + +void CallPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {} + + +void CallPrinter::VisitDoExpression(DoExpression* node) { Find(node->block()); } + + +void CallPrinter::VisitConditional(Conditional* node) { + Find(node->condition()); + Find(node->then_expression()); + Find(node->else_expression()); +} + + +void CallPrinter::VisitLiteral(Literal* node) { + PrintLiteral(*node->value(), true); +} + + +void CallPrinter::VisitRegExpLiteral(RegExpLiteral* node) { + Print("/"); + PrintLiteral(*node->pattern(), false); + Print("/"); + if (node->flags() & RegExp::kGlobal) Print("g"); + if (node->flags() & RegExp::kIgnoreCase) Print("i"); + if (node->flags() & RegExp::kMultiline) Print("m"); + if (node->flags() & RegExp::kUnicode) Print("u"); + if (node->flags() & RegExp::kSticky) Print("y"); +} + + +void CallPrinter::VisitObjectLiteral(ObjectLiteral* node) { + for (int i = 0; i < node->properties()->length(); i++) { + Find(node->properties()->at(i)->value()); + } +} + + +void CallPrinter::VisitArrayLiteral(ArrayLiteral* node) { + Print("["); + for (int i = 0; i < node->values()->length(); i++) { + if (i != 0) Print(","); + Find(node->values()->at(i), true); + } + Print("]"); +} + + +void CallPrinter::VisitVariableProxy(VariableProxy* node) { + if (is_builtin_) { + // Variable names of builtins are meaningless due to minification. + Print("(var)"); + } else { + PrintLiteral(*node->name(), false); + } +} + + +void CallPrinter::VisitAssignment(Assignment* node) { + Find(node->target()); + Find(node->value()); +} + + +void CallPrinter::VisitYield(Yield* node) { Find(node->expression()); } + + +void CallPrinter::VisitThrow(Throw* node) { Find(node->exception()); } + + +void CallPrinter::VisitProperty(Property* node) { + Expression* key = node->key(); + Literal* literal = key->AsLiteral(); + if (literal != NULL && literal->value()->IsInternalizedString()) { + Find(node->obj(), true); + Print("."); + PrintLiteral(*literal->value(), false); + } else { + Find(node->obj(), true); + Print("["); + Find(key, true); + Print("]"); + } +} + + +void CallPrinter::VisitCall(Call* node) { + bool was_found = !found_ && node->position() == position_; + if (was_found) { + // Bail out if the error is caused by a direct call to a variable in builtin + // code. The variable name is meaningless due to minification. + if (is_builtin_ && node->expression()->IsVariableProxy()) { + done_ = true; + return; + } + found_ = true; + } + Find(node->expression(), true); + if (!was_found) Print("(...)"); + FindArguments(node->arguments()); + if (was_found) done_ = true; +} + + +void CallPrinter::VisitCallNew(CallNew* node) { + bool was_found = !found_ && node->position() == position_; + if (was_found) { + // Bail out if the error is caused by a direct call to a variable in builtin + // code. The variable name is meaningless due to minification. + if (is_builtin_ && node->expression()->IsVariableProxy()) { + done_ = true; + return; + } + found_ = true; + } + Find(node->expression(), was_found); + FindArguments(node->arguments()); + if (was_found) done_ = true; +} + + +void CallPrinter::VisitCallRuntime(CallRuntime* node) { + FindArguments(node->arguments()); +} + + +void CallPrinter::VisitUnaryOperation(UnaryOperation* node) { + Token::Value op = node->op(); + bool needsSpace = + op == Token::DELETE || op == Token::TYPEOF || op == Token::VOID; + Print("(%s%s", Token::String(op), needsSpace ? " " : ""); + Find(node->expression(), true); + Print(")"); +} + + +void CallPrinter::VisitCountOperation(CountOperation* node) { + Print("("); + if (node->is_prefix()) Print("%s", Token::String(node->op())); + Find(node->expression(), true); + if (node->is_postfix()) Print("%s", Token::String(node->op())); + Print(")"); +} + + +void CallPrinter::VisitBinaryOperation(BinaryOperation* node) { + Print("("); + Find(node->left(), true); + Print(" %s ", Token::String(node->op())); + Find(node->right(), true); + Print(")"); +} + + +void CallPrinter::VisitCompareOperation(CompareOperation* node) { + Print("("); + Find(node->left(), true); + Print(" %s ", Token::String(node->op())); + Find(node->right(), true); + Print(")"); +} + + +void CallPrinter::VisitSpread(Spread* node) { + Print("(..."); + Find(node->expression(), true); + Print(")"); +} + + +void CallPrinter::VisitEmptyParentheses(EmptyParentheses* node) { + UNREACHABLE(); +} + + +void CallPrinter::VisitThisFunction(ThisFunction* node) {} + + +void CallPrinter::VisitSuperPropertyReference(SuperPropertyReference* node) {} + + +void CallPrinter::VisitSuperCallReference(SuperCallReference* node) { + Print("super"); +} + + +void CallPrinter::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* node) { + Find(node->expression()); +} + + +void CallPrinter::FindStatements(ZoneList<Statement*>* statements) { + if (statements == NULL) return; + for (int i = 0; i < statements->length(); i++) { + Find(statements->at(i)); + } +} + + +void CallPrinter::FindArguments(ZoneList<Expression*>* arguments) { + if (found_) return; + for (int i = 0; i < arguments->length(); i++) { + Find(arguments->at(i)); + } +} + + +void CallPrinter::PrintLiteral(Object* value, bool quote) { + Object* object = value; + if (object->IsString()) { + if (quote) Print("\""); + Print("%s", String::cast(object)->ToCString().get()); + if (quote) Print("\""); + } else if (object->IsNull()) { + Print("null"); + } else if (object->IsTrue()) { + Print("true"); + } else if (object->IsFalse()) { + Print("false"); + } else if (object->IsUndefined()) { + Print("undefined"); + } else if (object->IsNumber()) { + Print("%g", object->Number()); + } else if (object->IsSymbol()) { + // Symbols can only occur as literals if they were inserted by the parser. + PrintLiteral(Symbol::cast(object)->name(), false); + } +} + + +void CallPrinter::PrintLiteral(const AstRawString* value, bool quote) { + PrintLiteral(*value->string(), quote); +} + + +//----------------------------------------------------------------------------- + + +#ifdef DEBUG + +// A helper for ast nodes that use FeedbackVectorSlots. +static int FormatSlotNode(Vector<char>* buf, Expression* node, + const char* node_name, FeedbackVectorSlot slot) { + int pos = SNPrintF(*buf, "%s", node_name); + if (!slot.IsInvalid()) { + pos = SNPrintF(*buf + pos, " Slot(%d)", slot.ToInt()); + } + return pos; +} + + +PrettyPrinter::PrettyPrinter(Isolate* isolate) { + output_ = NULL; + size_ = 0; + pos_ = 0; + InitializeAstVisitor(isolate); +} + + +PrettyPrinter::~PrettyPrinter() { + DeleteArray(output_); +} + + +void PrettyPrinter::VisitBlock(Block* node) { + if (!node->ignore_completion_value()) Print("{ "); + PrintStatements(node->statements()); + if (node->statements()->length() > 0) Print(" "); + if (!node->ignore_completion_value()) Print("}"); +} + + +void PrettyPrinter::VisitVariableDeclaration(VariableDeclaration* node) { + Print("var "); + PrintLiteral(node->proxy()->name(), false); + Print(";"); +} + + +void PrettyPrinter::VisitFunctionDeclaration(FunctionDeclaration* node) { + Print("function "); + PrintLiteral(node->proxy()->name(), false); + Print(" = "); + PrintFunctionLiteral(node->fun()); + Print(";"); +} + + +void PrettyPrinter::VisitImportDeclaration(ImportDeclaration* node) { + Print("import "); + PrintLiteral(node->proxy()->name(), false); + Print(" from "); + PrintLiteral(node->module_specifier()->string(), true); + Print(";"); +} + + +void PrettyPrinter::VisitExportDeclaration(ExportDeclaration* node) { + Print("export "); + PrintLiteral(node->proxy()->name(), false); + Print(";"); +} + + +void PrettyPrinter::VisitExpressionStatement(ExpressionStatement* node) { + Visit(node->expression()); + Print(";"); +} + + +void PrettyPrinter::VisitEmptyStatement(EmptyStatement* node) { + Print(";"); +} + + +void PrettyPrinter::VisitSloppyBlockFunctionStatement( + SloppyBlockFunctionStatement* node) { + Visit(node->statement()); +} + + +void PrettyPrinter::VisitIfStatement(IfStatement* node) { + Print("if ("); + Visit(node->condition()); + Print(") "); + Visit(node->then_statement()); + if (node->HasElseStatement()) { + Print(" else "); + Visit(node->else_statement()); + } +} + + +void PrettyPrinter::VisitContinueStatement(ContinueStatement* node) { + Print("continue"); + ZoneList<const AstRawString*>* labels = node->target()->labels(); + if (labels != NULL) { + Print(" "); + DCHECK(labels->length() > 0); // guaranteed to have at least one entry + PrintLiteral(labels->at(0), false); // any label from the list is fine + } + Print(";"); +} + + +void PrettyPrinter::VisitBreakStatement(BreakStatement* node) { + Print("break"); + ZoneList<const AstRawString*>* labels = node->target()->labels(); + if (labels != NULL) { + Print(" "); + DCHECK(labels->length() > 0); // guaranteed to have at least one entry + PrintLiteral(labels->at(0), false); // any label from the list is fine + } + Print(";"); +} + + +void PrettyPrinter::VisitReturnStatement(ReturnStatement* node) { + Print("return "); + Visit(node->expression()); + Print(";"); +} + + +void PrettyPrinter::VisitWithStatement(WithStatement* node) { + Print("with ("); + Visit(node->expression()); + Print(") "); + Visit(node->statement()); +} + + +void PrettyPrinter::VisitSwitchStatement(SwitchStatement* node) { + PrintLabels(node->labels()); + Print("switch ("); + Visit(node->tag()); + Print(") { "); + ZoneList<CaseClause*>* cases = node->cases(); + for (int i = 0; i < cases->length(); i++) + Visit(cases->at(i)); + Print("}"); +} + + +void PrettyPrinter::VisitCaseClause(CaseClause* clause) { + if (clause->is_default()) { + Print("default"); + } else { + Print("case "); + Visit(clause->label()); + } + Print(": "); + PrintStatements(clause->statements()); + if (clause->statements()->length() > 0) + Print(" "); +} + + +void PrettyPrinter::VisitDoWhileStatement(DoWhileStatement* node) { + PrintLabels(node->labels()); + Print("do "); + Visit(node->body()); + Print(" while ("); + Visit(node->cond()); + Print(");"); +} + + +void PrettyPrinter::VisitWhileStatement(WhileStatement* node) { + PrintLabels(node->labels()); + Print("while ("); + Visit(node->cond()); + Print(") "); + Visit(node->body()); +} + + +void PrettyPrinter::VisitForStatement(ForStatement* node) { + PrintLabels(node->labels()); + Print("for ("); + if (node->init() != NULL) { + Visit(node->init()); + Print(" "); + } else { + Print("; "); + } + if (node->cond() != NULL) Visit(node->cond()); + Print("; "); + if (node->next() != NULL) { + Visit(node->next()); // prints extra ';', unfortunately + // to fix: should use Expression for next + } + Print(") "); + Visit(node->body()); +} + + +void PrettyPrinter::VisitForInStatement(ForInStatement* node) { + PrintLabels(node->labels()); + Print("for ("); + Visit(node->each()); + Print(" in "); + Visit(node->enumerable()); + Print(") "); + Visit(node->body()); +} + + +void PrettyPrinter::VisitForOfStatement(ForOfStatement* node) { + PrintLabels(node->labels()); + Print("for ("); + Visit(node->each()); + Print(" of "); + Visit(node->iterable()); + Print(") "); + Visit(node->body()); +} + + +void PrettyPrinter::VisitTryCatchStatement(TryCatchStatement* node) { + Print("try "); + Visit(node->try_block()); + Print(" catch ("); + const bool quote = false; + PrintLiteral(node->variable()->name(), quote); + Print(") "); + Visit(node->catch_block()); +} + + +void PrettyPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) { + Print("try "); + Visit(node->try_block()); + Print(" finally "); + Visit(node->finally_block()); +} + + +void PrettyPrinter::VisitDebuggerStatement(DebuggerStatement* node) { + Print("debugger "); +} + + +void PrettyPrinter::VisitFunctionLiteral(FunctionLiteral* node) { + Print("("); + PrintFunctionLiteral(node); + Print(")"); +} + + +void PrettyPrinter::VisitClassLiteral(ClassLiteral* node) { + Print("(class "); + PrintLiteral(node->name(), false); + if (node->extends()) { + Print(" extends "); + Visit(node->extends()); + } + Print(" { "); + for (int i = 0; i < node->properties()->length(); i++) { + PrintObjectLiteralProperty(node->properties()->at(i)); + } + Print(" })"); +} + + +void PrettyPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) { + Print("("); + PrintLiteral(node->name(), false); + Print(")"); +} + + +void PrettyPrinter::VisitDoExpression(DoExpression* node) { + Print("(do {"); + PrintStatements(node->block()->statements()); + Print("})"); +} + + +void PrettyPrinter::VisitConditional(Conditional* node) { + Visit(node->condition()); + Print(" ? "); + Visit(node->then_expression()); + Print(" : "); + Visit(node->else_expression()); +} + + +void PrettyPrinter::VisitLiteral(Literal* node) { + PrintLiteral(node->value(), true); +} + + +void PrettyPrinter::VisitRegExpLiteral(RegExpLiteral* node) { + Print(" RegExp("); + PrintLiteral(node->pattern(), false); + Print(","); + if (node->flags() & RegExp::kGlobal) Print("g"); + if (node->flags() & RegExp::kIgnoreCase) Print("i"); + if (node->flags() & RegExp::kMultiline) Print("m"); + if (node->flags() & RegExp::kUnicode) Print("u"); + if (node->flags() & RegExp::kSticky) Print("y"); + Print(") "); +} + + +void PrettyPrinter::VisitObjectLiteral(ObjectLiteral* node) { + Print("{ "); + for (int i = 0; i < node->properties()->length(); i++) { + if (i != 0) Print(","); + PrintObjectLiteralProperty(node->properties()->at(i)); + } + Print(" }"); +} + + +void PrettyPrinter::PrintObjectLiteralProperty( + ObjectLiteralProperty* property) { + // TODO(arv): Better printing of methods etc. + Print(" "); + Visit(property->key()); + Print(": "); + Visit(property->value()); +} + + +void PrettyPrinter::VisitArrayLiteral(ArrayLiteral* node) { + Print("[ "); + Print(" literal_index = %d", node->literal_index()); + for (int i = 0; i < node->values()->length(); i++) { + if (i != 0) Print(","); + Visit(node->values()->at(i)); + } + Print(" ]"); +} + + +void PrettyPrinter::VisitVariableProxy(VariableProxy* node) { + PrintLiteral(node->name(), false); +} + + +void PrettyPrinter::VisitAssignment(Assignment* node) { + Visit(node->target()); + Print(" %s ", Token::String(node->op())); + Visit(node->value()); +} + + +void PrettyPrinter::VisitYield(Yield* node) { + Print("yield "); + Visit(node->expression()); +} + + +void PrettyPrinter::VisitThrow(Throw* node) { + Print("throw "); + Visit(node->exception()); +} + + +void PrettyPrinter::VisitProperty(Property* node) { + Expression* key = node->key(); + Literal* literal = key->AsLiteral(); + if (literal != NULL && literal->value()->IsInternalizedString()) { + Print("("); + Visit(node->obj()); + Print(")."); + PrintLiteral(literal->value(), false); + } else { + Visit(node->obj()); + Print("["); + Visit(key); + Print("]"); + } +} + + +void PrettyPrinter::VisitCall(Call* node) { + Visit(node->expression()); + PrintArguments(node->arguments()); +} + + +void PrettyPrinter::VisitCallNew(CallNew* node) { + Print("new ("); + Visit(node->expression()); + Print(")"); + PrintArguments(node->arguments()); +} + + +void PrettyPrinter::VisitCallRuntime(CallRuntime* node) { + Print("%%%s\n", node->debug_name()); + PrintArguments(node->arguments()); +} + + +void PrettyPrinter::VisitUnaryOperation(UnaryOperation* node) { + Token::Value op = node->op(); + bool needsSpace = + op == Token::DELETE || op == Token::TYPEOF || op == Token::VOID; + Print("(%s%s", Token::String(op), needsSpace ? " " : ""); + Visit(node->expression()); + Print(")"); +} + + +void PrettyPrinter::VisitCountOperation(CountOperation* node) { + Print("("); + if (node->is_prefix()) Print("%s", Token::String(node->op())); + Visit(node->expression()); + if (node->is_postfix()) Print("%s", Token::String(node->op())); + Print(")"); +} + + +void PrettyPrinter::VisitBinaryOperation(BinaryOperation* node) { + Print("("); + Visit(node->left()); + Print(" %s ", Token::String(node->op())); + Visit(node->right()); + Print(")"); +} + + +void PrettyPrinter::VisitCompareOperation(CompareOperation* node) { + Print("("); + Visit(node->left()); + Print(" %s ", Token::String(node->op())); + Visit(node->right()); + Print(")"); +} + + +void PrettyPrinter::VisitSpread(Spread* node) { + Print("(..."); + Visit(node->expression()); + Print(")"); +} + + +void PrettyPrinter::VisitEmptyParentheses(EmptyParentheses* node) { + Print("()"); +} + + +void PrettyPrinter::VisitThisFunction(ThisFunction* node) { + Print("<this-function>"); +} + + +void PrettyPrinter::VisitSuperPropertyReference(SuperPropertyReference* node) { + Print("<super-property-reference>"); +} + + +void PrettyPrinter::VisitSuperCallReference(SuperCallReference* node) { + Print("<super-call-reference>"); +} + + +void PrettyPrinter::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* node) { + Visit(node->expression()); +} + + +const char* PrettyPrinter::Print(AstNode* node) { + Init(); + Visit(node); + return output_; +} + + +const char* PrettyPrinter::PrintExpression(FunctionLiteral* program) { + Init(); + ExpressionStatement* statement = + program->body()->at(0)->AsExpressionStatement(); + Visit(statement->expression()); + return output_; +} + + +const char* PrettyPrinter::PrintProgram(FunctionLiteral* program) { + Init(); + PrintStatements(program->body()); + Print("\n"); + return output_; +} + + +void PrettyPrinter::PrintOut(Isolate* isolate, AstNode* node) { + PrettyPrinter printer(isolate); + PrintF("%s\n", printer.Print(node)); +} + + +void PrettyPrinter::Init() { + if (size_ == 0) { + DCHECK(output_ == NULL); + const int initial_size = 256; + output_ = NewArray<char>(initial_size); + size_ = initial_size; + } + output_[0] = '\0'; + pos_ = 0; +} + + +void PrettyPrinter::Print(const char* format, ...) { + for (;;) { + va_list arguments; + va_start(arguments, format); + int n = VSNPrintF(Vector<char>(output_, size_) + pos_, + format, + arguments); + va_end(arguments); + + if (n >= 0) { + // there was enough space - we are done + pos_ += n; + return; + } else { + // there was not enough space - allocate more and try again + const int slack = 32; + int new_size = size_ + (size_ >> 1) + slack; + char* new_output = NewArray<char>(new_size); + MemCopy(new_output, output_, pos_); + DeleteArray(output_); + output_ = new_output; + size_ = new_size; + } + } +} + + +void PrettyPrinter::PrintStatements(ZoneList<Statement*>* statements) { + if (statements == NULL) return; + for (int i = 0; i < statements->length(); i++) { + if (i != 0) Print(" "); + Visit(statements->at(i)); + } +} + + +void PrettyPrinter::PrintLabels(ZoneList<const AstRawString*>* labels) { + if (labels != NULL) { + for (int i = 0; i < labels->length(); i++) { + PrintLiteral(labels->at(i), false); + Print(": "); + } + } +} + + +void PrettyPrinter::PrintArguments(ZoneList<Expression*>* arguments) { + Print("("); + for (int i = 0; i < arguments->length(); i++) { + if (i != 0) Print(", "); + Visit(arguments->at(i)); + } + Print(")"); +} + + +void PrettyPrinter::PrintLiteral(Handle<Object> value, bool quote) { + Object* object = *value; + if (object->IsString()) { + String* string = String::cast(object); + if (quote) Print("\""); + for (int i = 0; i < string->length(); i++) { + Print("%c", string->Get(i)); + } + if (quote) Print("\""); + } else if (object->IsNull()) { + Print("null"); + } else if (object->IsTrue()) { + Print("true"); + } else if (object->IsFalse()) { + Print("false"); + } else if (object->IsUndefined()) { + Print("undefined"); + } else if (object->IsNumber()) { + Print("%g", object->Number()); + } else if (object->IsJSObject()) { + // regular expression + if (object->IsJSFunction()) { + Print("JS-Function"); + } else if (object->IsJSArray()) { + Print("JS-array[%u]", JSArray::cast(object)->length()); + } else if (object->IsJSObject()) { + Print("JS-Object"); + } else { + Print("?UNKNOWN?"); + } + } else if (object->IsFixedArray()) { + Print("FixedArray"); + } else { + Print("<unknown literal %p>", object); + } +} + + +void PrettyPrinter::PrintLiteral(const AstRawString* value, bool quote) { + PrintLiteral(value->string(), quote); +} + + +void PrettyPrinter::PrintParameters(Scope* scope) { + Print("("); + for (int i = 0; i < scope->num_parameters(); i++) { + if (i > 0) Print(", "); + PrintLiteral(scope->parameter(i)->name(), false); + } + Print(")"); +} + + +void PrettyPrinter::PrintDeclarations(ZoneList<Declaration*>* declarations) { + for (int i = 0; i < declarations->length(); i++) { + if (i > 0) Print(" "); + Visit(declarations->at(i)); + } +} + + +void PrettyPrinter::PrintFunctionLiteral(FunctionLiteral* function) { + Print("function "); + PrintLiteral(function->name(), false); + PrintParameters(function->scope()); + Print(" { "); + PrintDeclarations(function->scope()->declarations()); + PrintStatements(function->body()); + Print(" }"); +} + + +//----------------------------------------------------------------------------- + +class IndentedScope BASE_EMBEDDED { + public: + IndentedScope(AstPrinter* printer, const char* txt) + : ast_printer_(printer) { + ast_printer_->PrintIndented(txt); + ast_printer_->Print("\n"); + ast_printer_->inc_indent(); + } + + IndentedScope(AstPrinter* printer, const char* txt, int pos) + : ast_printer_(printer) { + ast_printer_->PrintIndented(txt); + ast_printer_->Print(" at %d\n", pos); + ast_printer_->inc_indent(); + } + + virtual ~IndentedScope() { + ast_printer_->dec_indent(); + } + + private: + AstPrinter* ast_printer_; +}; + + +//----------------------------------------------------------------------------- + + +AstPrinter::AstPrinter(Isolate* isolate) : PrettyPrinter(isolate), indent_(0) {} + + +AstPrinter::~AstPrinter() { + DCHECK(indent_ == 0); +} + + +void AstPrinter::PrintIndented(const char* txt) { + for (int i = 0; i < indent_; i++) { + Print(". "); + } + Print(txt); +} + + +void AstPrinter::PrintLiteralIndented(const char* info, + Handle<Object> value, + bool quote) { + PrintIndented(info); + Print(" "); + PrintLiteral(value, quote); + Print("\n"); +} + + +void AstPrinter::PrintLiteralWithModeIndented(const char* info, + Variable* var, + Handle<Object> value) { + if (var == NULL) { + PrintLiteralIndented(info, value, true); + } else { + EmbeddedVector<char, 256> buf; + int pos = SNPrintF(buf, "%s (mode = %s", info, + Variable::Mode2String(var->mode())); + SNPrintF(buf + pos, ")"); + PrintLiteralIndented(buf.start(), value, true); + } +} + + +void AstPrinter::PrintLabelsIndented(ZoneList<const AstRawString*>* labels) { + if (labels == NULL || labels->length() == 0) return; + PrintIndented("LABELS "); + PrintLabels(labels); + Print("\n"); +} + + +void AstPrinter::PrintIndentedVisit(const char* s, AstNode* node) { + IndentedScope indent(this, s, node->position()); + Visit(node); +} + + +const char* AstPrinter::PrintProgram(FunctionLiteral* program) { + Init(); + { IndentedScope indent(this, "FUNC", program->position()); + PrintLiteralIndented("NAME", program->name(), true); + PrintLiteralIndented("INFERRED NAME", program->inferred_name(), true); + PrintParameters(program->scope()); + PrintDeclarations(program->scope()->declarations()); + PrintStatements(program->body()); + } + return Output(); +} + + +void AstPrinter::PrintDeclarations(ZoneList<Declaration*>* declarations) { + if (declarations->length() > 0) { + IndentedScope indent(this, "DECLS"); + for (int i = 0; i < declarations->length(); i++) { + Visit(declarations->at(i)); + } + } +} + + +void AstPrinter::PrintParameters(Scope* scope) { + if (scope->num_parameters() > 0) { + IndentedScope indent(this, "PARAMS"); + for (int i = 0; i < scope->num_parameters(); i++) { + PrintLiteralWithModeIndented("VAR", scope->parameter(i), + scope->parameter(i)->name()); + } + } +} + + +void AstPrinter::PrintStatements(ZoneList<Statement*>* statements) { + for (int i = 0; i < statements->length(); i++) { + Visit(statements->at(i)); + } +} + + +void AstPrinter::PrintArguments(ZoneList<Expression*>* arguments) { + for (int i = 0; i < arguments->length(); i++) { + Visit(arguments->at(i)); + } +} + + +void AstPrinter::VisitBlock(Block* node) { + const char* block_txt = + node->ignore_completion_value() ? "BLOCK NOCOMPLETIONS" : "BLOCK"; + IndentedScope indent(this, block_txt, node->position()); + PrintStatements(node->statements()); +} + + +// TODO(svenpanne) Start with IndentedScope. +void AstPrinter::VisitVariableDeclaration(VariableDeclaration* node) { + PrintLiteralWithModeIndented(Variable::Mode2String(node->mode()), + node->proxy()->var(), + node->proxy()->name()); +} + + +// TODO(svenpanne) Start with IndentedScope. +void AstPrinter::VisitFunctionDeclaration(FunctionDeclaration* node) { + PrintIndented("FUNCTION "); + PrintLiteral(node->proxy()->name(), true); + Print(" = function "); + PrintLiteral(node->fun()->name(), false); + Print("\n"); +} + + +void AstPrinter::VisitImportDeclaration(ImportDeclaration* node) { + IndentedScope indent(this, "IMPORT", node->position()); + PrintLiteralIndented("NAME", node->proxy()->name(), true); + PrintLiteralIndented("FROM", node->module_specifier()->string(), true); +} + + +void AstPrinter::VisitExportDeclaration(ExportDeclaration* node) { + IndentedScope indent(this, "EXPORT", node->position()); + PrintLiteral(node->proxy()->name(), true); +} + + +void AstPrinter::VisitExpressionStatement(ExpressionStatement* node) { + IndentedScope indent(this, "EXPRESSION STATEMENT", node->position()); + Visit(node->expression()); +} + + +void AstPrinter::VisitEmptyStatement(EmptyStatement* node) { + IndentedScope indent(this, "EMPTY", node->position()); +} + + +void AstPrinter::VisitSloppyBlockFunctionStatement( + SloppyBlockFunctionStatement* node) { + Visit(node->statement()); +} + + +void AstPrinter::VisitIfStatement(IfStatement* node) { + IndentedScope indent(this, "IF", node->position()); + PrintIndentedVisit("CONDITION", node->condition()); + PrintIndentedVisit("THEN", node->then_statement()); + if (node->HasElseStatement()) { + PrintIndentedVisit("ELSE", node->else_statement()); + } +} + + +void AstPrinter::VisitContinueStatement(ContinueStatement* node) { + IndentedScope indent(this, "CONTINUE", node->position()); + PrintLabelsIndented(node->target()->labels()); +} + + +void AstPrinter::VisitBreakStatement(BreakStatement* node) { + IndentedScope indent(this, "BREAK", node->position()); + PrintLabelsIndented(node->target()->labels()); +} + + +void AstPrinter::VisitReturnStatement(ReturnStatement* node) { + IndentedScope indent(this, "RETURN", node->position()); + Visit(node->expression()); +} + + +void AstPrinter::VisitWithStatement(WithStatement* node) { + IndentedScope indent(this, "WITH", node->position()); + PrintIndentedVisit("OBJECT", node->expression()); + PrintIndentedVisit("BODY", node->statement()); +} + + +void AstPrinter::VisitSwitchStatement(SwitchStatement* node) { + IndentedScope indent(this, "SWITCH", node->position()); + PrintLabelsIndented(node->labels()); + PrintIndentedVisit("TAG", node->tag()); + for (int i = 0; i < node->cases()->length(); i++) { + Visit(node->cases()->at(i)); + } +} + + +void AstPrinter::VisitCaseClause(CaseClause* clause) { + if (clause->is_default()) { + IndentedScope indent(this, "DEFAULT", clause->position()); + PrintStatements(clause->statements()); + } else { + IndentedScope indent(this, "CASE", clause->position()); + Visit(clause->label()); + PrintStatements(clause->statements()); + } +} + + +void AstPrinter::VisitDoWhileStatement(DoWhileStatement* node) { + IndentedScope indent(this, "DO", node->position()); + PrintLabelsIndented(node->labels()); + PrintIndentedVisit("BODY", node->body()); + PrintIndentedVisit("COND", node->cond()); +} + + +void AstPrinter::VisitWhileStatement(WhileStatement* node) { + IndentedScope indent(this, "WHILE", node->position()); + PrintLabelsIndented(node->labels()); + PrintIndentedVisit("COND", node->cond()); + PrintIndentedVisit("BODY", node->body()); +} + + +void AstPrinter::VisitForStatement(ForStatement* node) { + IndentedScope indent(this, "FOR", node->position()); + PrintLabelsIndented(node->labels()); + if (node->init()) PrintIndentedVisit("INIT", node->init()); + if (node->cond()) PrintIndentedVisit("COND", node->cond()); + PrintIndentedVisit("BODY", node->body()); + if (node->next()) PrintIndentedVisit("NEXT", node->next()); +} + + +void AstPrinter::VisitForInStatement(ForInStatement* node) { + IndentedScope indent(this, "FOR IN", node->position()); + PrintIndentedVisit("FOR", node->each()); + PrintIndentedVisit("IN", node->enumerable()); + PrintIndentedVisit("BODY", node->body()); +} + + +void AstPrinter::VisitForOfStatement(ForOfStatement* node) { + IndentedScope indent(this, "FOR OF", node->position()); + PrintIndentedVisit("FOR", node->each()); + PrintIndentedVisit("OF", node->iterable()); + PrintIndentedVisit("BODY", node->body()); +} + + +void AstPrinter::VisitTryCatchStatement(TryCatchStatement* node) { + IndentedScope indent(this, "TRY CATCH", node->position()); + PrintIndentedVisit("TRY", node->try_block()); + PrintLiteralWithModeIndented("CATCHVAR", + node->variable(), + node->variable()->name()); + PrintIndentedVisit("CATCH", node->catch_block()); +} + + +void AstPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) { + IndentedScope indent(this, "TRY FINALLY", node->position()); + PrintIndentedVisit("TRY", node->try_block()); + PrintIndentedVisit("FINALLY", node->finally_block()); +} + + +void AstPrinter::VisitDebuggerStatement(DebuggerStatement* node) { + IndentedScope indent(this, "DEBUGGER", node->position()); +} + + +void AstPrinter::VisitFunctionLiteral(FunctionLiteral* node) { + IndentedScope indent(this, "FUNC LITERAL", node->position()); + PrintLiteralIndented("NAME", node->name(), false); + PrintLiteralIndented("INFERRED NAME", node->inferred_name(), false); + PrintParameters(node->scope()); + // We don't want to see the function literal in this case: it + // will be printed via PrintProgram when the code for it is + // generated. + // PrintStatements(node->body()); +} + + +void AstPrinter::VisitClassLiteral(ClassLiteral* node) { + IndentedScope indent(this, "CLASS LITERAL", node->position()); + if (node->raw_name() != nullptr) { + PrintLiteralIndented("NAME", node->name(), false); + } + if (node->extends() != nullptr) { + PrintIndentedVisit("EXTENDS", node->extends()); + } + PrintProperties(node->properties()); +} + + +void AstPrinter::PrintProperties( + ZoneList<ObjectLiteral::Property*>* properties) { + for (int i = 0; i < properties->length(); i++) { + ObjectLiteral::Property* property = properties->at(i); + const char* prop_kind = nullptr; + switch (property->kind()) { + case ObjectLiteral::Property::CONSTANT: + prop_kind = "CONSTANT"; + break; + case ObjectLiteral::Property::COMPUTED: + prop_kind = "COMPUTED"; + break; + case ObjectLiteral::Property::MATERIALIZED_LITERAL: + prop_kind = "MATERIALIZED_LITERAL"; + break; + case ObjectLiteral::Property::PROTOTYPE: + prop_kind = "PROTOTYPE"; + break; + case ObjectLiteral::Property::GETTER: + prop_kind = "GETTER"; + break; + case ObjectLiteral::Property::SETTER: + prop_kind = "SETTER"; + break; + } + EmbeddedVector<char, 128> buf; + SNPrintF(buf, "PROPERTY%s - %s", property->is_static() ? " - STATIC" : "", + prop_kind); + IndentedScope prop(this, buf.start()); + PrintIndentedVisit("KEY", properties->at(i)->key()); + PrintIndentedVisit("VALUE", properties->at(i)->value()); + } +} + + +void AstPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) { + IndentedScope indent(this, "NATIVE FUNC LITERAL", node->position()); + PrintLiteralIndented("NAME", node->name(), false); +} + + +void AstPrinter::VisitDoExpression(DoExpression* node) { + IndentedScope indent(this, "DO EXPRESSION", node->position()); + PrintStatements(node->block()->statements()); +} + + +void AstPrinter::VisitConditional(Conditional* node) { + IndentedScope indent(this, "CONDITIONAL", node->position()); + PrintIndentedVisit("CONDITION", node->condition()); + PrintIndentedVisit("THEN", node->then_expression()); + PrintIndentedVisit("ELSE", node->else_expression()); +} + + +// TODO(svenpanne) Start with IndentedScope. +void AstPrinter::VisitLiteral(Literal* node) { + PrintLiteralIndented("LITERAL", node->value(), true); +} + + +void AstPrinter::VisitRegExpLiteral(RegExpLiteral* node) { + IndentedScope indent(this, "REGEXP LITERAL", node->position()); + EmbeddedVector<char, 128> buf; + SNPrintF(buf, "literal_index = %d\n", node->literal_index()); + PrintIndented(buf.start()); + PrintLiteralIndented("PATTERN", node->pattern(), false); + int i = 0; + if (node->flags() & RegExp::kGlobal) buf[i++] = 'g'; + if (node->flags() & RegExp::kIgnoreCase) buf[i++] = 'i'; + if (node->flags() & RegExp::kMultiline) buf[i++] = 'm'; + if (node->flags() & RegExp::kUnicode) buf[i++] = 'u'; + if (node->flags() & RegExp::kSticky) buf[i++] = 'y'; + buf[i] = '\0'; + PrintIndented("FLAGS "); + Print(buf.start()); + Print("\n"); +} + + +void AstPrinter::VisitObjectLiteral(ObjectLiteral* node) { + IndentedScope indent(this, "OBJ LITERAL", node->position()); + EmbeddedVector<char, 128> buf; + SNPrintF(buf, "literal_index = %d\n", node->literal_index()); + PrintIndented(buf.start()); + PrintProperties(node->properties()); +} + + +void AstPrinter::VisitArrayLiteral(ArrayLiteral* node) { + IndentedScope indent(this, "ARRAY LITERAL", node->position()); + + EmbeddedVector<char, 128> buf; + SNPrintF(buf, "literal_index = %d\n", node->literal_index()); + PrintIndented(buf.start()); + if (node->values()->length() > 0) { + IndentedScope indent(this, "VALUES", node->position()); + for (int i = 0; i < node->values()->length(); i++) { + Visit(node->values()->at(i)); + } + } +} + + +void AstPrinter::VisitVariableProxy(VariableProxy* node) { + Variable* var = node->var(); + EmbeddedVector<char, 128> buf; + int pos = + FormatSlotNode(&buf, node, "VAR PROXY", node->VariableFeedbackSlot()); + + switch (var->location()) { + case VariableLocation::UNALLOCATED: + break; + case VariableLocation::PARAMETER: + SNPrintF(buf + pos, " parameter[%d]", var->index()); + break; + case VariableLocation::LOCAL: + SNPrintF(buf + pos, " local[%d]", var->index()); + break; + case VariableLocation::CONTEXT: + SNPrintF(buf + pos, " context[%d]", var->index()); + break; + case VariableLocation::GLOBAL: + SNPrintF(buf + pos, " global[%d]", var->index()); + break; + case VariableLocation::LOOKUP: + SNPrintF(buf + pos, " lookup"); + break; + } + PrintLiteralWithModeIndented(buf.start(), var, node->name()); +} + + +void AstPrinter::VisitAssignment(Assignment* node) { + IndentedScope indent(this, Token::Name(node->op()), node->position()); + Visit(node->target()); + Visit(node->value()); +} + + +void AstPrinter::VisitYield(Yield* node) { + IndentedScope indent(this, "YIELD", node->position()); + Visit(node->expression()); +} + + +void AstPrinter::VisitThrow(Throw* node) { + IndentedScope indent(this, "THROW", node->position()); + Visit(node->exception()); +} + + +void AstPrinter::VisitProperty(Property* node) { + EmbeddedVector<char, 128> buf; + FormatSlotNode(&buf, node, "PROPERTY", node->PropertyFeedbackSlot()); + IndentedScope indent(this, buf.start(), node->position()); + + Visit(node->obj()); + Literal* literal = node->key()->AsLiteral(); + if (literal != NULL && literal->value()->IsInternalizedString()) { + PrintLiteralIndented("NAME", literal->value(), false); + } else { + PrintIndentedVisit("KEY", node->key()); + } +} + + +void AstPrinter::VisitCall(Call* node) { + EmbeddedVector<char, 128> buf; + FormatSlotNode(&buf, node, "CALL", node->CallFeedbackICSlot()); + IndentedScope indent(this, buf.start()); + + Visit(node->expression()); + PrintArguments(node->arguments()); +} + + +void AstPrinter::VisitCallNew(CallNew* node) { + IndentedScope indent(this, "CALL NEW", node->position()); + Visit(node->expression()); + PrintArguments(node->arguments()); +} + + +void AstPrinter::VisitCallRuntime(CallRuntime* node) { + EmbeddedVector<char, 128> buf; + SNPrintF(buf, "CALL RUNTIME %s", node->debug_name()); + IndentedScope indent(this, buf.start(), node->position()); + PrintArguments(node->arguments()); +} + + +void AstPrinter::VisitUnaryOperation(UnaryOperation* node) { + IndentedScope indent(this, Token::Name(node->op()), node->position()); + Visit(node->expression()); +} + + +void AstPrinter::VisitCountOperation(CountOperation* node) { + EmbeddedVector<char, 128> buf; + SNPrintF(buf, "%s %s", (node->is_prefix() ? "PRE" : "POST"), + Token::Name(node->op())); + IndentedScope indent(this, buf.start(), node->position()); + Visit(node->expression()); +} + + +void AstPrinter::VisitBinaryOperation(BinaryOperation* node) { + IndentedScope indent(this, Token::Name(node->op()), node->position()); + Visit(node->left()); + Visit(node->right()); +} + + +void AstPrinter::VisitCompareOperation(CompareOperation* node) { + IndentedScope indent(this, Token::Name(node->op()), node->position()); + Visit(node->left()); + Visit(node->right()); +} + + +void AstPrinter::VisitSpread(Spread* node) { + IndentedScope indent(this, "...", node->position()); + Visit(node->expression()); +} + + +void AstPrinter::VisitEmptyParentheses(EmptyParentheses* node) { + IndentedScope indent(this, "()", node->position()); +} + + +void AstPrinter::VisitThisFunction(ThisFunction* node) { + IndentedScope indent(this, "THIS-FUNCTION", node->position()); +} + + +void AstPrinter::VisitSuperPropertyReference(SuperPropertyReference* node) { + IndentedScope indent(this, "SUPER-PROPERTY-REFERENCE", node->position()); +} + + +void AstPrinter::VisitSuperCallReference(SuperCallReference* node) { + IndentedScope indent(this, "SUPER-CALL-REFERENCE", node->position()); +} + + +void AstPrinter::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* node) { + Visit(node->expression()); +} + + +#endif // DEBUG + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/prettyprinter.h b/deps/v8/src/ast/prettyprinter.h new file mode 100644 index 0000000000..7e4dcdc804 --- /dev/null +++ b/deps/v8/src/ast/prettyprinter.h @@ -0,0 +1,140 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_PRETTYPRINTER_H_ +#define V8_AST_PRETTYPRINTER_H_ + +#include "src/allocation.h" +#include "src/ast/ast.h" + +namespace v8 { +namespace internal { + +class CallPrinter : public AstVisitor { + public: + explicit CallPrinter(Isolate* isolate, bool is_builtin); + virtual ~CallPrinter(); + + // The following routine prints the node with position |position| into a + // string. The result string is alive as long as the CallPrinter is alive. + const char* Print(FunctionLiteral* program, int position); + + void Print(const char* format, ...); + + void Find(AstNode* node, bool print = false); + +// Individual nodes +#define DECLARE_VISIT(type) void Visit##type(type* node) override; + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + private: + void Init(); + char* output_; // output string buffer + int size_; // output_ size + int pos_; // current printing position + int position_; // position of ast node to print + bool found_; + bool done_; + bool is_builtin_; + + DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); + + protected: + void PrintLiteral(Object* value, bool quote); + void PrintLiteral(const AstRawString* value, bool quote); + void FindStatements(ZoneList<Statement*>* statements); + void FindArguments(ZoneList<Expression*>* arguments); +}; + + +#ifdef DEBUG + +class PrettyPrinter: public AstVisitor { + public: + explicit PrettyPrinter(Isolate* isolate); + virtual ~PrettyPrinter(); + + // The following routines print a node into a string. + // The result string is alive as long as the PrettyPrinter is alive. + const char* Print(AstNode* node); + const char* PrintExpression(FunctionLiteral* program); + const char* PrintProgram(FunctionLiteral* program); + + void Print(const char* format, ...); + + // Print a node to stdout. + static void PrintOut(Isolate* isolate, AstNode* node); + + // Individual nodes +#define DECLARE_VISIT(type) void Visit##type(type* node) override; + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + private: + char* output_; // output string buffer + int size_; // output_ size + int pos_; // current printing position + + protected: + void Init(); + const char* Output() const { return output_; } + + virtual void PrintStatements(ZoneList<Statement*>* statements); + void PrintLabels(ZoneList<const AstRawString*>* labels); + virtual void PrintArguments(ZoneList<Expression*>* arguments); + void PrintLiteral(Handle<Object> value, bool quote); + void PrintLiteral(const AstRawString* value, bool quote); + void PrintParameters(Scope* scope); + void PrintDeclarations(ZoneList<Declaration*>* declarations); + void PrintFunctionLiteral(FunctionLiteral* function); + void PrintCaseClause(CaseClause* clause); + void PrintObjectLiteralProperty(ObjectLiteralProperty* property); + + DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); +}; + + +// Prints the AST structure +class AstPrinter: public PrettyPrinter { + public: + explicit AstPrinter(Isolate* isolate); + virtual ~AstPrinter(); + + const char* PrintProgram(FunctionLiteral* program); + + // Individual nodes +#define DECLARE_VISIT(type) virtual void Visit##type(type* node); + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + private: + friend class IndentedScope; + void PrintIndented(const char* txt); + void PrintIndentedVisit(const char* s, AstNode* node); + + void PrintStatements(ZoneList<Statement*>* statements); + void PrintDeclarations(ZoneList<Declaration*>* declarations); + void PrintParameters(Scope* scope); + void PrintArguments(ZoneList<Expression*>* arguments); + void PrintCaseClause(CaseClause* clause); + void PrintLiteralIndented(const char* info, Handle<Object> value, bool quote); + void PrintLiteralWithModeIndented(const char* info, + Variable* var, + Handle<Object> value); + void PrintLabelsIndented(ZoneList<const AstRawString*>* labels); + void PrintProperties(ZoneList<ObjectLiteral::Property*>* properties); + + void inc_indent() { indent_++; } + void dec_indent() { indent_--; } + + int indent_; +}; + +#endif // DEBUG + +} // namespace internal +} // namespace v8 + +#endif // V8_AST_PRETTYPRINTER_H_ diff --git a/deps/v8/src/ast/scopeinfo.cc b/deps/v8/src/ast/scopeinfo.cc new file mode 100644 index 0000000000..668879fe51 --- /dev/null +++ b/deps/v8/src/ast/scopeinfo.cc @@ -0,0 +1,858 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/scopeinfo.h" + +#include <stdlib.h> + +#include "src/ast/scopes.h" +#include "src/bootstrapper.h" + +namespace v8 { +namespace internal { + + +Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, + Scope* scope) { + // Collect stack and context locals. + ZoneList<Variable*> stack_locals(scope->StackLocalCount(), zone); + ZoneList<Variable*> context_locals(scope->ContextLocalCount(), zone); + ZoneList<Variable*> context_globals(scope->ContextGlobalCount(), zone); + ZoneList<Variable*> strong_mode_free_variables(0, zone); + + scope->CollectStackAndContextLocals(&stack_locals, &context_locals, + &context_globals, + &strong_mode_free_variables); + const int stack_local_count = stack_locals.length(); + const int context_local_count = context_locals.length(); + const int context_global_count = context_globals.length(); + const int strong_mode_free_variable_count = + strong_mode_free_variables.length(); + // Make sure we allocate the correct amount. + DCHECK_EQ(scope->ContextLocalCount(), context_local_count); + DCHECK_EQ(scope->ContextGlobalCount(), context_global_count); + + // Determine use and location of the "this" binding if it is present. + VariableAllocationInfo receiver_info; + if (scope->has_this_declaration()) { + Variable* var = scope->receiver(); + if (!var->is_used()) { + receiver_info = UNUSED; + } else if (var->IsContextSlot()) { + receiver_info = CONTEXT; + } else { + DCHECK(var->IsParameter()); + receiver_info = STACK; + } + } else { + receiver_info = NONE; + } + + bool has_new_target = scope->new_target_var() != nullptr; + + // Determine use and location of the function variable if it is present. + VariableAllocationInfo function_name_info; + VariableMode function_variable_mode; + if (scope->is_function_scope() && scope->function() != NULL) { + Variable* var = scope->function()->proxy()->var(); + if (!var->is_used()) { + function_name_info = UNUSED; + } else if (var->IsContextSlot()) { + function_name_info = CONTEXT; + } else { + DCHECK(var->IsStackLocal()); + function_name_info = STACK; + } + function_variable_mode = var->mode(); + } else { + function_name_info = NONE; + function_variable_mode = VAR; + } + DCHECK(context_global_count == 0 || scope->scope_type() == SCRIPT_SCOPE); + + const bool has_function_name = function_name_info != NONE; + const bool has_receiver = receiver_info == STACK || receiver_info == CONTEXT; + const int parameter_count = scope->num_parameters(); + const int length = kVariablePartIndex + parameter_count + + (1 + stack_local_count) + 2 * context_local_count + + 2 * context_global_count + + 3 * strong_mode_free_variable_count + + (has_receiver ? 1 : 0) + (has_function_name ? 2 : 0); + + Factory* factory = isolate->factory(); + Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length); + + bool has_simple_parameters = + scope->is_function_scope() && scope->has_simple_parameters(); + + // Encode the flags. + int flags = ScopeTypeField::encode(scope->scope_type()) | + CallsEvalField::encode(scope->calls_eval()) | + LanguageModeField::encode(scope->language_mode()) | + DeclarationScopeField::encode(scope->is_declaration_scope()) | + ReceiverVariableField::encode(receiver_info) | + HasNewTargetField::encode(has_new_target) | + FunctionVariableField::encode(function_name_info) | + FunctionVariableMode::encode(function_variable_mode) | + AsmModuleField::encode(scope->asm_module()) | + AsmFunctionField::encode(scope->asm_function()) | + HasSimpleParametersField::encode(has_simple_parameters) | + FunctionKindField::encode(scope->function_kind()); + scope_info->SetFlags(flags); + scope_info->SetParameterCount(parameter_count); + scope_info->SetStackLocalCount(stack_local_count); + scope_info->SetContextLocalCount(context_local_count); + scope_info->SetContextGlobalCount(context_global_count); + scope_info->SetStrongModeFreeVariableCount(strong_mode_free_variable_count); + + int index = kVariablePartIndex; + // Add parameters. + DCHECK(index == scope_info->ParameterEntriesIndex()); + for (int i = 0; i < parameter_count; ++i) { + scope_info->set(index++, *scope->parameter(i)->name()); + } + + // Add stack locals' names. We are assuming that the stack locals' + // slots are allocated in increasing order, so we can simply add + // them to the ScopeInfo object. + int first_slot_index; + if (stack_local_count > 0) { + first_slot_index = stack_locals[0]->index(); + } else { + first_slot_index = 0; + } + DCHECK(index == scope_info->StackLocalFirstSlotIndex()); + scope_info->set(index++, Smi::FromInt(first_slot_index)); + DCHECK(index == scope_info->StackLocalEntriesIndex()); + for (int i = 0; i < stack_local_count; ++i) { + DCHECK(stack_locals[i]->index() == first_slot_index + i); + scope_info->set(index++, *stack_locals[i]->name()); + } + + // Due to usage analysis, context-allocated locals are not necessarily in + // increasing order: Some of them may be parameters which are allocated before + // the non-parameter locals. When the non-parameter locals are sorted + // according to usage, the allocated slot indices may not be in increasing + // order with the variable list anymore. Thus, we first need to sort them by + // context slot index before adding them to the ScopeInfo object. + context_locals.Sort(&Variable::CompareIndex); + + // Add context locals' names. + DCHECK(index == scope_info->ContextLocalNameEntriesIndex()); + for (int i = 0; i < context_local_count; ++i) { + scope_info->set(index++, *context_locals[i]->name()); + } + + // Add context globals' names. + DCHECK(index == scope_info->ContextGlobalNameEntriesIndex()); + for (int i = 0; i < context_global_count; ++i) { + scope_info->set(index++, *context_globals[i]->name()); + } + + // Add context locals' info. + DCHECK(index == scope_info->ContextLocalInfoEntriesIndex()); + for (int i = 0; i < context_local_count; ++i) { + Variable* var = context_locals[i]; + uint32_t value = + ContextLocalMode::encode(var->mode()) | + ContextLocalInitFlag::encode(var->initialization_flag()) | + ContextLocalMaybeAssignedFlag::encode(var->maybe_assigned()); + scope_info->set(index++, Smi::FromInt(value)); + } + + // Add context globals' info. + DCHECK(index == scope_info->ContextGlobalInfoEntriesIndex()); + for (int i = 0; i < context_global_count; ++i) { + Variable* var = context_globals[i]; + // TODO(ishell): do we need this kind of info for globals here? + uint32_t value = + ContextLocalMode::encode(var->mode()) | + ContextLocalInitFlag::encode(var->initialization_flag()) | + ContextLocalMaybeAssignedFlag::encode(var->maybe_assigned()); + scope_info->set(index++, Smi::FromInt(value)); + } + + DCHECK(index == scope_info->StrongModeFreeVariableNameEntriesIndex()); + for (int i = 0; i < strong_mode_free_variable_count; ++i) { + scope_info->set(index++, *strong_mode_free_variables[i]->name()); + } + + DCHECK(index == scope_info->StrongModeFreeVariablePositionEntriesIndex()); + for (int i = 0; i < strong_mode_free_variable_count; ++i) { + // Unfortunately, the source code positions are stored as int even though + // int32_t would be enough (given the maximum source code length). + Handle<Object> start_position = factory->NewNumberFromInt( + static_cast<int32_t>(strong_mode_free_variables[i] + ->strong_mode_reference_start_position())); + scope_info->set(index++, *start_position); + Handle<Object> end_position = factory->NewNumberFromInt( + static_cast<int32_t>(strong_mode_free_variables[i] + ->strong_mode_reference_end_position())); + scope_info->set(index++, *end_position); + } + + // If the receiver is allocated, add its index. + DCHECK(index == scope_info->ReceiverEntryIndex()); + if (has_receiver) { + int var_index = scope->receiver()->index(); + scope_info->set(index++, Smi::FromInt(var_index)); + // ?? DCHECK(receiver_info != CONTEXT || var_index == + // scope_info->ContextLength() - 1); + } + + // If present, add the function variable name and its index. + DCHECK(index == scope_info->FunctionNameEntryIndex()); + if (has_function_name) { + int var_index = scope->function()->proxy()->var()->index(); + scope_info->set(index++, *scope->function()->proxy()->name()); + scope_info->set(index++, Smi::FromInt(var_index)); + DCHECK(function_name_info != CONTEXT || + var_index == scope_info->ContextLength() - 1); + } + + DCHECK(index == scope_info->length()); + DCHECK(scope->num_parameters() == scope_info->ParameterCount()); + DCHECK(scope->num_heap_slots() == scope_info->ContextLength() || + (scope->num_heap_slots() == kVariablePartIndex && + scope_info->ContextLength() == 0)); + return scope_info; +} + + +Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) { + DCHECK(isolate->bootstrapper()->IsActive()); + + const int stack_local_count = 0; + const int context_local_count = 1; + const int context_global_count = 0; + const int strong_mode_free_variable_count = 0; + const bool has_simple_parameters = true; + const VariableAllocationInfo receiver_info = CONTEXT; + const VariableAllocationInfo function_name_info = NONE; + const VariableMode function_variable_mode = VAR; + const bool has_function_name = false; + const bool has_receiver = true; + const int parameter_count = 0; + const int length = kVariablePartIndex + parameter_count + + (1 + stack_local_count) + 2 * context_local_count + + 2 * context_global_count + + 3 * strong_mode_free_variable_count + + (has_receiver ? 1 : 0) + (has_function_name ? 2 : 0); + + Factory* factory = isolate->factory(); + Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length); + + // Encode the flags. + int flags = ScopeTypeField::encode(SCRIPT_SCOPE) | + CallsEvalField::encode(false) | + LanguageModeField::encode(SLOPPY) | + DeclarationScopeField::encode(true) | + ReceiverVariableField::encode(receiver_info) | + FunctionVariableField::encode(function_name_info) | + FunctionVariableMode::encode(function_variable_mode) | + AsmModuleField::encode(false) | AsmFunctionField::encode(false) | + HasSimpleParametersField::encode(has_simple_parameters) | + FunctionKindField::encode(FunctionKind::kNormalFunction); + scope_info->SetFlags(flags); + scope_info->SetParameterCount(parameter_count); + scope_info->SetStackLocalCount(stack_local_count); + scope_info->SetContextLocalCount(context_local_count); + scope_info->SetContextGlobalCount(context_global_count); + scope_info->SetStrongModeFreeVariableCount(strong_mode_free_variable_count); + + int index = kVariablePartIndex; + const int first_slot_index = 0; + DCHECK(index == scope_info->StackLocalFirstSlotIndex()); + scope_info->set(index++, Smi::FromInt(first_slot_index)); + DCHECK(index == scope_info->StackLocalEntriesIndex()); + + // Here we add info for context-allocated "this". + DCHECK(index == scope_info->ContextLocalNameEntriesIndex()); + scope_info->set(index++, *isolate->factory()->this_string()); + DCHECK(index == scope_info->ContextLocalInfoEntriesIndex()); + const uint32_t value = ContextLocalMode::encode(CONST) | + ContextLocalInitFlag::encode(kCreatedInitialized) | + ContextLocalMaybeAssignedFlag::encode(kNotAssigned); + scope_info->set(index++, Smi::FromInt(value)); + + DCHECK(index == scope_info->StrongModeFreeVariableNameEntriesIndex()); + DCHECK(index == scope_info->StrongModeFreeVariablePositionEntriesIndex()); + + // And here we record that this scopeinfo binds a receiver. + DCHECK(index == scope_info->ReceiverEntryIndex()); + const int receiver_index = Context::MIN_CONTEXT_SLOTS + 0; + scope_info->set(index++, Smi::FromInt(receiver_index)); + + DCHECK(index == scope_info->FunctionNameEntryIndex()); + + DCHECK_EQ(index, scope_info->length()); + DCHECK_EQ(scope_info->ParameterCount(), 0); + DCHECK_EQ(scope_info->ContextLength(), Context::MIN_CONTEXT_SLOTS + 1); + + return scope_info; +} + + +ScopeInfo* ScopeInfo::Empty(Isolate* isolate) { + return reinterpret_cast<ScopeInfo*>(isolate->heap()->empty_fixed_array()); +} + + +ScopeType ScopeInfo::scope_type() { + DCHECK(length() > 0); + return ScopeTypeField::decode(Flags()); +} + + +bool ScopeInfo::CallsEval() { + return length() > 0 && CallsEvalField::decode(Flags()); +} + + +LanguageMode ScopeInfo::language_mode() { + return length() > 0 ? LanguageModeField::decode(Flags()) : SLOPPY; +} + + +bool ScopeInfo::is_declaration_scope() { + return DeclarationScopeField::decode(Flags()); +} + + +int ScopeInfo::LocalCount() { + return StackLocalCount() + ContextLocalCount(); +} + + +int ScopeInfo::StackSlotCount() { + if (length() > 0) { + bool function_name_stack_slot = + FunctionVariableField::decode(Flags()) == STACK; + return StackLocalCount() + (function_name_stack_slot ? 1 : 0); + } + return 0; +} + + +int ScopeInfo::ContextLength() { + if (length() > 0) { + int context_locals = ContextLocalCount(); + int context_globals = ContextGlobalCount(); + bool function_name_context_slot = + FunctionVariableField::decode(Flags()) == CONTEXT; + bool has_context = context_locals > 0 || context_globals > 0 || + function_name_context_slot || + scope_type() == WITH_SCOPE || + (scope_type() == BLOCK_SCOPE && CallsSloppyEval() && + is_declaration_scope()) || + (scope_type() == FUNCTION_SCOPE && CallsSloppyEval()) || + scope_type() == MODULE_SCOPE; + + if (has_context) { + return Context::MIN_CONTEXT_SLOTS + context_locals + context_globals + + (function_name_context_slot ? 1 : 0); + } + } + return 0; +} + + +bool ScopeInfo::HasReceiver() { + if (length() > 0) { + return NONE != ReceiverVariableField::decode(Flags()); + } else { + return false; + } +} + + +bool ScopeInfo::HasAllocatedReceiver() { + if (length() > 0) { + VariableAllocationInfo allocation = ReceiverVariableField::decode(Flags()); + return allocation == STACK || allocation == CONTEXT; + } else { + return false; + } +} + + +bool ScopeInfo::HasNewTarget() { return HasNewTargetField::decode(Flags()); } + + +bool ScopeInfo::HasFunctionName() { + if (length() > 0) { + return NONE != FunctionVariableField::decode(Flags()); + } else { + return false; + } +} + + +bool ScopeInfo::HasHeapAllocatedLocals() { + if (length() > 0) { + return ContextLocalCount() > 0; + } else { + return false; + } +} + + +bool ScopeInfo::HasContext() { + return ContextLength() > 0; +} + + +String* ScopeInfo::FunctionName() { + DCHECK(HasFunctionName()); + return String::cast(get(FunctionNameEntryIndex())); +} + + +String* ScopeInfo::ParameterName(int var) { + DCHECK(0 <= var && var < ParameterCount()); + int info_index = ParameterEntriesIndex() + var; + return String::cast(get(info_index)); +} + + +String* ScopeInfo::LocalName(int var) { + DCHECK(0 <= var && var < LocalCount()); + DCHECK(StackLocalEntriesIndex() + StackLocalCount() == + ContextLocalNameEntriesIndex()); + int info_index = StackLocalEntriesIndex() + var; + return String::cast(get(info_index)); +} + + +String* ScopeInfo::StackLocalName(int var) { + DCHECK(0 <= var && var < StackLocalCount()); + int info_index = StackLocalEntriesIndex() + var; + return String::cast(get(info_index)); +} + + +int ScopeInfo::StackLocalIndex(int var) { + DCHECK(0 <= var && var < StackLocalCount()); + int first_slot_index = Smi::cast(get(StackLocalFirstSlotIndex()))->value(); + return first_slot_index + var; +} + + +String* ScopeInfo::ContextLocalName(int var) { + DCHECK(0 <= var && var < ContextLocalCount() + ContextGlobalCount()); + int info_index = ContextLocalNameEntriesIndex() + var; + return String::cast(get(info_index)); +} + + +VariableMode ScopeInfo::ContextLocalMode(int var) { + DCHECK(0 <= var && var < ContextLocalCount() + ContextGlobalCount()); + int info_index = ContextLocalInfoEntriesIndex() + var; + int value = Smi::cast(get(info_index))->value(); + return ContextLocalMode::decode(value); +} + + +InitializationFlag ScopeInfo::ContextLocalInitFlag(int var) { + DCHECK(0 <= var && var < ContextLocalCount() + ContextGlobalCount()); + int info_index = ContextLocalInfoEntriesIndex() + var; + int value = Smi::cast(get(info_index))->value(); + return ContextLocalInitFlag::decode(value); +} + + +MaybeAssignedFlag ScopeInfo::ContextLocalMaybeAssignedFlag(int var) { + DCHECK(0 <= var && var < ContextLocalCount() + ContextGlobalCount()); + int info_index = ContextLocalInfoEntriesIndex() + var; + int value = Smi::cast(get(info_index))->value(); + return ContextLocalMaybeAssignedFlag::decode(value); +} + + +bool ScopeInfo::LocalIsSynthetic(int var) { + DCHECK(0 <= var && var < LocalCount()); + // There's currently no flag stored on the ScopeInfo to indicate that a + // variable is a compiler-introduced temporary. However, to avoid conflict + // with user declarations, the current temporaries like .generator_object and + // .result start with a dot, so we can use that as a flag. It's a hack! + Handle<String> name(LocalName(var)); + return (name->length() > 0 && name->Get(0) == '.') || + name->Equals(*GetIsolate()->factory()->this_string()); +} + + +String* ScopeInfo::StrongModeFreeVariableName(int var) { + DCHECK(0 <= var && var < StrongModeFreeVariableCount()); + int info_index = StrongModeFreeVariableNameEntriesIndex() + var; + return String::cast(get(info_index)); +} + + +int ScopeInfo::StrongModeFreeVariableStartPosition(int var) { + DCHECK(0 <= var && var < StrongModeFreeVariableCount()); + int info_index = StrongModeFreeVariablePositionEntriesIndex() + var * 2; + int32_t value = 0; + bool ok = get(info_index)->ToInt32(&value); + USE(ok); + DCHECK(ok); + return value; +} + + +int ScopeInfo::StrongModeFreeVariableEndPosition(int var) { + DCHECK(0 <= var && var < StrongModeFreeVariableCount()); + int info_index = StrongModeFreeVariablePositionEntriesIndex() + var * 2 + 1; + int32_t value = 0; + bool ok = get(info_index)->ToInt32(&value); + USE(ok); + DCHECK(ok); + return value; +} + + +int ScopeInfo::StackSlotIndex(String* name) { + DCHECK(name->IsInternalizedString()); + if (length() > 0) { + int first_slot_index = Smi::cast(get(StackLocalFirstSlotIndex()))->value(); + int start = StackLocalEntriesIndex(); + int end = StackLocalEntriesIndex() + StackLocalCount(); + for (int i = start; i < end; ++i) { + if (name == get(i)) { + return i - start + first_slot_index; + } + } + } + return -1; +} + + +int ScopeInfo::ContextSlotIndex(Handle<ScopeInfo> scope_info, + Handle<String> name, VariableMode* mode, + InitializationFlag* init_flag, + MaybeAssignedFlag* maybe_assigned_flag) { + DCHECK(name->IsInternalizedString()); + DCHECK(mode != NULL); + DCHECK(init_flag != NULL); + if (scope_info->length() > 0) { + ContextSlotCache* context_slot_cache = + scope_info->GetIsolate()->context_slot_cache(); + int result = context_slot_cache->Lookup(*scope_info, *name, mode, init_flag, + maybe_assigned_flag); + if (result != ContextSlotCache::kNotFound) { + DCHECK(result < scope_info->ContextLength()); + return result; + } + + int start = scope_info->ContextLocalNameEntriesIndex(); + int end = scope_info->ContextLocalNameEntriesIndex() + + scope_info->ContextLocalCount(); + for (int i = start; i < end; ++i) { + if (*name == scope_info->get(i)) { + int var = i - start; + *mode = scope_info->ContextLocalMode(var); + *init_flag = scope_info->ContextLocalInitFlag(var); + *maybe_assigned_flag = scope_info->ContextLocalMaybeAssignedFlag(var); + result = Context::MIN_CONTEXT_SLOTS + var; + + context_slot_cache->Update(scope_info, name, *mode, *init_flag, + *maybe_assigned_flag, result); + DCHECK(result < scope_info->ContextLength()); + return result; + } + } + // Cache as not found. Mode, init flag and maybe assigned flag don't matter. + context_slot_cache->Update(scope_info, name, TEMPORARY, + kNeedsInitialization, kNotAssigned, -1); + } + return -1; +} + + +int ScopeInfo::ContextGlobalSlotIndex(Handle<ScopeInfo> scope_info, + Handle<String> name, VariableMode* mode, + InitializationFlag* init_flag, + MaybeAssignedFlag* maybe_assigned_flag) { + DCHECK(name->IsInternalizedString()); + DCHECK(mode != NULL); + DCHECK(init_flag != NULL); + if (scope_info->length() > 0) { + // This is to ensure that ContextLocalMode() and co. queries would work. + DCHECK_EQ(scope_info->ContextGlobalNameEntriesIndex(), + scope_info->ContextLocalNameEntriesIndex() + + scope_info->ContextLocalCount()); + int base = scope_info->ContextLocalNameEntriesIndex(); + int start = scope_info->ContextGlobalNameEntriesIndex(); + int end = scope_info->ContextGlobalNameEntriesIndex() + + scope_info->ContextGlobalCount(); + for (int i = start; i < end; ++i) { + if (*name == scope_info->get(i)) { + int var = i - base; + *mode = scope_info->ContextLocalMode(var); + *init_flag = scope_info->ContextLocalInitFlag(var); + *maybe_assigned_flag = scope_info->ContextLocalMaybeAssignedFlag(var); + int result = Context::MIN_CONTEXT_SLOTS + var; + DCHECK(result < scope_info->ContextLength()); + return result; + } + } + } + return -1; +} + + +String* ScopeInfo::ContextSlotName(int slot_index) { + int const var = slot_index - Context::MIN_CONTEXT_SLOTS; + DCHECK_LE(0, var); + DCHECK_LT(var, ContextLocalCount() + ContextGlobalCount()); + return ContextLocalName(var); +} + + +int ScopeInfo::ParameterIndex(String* name) { + DCHECK(name->IsInternalizedString()); + if (length() > 0) { + // We must read parameters from the end since for + // multiply declared parameters the value of the + // last declaration of that parameter is used + // inside a function (and thus we need to look + // at the last index). Was bug# 1110337. + int start = ParameterEntriesIndex(); + int end = ParameterEntriesIndex() + ParameterCount(); + for (int i = end - 1; i >= start; --i) { + if (name == get(i)) { + return i - start; + } + } + } + return -1; +} + + +int ScopeInfo::ReceiverContextSlotIndex() { + if (length() > 0 && ReceiverVariableField::decode(Flags()) == CONTEXT) + return Smi::cast(get(ReceiverEntryIndex()))->value(); + return -1; +} + + +int ScopeInfo::FunctionContextSlotIndex(String* name, VariableMode* mode) { + DCHECK(name->IsInternalizedString()); + DCHECK(mode != NULL); + if (length() > 0) { + if (FunctionVariableField::decode(Flags()) == CONTEXT && + FunctionName() == name) { + *mode = FunctionVariableMode::decode(Flags()); + return Smi::cast(get(FunctionNameEntryIndex() + 1))->value(); + } + } + return -1; +} + + +FunctionKind ScopeInfo::function_kind() { + return FunctionKindField::decode(Flags()); +} + + +int ScopeInfo::ParameterEntriesIndex() { + DCHECK(length() > 0); + return kVariablePartIndex; +} + + +int ScopeInfo::StackLocalFirstSlotIndex() { + return ParameterEntriesIndex() + ParameterCount(); +} + + +int ScopeInfo::StackLocalEntriesIndex() { + return StackLocalFirstSlotIndex() + 1; +} + + +int ScopeInfo::ContextLocalNameEntriesIndex() { + return StackLocalEntriesIndex() + StackLocalCount(); +} + + +int ScopeInfo::ContextGlobalNameEntriesIndex() { + return ContextLocalNameEntriesIndex() + ContextLocalCount(); +} + + +int ScopeInfo::ContextLocalInfoEntriesIndex() { + return ContextGlobalNameEntriesIndex() + ContextGlobalCount(); +} + + +int ScopeInfo::ContextGlobalInfoEntriesIndex() { + return ContextLocalInfoEntriesIndex() + ContextLocalCount(); +} + + +int ScopeInfo::StrongModeFreeVariableNameEntriesIndex() { + return ContextGlobalInfoEntriesIndex() + ContextGlobalCount(); +} + + +int ScopeInfo::StrongModeFreeVariablePositionEntriesIndex() { + return StrongModeFreeVariableNameEntriesIndex() + + StrongModeFreeVariableCount(); +} + + +int ScopeInfo::ReceiverEntryIndex() { + return StrongModeFreeVariablePositionEntriesIndex() + + 2 * StrongModeFreeVariableCount(); +} + + +int ScopeInfo::FunctionNameEntryIndex() { + return ReceiverEntryIndex() + (HasAllocatedReceiver() ? 1 : 0); +} + + +int ContextSlotCache::Hash(Object* data, String* name) { + // Uses only lower 32 bits if pointers are larger. + uintptr_t addr_hash = + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(data)) >> 2; + return static_cast<int>((addr_hash ^ name->Hash()) % kLength); +} + + +int ContextSlotCache::Lookup(Object* data, String* name, VariableMode* mode, + InitializationFlag* init_flag, + MaybeAssignedFlag* maybe_assigned_flag) { + int index = Hash(data, name); + Key& key = keys_[index]; + if ((key.data == data) && key.name->Equals(name)) { + Value result(values_[index]); + if (mode != NULL) *mode = result.mode(); + if (init_flag != NULL) *init_flag = result.initialization_flag(); + if (maybe_assigned_flag != NULL) + *maybe_assigned_flag = result.maybe_assigned_flag(); + return result.index() + kNotFound; + } + return kNotFound; +} + + +void ContextSlotCache::Update(Handle<Object> data, Handle<String> name, + VariableMode mode, InitializationFlag init_flag, + MaybeAssignedFlag maybe_assigned_flag, + int slot_index) { + DisallowHeapAllocation no_gc; + Handle<String> internalized_name; + DCHECK(slot_index > kNotFound); + if (StringTable::InternalizeStringIfExists(name->GetIsolate(), name). + ToHandle(&internalized_name)) { + int index = Hash(*data, *internalized_name); + Key& key = keys_[index]; + key.data = *data; + key.name = *internalized_name; + // Please note value only takes a uint as index. + values_[index] = Value(mode, init_flag, maybe_assigned_flag, + slot_index - kNotFound).raw(); +#ifdef DEBUG + ValidateEntry(data, name, mode, init_flag, maybe_assigned_flag, slot_index); +#endif + } +} + + +void ContextSlotCache::Clear() { + for (int index = 0; index < kLength; index++) keys_[index].data = NULL; +} + + +#ifdef DEBUG + +void ContextSlotCache::ValidateEntry(Handle<Object> data, Handle<String> name, + VariableMode mode, + InitializationFlag init_flag, + MaybeAssignedFlag maybe_assigned_flag, + int slot_index) { + DisallowHeapAllocation no_gc; + Handle<String> internalized_name; + if (StringTable::InternalizeStringIfExists(name->GetIsolate(), name). + ToHandle(&internalized_name)) { + int index = Hash(*data, *name); + Key& key = keys_[index]; + DCHECK(key.data == *data); + DCHECK(key.name->Equals(*name)); + Value result(values_[index]); + DCHECK(result.mode() == mode); + DCHECK(result.initialization_flag() == init_flag); + DCHECK(result.maybe_assigned_flag() == maybe_assigned_flag); + DCHECK(result.index() + kNotFound == slot_index); + } +} + + +static void PrintList(const char* list_name, + int nof_internal_slots, + int start, + int end, + ScopeInfo* scope_info) { + if (start < end) { + PrintF("\n // %s\n", list_name); + if (nof_internal_slots > 0) { + PrintF(" %2d - %2d [internal slots]\n", 0 , nof_internal_slots - 1); + } + for (int i = nof_internal_slots; start < end; ++i, ++start) { + PrintF(" %2d ", i); + String::cast(scope_info->get(start))->ShortPrint(); + PrintF("\n"); + } + } +} + + +void ScopeInfo::Print() { + PrintF("ScopeInfo "); + if (HasFunctionName()) { + FunctionName()->ShortPrint(); + } else { + PrintF("/* no function name */"); + } + PrintF("{"); + + if (length() > 0) { + PrintList("parameters", 0, ParameterEntriesIndex(), + ParameterEntriesIndex() + ParameterCount(), this); + PrintList("stack slots", 0, StackLocalEntriesIndex(), + StackLocalEntriesIndex() + StackLocalCount(), this); + PrintList("context slots", Context::MIN_CONTEXT_SLOTS, + ContextLocalNameEntriesIndex(), + ContextLocalNameEntriesIndex() + ContextLocalCount(), this); + } + + PrintF("}\n"); +} +#endif // DEBUG + + +//--------------------------------------------------------------------------- +// ModuleInfo. + +Handle<ModuleInfo> ModuleInfo::Create(Isolate* isolate, + ModuleDescriptor* descriptor, + Scope* scope) { + Handle<ModuleInfo> info = Allocate(isolate, descriptor->Length()); + info->set_host_index(descriptor->Index()); + int i = 0; + for (ModuleDescriptor::Iterator it = descriptor->iterator(); !it.done(); + it.Advance(), ++i) { + Variable* var = scope->LookupLocal(it.local_name()); + info->set_name(i, *(it.export_name()->string())); + info->set_mode(i, var->mode()); + DCHECK(var->index() >= 0); + info->set_index(i, var->index()); + } + DCHECK(i == info->length()); + return info; +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/scopeinfo.h b/deps/v8/src/ast/scopeinfo.h new file mode 100644 index 0000000000..489a672ed8 --- /dev/null +++ b/deps/v8/src/ast/scopeinfo.h @@ -0,0 +1,175 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_SCOPEINFO_H_ +#define V8_AST_SCOPEINFO_H_ + +#include "src/allocation.h" +#include "src/ast/modules.h" +#include "src/ast/variables.h" + +namespace v8 { +namespace internal { + +// Cache for mapping (data, property name) into context slot index. +// The cache contains both positive and negative results. +// Slot index equals -1 means the property is absent. +// Cleared at startup and prior to mark sweep collection. +class ContextSlotCache { + public: + // Lookup context slot index for (data, name). + // If absent, kNotFound is returned. + int Lookup(Object* data, String* name, VariableMode* mode, + InitializationFlag* init_flag, + MaybeAssignedFlag* maybe_assigned_flag); + + // Update an element in the cache. + void Update(Handle<Object> data, Handle<String> name, VariableMode mode, + InitializationFlag init_flag, + MaybeAssignedFlag maybe_assigned_flag, int slot_index); + + // Clear the cache. + void Clear(); + + static const int kNotFound = -2; + + private: + ContextSlotCache() { + for (int i = 0; i < kLength; ++i) { + keys_[i].data = NULL; + keys_[i].name = NULL; + values_[i] = kNotFound; + } + } + + inline static int Hash(Object* data, String* name); + +#ifdef DEBUG + void ValidateEntry(Handle<Object> data, Handle<String> name, + VariableMode mode, InitializationFlag init_flag, + MaybeAssignedFlag maybe_assigned_flag, int slot_index); +#endif + + static const int kLength = 256; + struct Key { + Object* data; + String* name; + }; + + struct Value { + Value(VariableMode mode, InitializationFlag init_flag, + MaybeAssignedFlag maybe_assigned_flag, int index) { + DCHECK(ModeField::is_valid(mode)); + DCHECK(InitField::is_valid(init_flag)); + DCHECK(MaybeAssignedField::is_valid(maybe_assigned_flag)); + DCHECK(IndexField::is_valid(index)); + value_ = ModeField::encode(mode) | IndexField::encode(index) | + InitField::encode(init_flag) | + MaybeAssignedField::encode(maybe_assigned_flag); + DCHECK(mode == this->mode()); + DCHECK(init_flag == this->initialization_flag()); + DCHECK(maybe_assigned_flag == this->maybe_assigned_flag()); + DCHECK(index == this->index()); + } + + explicit inline Value(uint32_t value) : value_(value) {} + + uint32_t raw() { return value_; } + + VariableMode mode() { return ModeField::decode(value_); } + + InitializationFlag initialization_flag() { + return InitField::decode(value_); + } + + MaybeAssignedFlag maybe_assigned_flag() { + return MaybeAssignedField::decode(value_); + } + + int index() { return IndexField::decode(value_); } + + // Bit fields in value_ (type, shift, size). Must be public so the + // constants can be embedded in generated code. + class ModeField : public BitField<VariableMode, 0, 4> {}; + class InitField : public BitField<InitializationFlag, 4, 1> {}; + class MaybeAssignedField : public BitField<MaybeAssignedFlag, 5, 1> {}; + class IndexField : public BitField<int, 6, 32 - 6> {}; + + private: + uint32_t value_; + }; + + Key keys_[kLength]; + uint32_t values_[kLength]; + + friend class Isolate; + DISALLOW_COPY_AND_ASSIGN(ContextSlotCache); +}; + + + + +//--------------------------------------------------------------------------- +// Auxiliary class used for the description of module instances. +// Used by Runtime_DeclareModules. + +class ModuleInfo: public FixedArray { + public: + static ModuleInfo* cast(Object* description) { + return static_cast<ModuleInfo*>(FixedArray::cast(description)); + } + + static Handle<ModuleInfo> Create(Isolate* isolate, + ModuleDescriptor* descriptor, Scope* scope); + + // Index of module's context in host context. + int host_index() { return Smi::cast(get(HOST_OFFSET))->value(); } + + // Name, mode, and index of the i-th export, respectively. + // For value exports, the index is the slot of the value in the module + // context, for exported modules it is the slot index of the + // referred module's context in the host context. + // TODO(rossberg): This format cannot yet handle exports of modules declared + // in earlier scripts. + String* name(int i) { return String::cast(get(name_offset(i))); } + VariableMode mode(int i) { + return static_cast<VariableMode>(Smi::cast(get(mode_offset(i)))->value()); + } + int index(int i) { return Smi::cast(get(index_offset(i)))->value(); } + + int length() { return (FixedArray::length() - HEADER_SIZE) / ITEM_SIZE; } + + private: + // The internal format is: Index, (Name, VariableMode, Index)* + enum { + HOST_OFFSET, + NAME_OFFSET, + MODE_OFFSET, + INDEX_OFFSET, + HEADER_SIZE = NAME_OFFSET, + ITEM_SIZE = INDEX_OFFSET - NAME_OFFSET + 1 + }; + inline int name_offset(int i) { return NAME_OFFSET + i * ITEM_SIZE; } + inline int mode_offset(int i) { return MODE_OFFSET + i * ITEM_SIZE; } + inline int index_offset(int i) { return INDEX_OFFSET + i * ITEM_SIZE; } + + static Handle<ModuleInfo> Allocate(Isolate* isolate, int length) { + return Handle<ModuleInfo>::cast( + isolate->factory()->NewFixedArray(HEADER_SIZE + ITEM_SIZE * length)); + } + void set_host_index(int index) { set(HOST_OFFSET, Smi::FromInt(index)); } + void set_name(int i, String* name) { set(name_offset(i), name); } + void set_mode(int i, VariableMode mode) { + set(mode_offset(i), Smi::FromInt(mode)); + } + void set_index(int i, int index) { + set(index_offset(i), Smi::FromInt(index)); + } +}; + + +} // namespace internal +} // namespace v8 + +#endif // V8_AST_SCOPEINFO_H_ diff --git a/deps/v8/src/ast/scopes.cc b/deps/v8/src/ast/scopes.cc new file mode 100644 index 0000000000..c2b05b7c04 --- /dev/null +++ b/deps/v8/src/ast/scopes.cc @@ -0,0 +1,1698 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/scopes.h" + +#include "src/accessors.h" +#include "src/ast/scopeinfo.h" +#include "src/bootstrapper.h" +#include "src/messages.h" +#include "src/parsing/parser.h" // for ParseInfo + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// Implementation of LocalsMap +// +// Note: We are storing the handle locations as key values in the hash map. +// When inserting a new variable via Declare(), we rely on the fact that +// the handle location remains alive for the duration of that variable +// use. Because a Variable holding a handle with the same location exists +// this is ensured. + +VariableMap::VariableMap(Zone* zone) + : ZoneHashMap(ZoneHashMap::PointersMatch, 8, ZoneAllocationPolicy(zone)), + zone_(zone) {} +VariableMap::~VariableMap() {} + + +Variable* VariableMap::Declare(Scope* scope, const AstRawString* name, + VariableMode mode, Variable::Kind kind, + InitializationFlag initialization_flag, + MaybeAssignedFlag maybe_assigned_flag, + int declaration_group_start) { + // AstRawStrings are unambiguous, i.e., the same string is always represented + // by the same AstRawString*. + // FIXME(marja): fix the type of Lookup. + Entry* p = + ZoneHashMap::LookupOrInsert(const_cast<AstRawString*>(name), name->hash(), + ZoneAllocationPolicy(zone())); + if (p->value == NULL) { + // The variable has not been declared yet -> insert it. + DCHECK(p->key == name); + if (kind == Variable::CLASS) { + p->value = new (zone()) + ClassVariable(scope, name, mode, initialization_flag, + maybe_assigned_flag, declaration_group_start); + } else { + p->value = new (zone()) Variable( + scope, name, mode, kind, initialization_flag, maybe_assigned_flag); + } + } + return reinterpret_cast<Variable*>(p->value); +} + + +Variable* VariableMap::Lookup(const AstRawString* name) { + Entry* p = ZoneHashMap::Lookup(const_cast<AstRawString*>(name), name->hash()); + if (p != NULL) { + DCHECK(reinterpret_cast<const AstRawString*>(p->key) == name); + DCHECK(p->value != NULL); + return reinterpret_cast<Variable*>(p->value); + } + return NULL; +} + + +SloppyBlockFunctionMap::SloppyBlockFunctionMap(Zone* zone) + : ZoneHashMap(ZoneHashMap::PointersMatch, 8, ZoneAllocationPolicy(zone)), + zone_(zone) {} +SloppyBlockFunctionMap::~SloppyBlockFunctionMap() {} + + +void SloppyBlockFunctionMap::Declare(const AstRawString* name, + SloppyBlockFunctionStatement* stmt) { + // AstRawStrings are unambiguous, i.e., the same string is always represented + // by the same AstRawString*. + Entry* p = + ZoneHashMap::LookupOrInsert(const_cast<AstRawString*>(name), name->hash(), + ZoneAllocationPolicy(zone_)); + if (p->value == nullptr) { + p->value = new (zone_->New(sizeof(Vector))) Vector(zone_); + } + Vector* delegates = static_cast<Vector*>(p->value); + delegates->push_back(stmt); +} + + +// ---------------------------------------------------------------------------- +// Implementation of Scope + +Scope::Scope(Zone* zone, Scope* outer_scope, ScopeType scope_type, + AstValueFactory* ast_value_factory, FunctionKind function_kind) + : inner_scopes_(4, zone), + variables_(zone), + temps_(4, zone), + params_(4, zone), + unresolved_(16, zone), + decls_(4, zone), + module_descriptor_( + scope_type == MODULE_SCOPE ? ModuleDescriptor::New(zone) : NULL), + sloppy_block_function_map_(zone), + already_resolved_(false), + ast_value_factory_(ast_value_factory), + zone_(zone), + class_declaration_group_start_(-1) { + SetDefaults(scope_type, outer_scope, Handle<ScopeInfo>::null(), + function_kind); + // The outermost scope must be a script scope. + DCHECK(scope_type == SCRIPT_SCOPE || outer_scope != NULL); + DCHECK(!HasIllegalRedeclaration()); +} + + +Scope::Scope(Zone* zone, Scope* inner_scope, ScopeType scope_type, + Handle<ScopeInfo> scope_info, AstValueFactory* value_factory) + : inner_scopes_(4, zone), + variables_(zone), + temps_(4, zone), + params_(4, zone), + unresolved_(16, zone), + decls_(4, zone), + module_descriptor_(NULL), + sloppy_block_function_map_(zone), + already_resolved_(true), + ast_value_factory_(value_factory), + zone_(zone), + class_declaration_group_start_(-1) { + SetDefaults(scope_type, NULL, scope_info); + if (!scope_info.is_null()) { + num_heap_slots_ = scope_info_->ContextLength(); + } + // Ensure at least MIN_CONTEXT_SLOTS to indicate a materialized context. + num_heap_slots_ = Max(num_heap_slots_, + static_cast<int>(Context::MIN_CONTEXT_SLOTS)); + AddInnerScope(inner_scope); +} + + +Scope::Scope(Zone* zone, Scope* inner_scope, + const AstRawString* catch_variable_name, + AstValueFactory* value_factory) + : inner_scopes_(1, zone), + variables_(zone), + temps_(0, zone), + params_(0, zone), + unresolved_(0, zone), + decls_(0, zone), + module_descriptor_(NULL), + sloppy_block_function_map_(zone), + already_resolved_(true), + ast_value_factory_(value_factory), + zone_(zone), + class_declaration_group_start_(-1) { + SetDefaults(CATCH_SCOPE, NULL, Handle<ScopeInfo>::null()); + AddInnerScope(inner_scope); + ++num_var_or_const_; + num_heap_slots_ = Context::MIN_CONTEXT_SLOTS; + Variable* variable = variables_.Declare(this, + catch_variable_name, + VAR, + Variable::NORMAL, + kCreatedInitialized); + AllocateHeapSlot(variable); +} + + +void Scope::SetDefaults(ScopeType scope_type, Scope* outer_scope, + Handle<ScopeInfo> scope_info, + FunctionKind function_kind) { + outer_scope_ = outer_scope; + scope_type_ = scope_type; + is_declaration_scope_ = + is_eval_scope() || is_function_scope() || + is_module_scope() || is_script_scope(); + function_kind_ = function_kind; + scope_name_ = ast_value_factory_->empty_string(); + dynamics_ = nullptr; + receiver_ = nullptr; + new_target_ = nullptr; + function_ = nullptr; + arguments_ = nullptr; + this_function_ = nullptr; + illegal_redecl_ = nullptr; + scope_inside_with_ = false; + scope_contains_with_ = false; + scope_calls_eval_ = false; + scope_uses_arguments_ = false; + scope_uses_super_property_ = false; + asm_module_ = false; + asm_function_ = outer_scope != NULL && outer_scope->asm_module_; + // Inherit the language mode from the parent scope. + language_mode_ = outer_scope != NULL ? outer_scope->language_mode_ : SLOPPY; + outer_scope_calls_sloppy_eval_ = false; + inner_scope_calls_eval_ = false; + scope_nonlinear_ = false; + force_eager_compilation_ = false; + force_context_allocation_ = (outer_scope != NULL && !is_function_scope()) + ? outer_scope->has_forced_context_allocation() : false; + num_var_or_const_ = 0; + num_stack_slots_ = 0; + num_heap_slots_ = 0; + num_global_slots_ = 0; + arity_ = 0; + has_simple_parameters_ = true; + rest_parameter_ = NULL; + rest_index_ = -1; + scope_info_ = scope_info; + start_position_ = RelocInfo::kNoPosition; + end_position_ = RelocInfo::kNoPosition; + if (!scope_info.is_null()) { + scope_calls_eval_ = scope_info->CallsEval(); + language_mode_ = scope_info->language_mode(); + is_declaration_scope_ = scope_info->is_declaration_scope(); + function_kind_ = scope_info->function_kind(); + } +} + + +Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone, + Context* context, Scope* script_scope) { + // Reconstruct the outer scope chain from a closure's context chain. + Scope* current_scope = NULL; + Scope* innermost_scope = NULL; + bool contains_with = false; + while (!context->IsNativeContext()) { + if (context->IsWithContext()) { + Scope* with_scope = new (zone) + Scope(zone, current_scope, WITH_SCOPE, Handle<ScopeInfo>::null(), + script_scope->ast_value_factory_); + current_scope = with_scope; + // All the inner scopes are inside a with. + contains_with = true; + for (Scope* s = innermost_scope; s != NULL; s = s->outer_scope()) { + s->scope_inside_with_ = true; + } + } else if (context->IsScriptContext()) { + ScopeInfo* scope_info = context->scope_info(); + current_scope = new (zone) Scope(zone, current_scope, SCRIPT_SCOPE, + Handle<ScopeInfo>(scope_info), + script_scope->ast_value_factory_); + } else if (context->IsModuleContext()) { + ScopeInfo* scope_info = context->module()->scope_info(); + current_scope = new (zone) Scope(zone, current_scope, MODULE_SCOPE, + Handle<ScopeInfo>(scope_info), + script_scope->ast_value_factory_); + } else if (context->IsFunctionContext()) { + ScopeInfo* scope_info = context->closure()->shared()->scope_info(); + current_scope = new (zone) Scope(zone, current_scope, FUNCTION_SCOPE, + Handle<ScopeInfo>(scope_info), + script_scope->ast_value_factory_); + if (scope_info->IsAsmFunction()) current_scope->asm_function_ = true; + if (scope_info->IsAsmModule()) current_scope->asm_module_ = true; + } else if (context->IsBlockContext()) { + ScopeInfo* scope_info = context->scope_info(); + current_scope = new (zone) + Scope(zone, current_scope, BLOCK_SCOPE, Handle<ScopeInfo>(scope_info), + script_scope->ast_value_factory_); + } else { + DCHECK(context->IsCatchContext()); + String* name = context->catch_name(); + current_scope = new (zone) Scope( + zone, current_scope, + script_scope->ast_value_factory_->GetString(Handle<String>(name)), + script_scope->ast_value_factory_); + } + if (contains_with) current_scope->RecordWithStatement(); + if (innermost_scope == NULL) innermost_scope = current_scope; + + // Forget about a with when we move to a context for a different function. + if (context->previous()->closure() != context->closure()) { + contains_with = false; + } + context = context->previous(); + } + + script_scope->AddInnerScope(current_scope); + script_scope->PropagateScopeInfo(false); + return (innermost_scope == NULL) ? script_scope : innermost_scope; +} + + +bool Scope::Analyze(ParseInfo* info) { + DCHECK(info->literal() != NULL); + DCHECK(info->scope() == NULL); + Scope* scope = info->literal()->scope(); + Scope* top = scope; + + // Traverse the scope tree up to the first unresolved scope or the global + // scope and start scope resolution and variable allocation from that scope. + while (!top->is_script_scope() && + !top->outer_scope()->already_resolved()) { + top = top->outer_scope(); + } + + // Allocate the variables. + { + AstNodeFactory ast_node_factory(info->ast_value_factory()); + if (!top->AllocateVariables(info, &ast_node_factory)) { + DCHECK(top->pending_error_handler_.has_pending_error()); + top->pending_error_handler_.ThrowPendingError(info->isolate(), + info->script()); + return false; + } + } + +#ifdef DEBUG + if (info->script_is_native() ? FLAG_print_builtin_scopes + : FLAG_print_scopes) { + scope->Print(); + } +#endif + + info->set_scope(scope); + return true; +} + + +void Scope::Initialize() { + DCHECK(!already_resolved()); + + // Add this scope as a new inner scope of the outer scope. + if (outer_scope_ != NULL) { + outer_scope_->inner_scopes_.Add(this, zone()); + scope_inside_with_ = outer_scope_->scope_inside_with_ || is_with_scope(); + } else { + scope_inside_with_ = is_with_scope(); + } + + // Declare convenience variables and the receiver. + if (is_declaration_scope() && has_this_declaration()) { + bool subclass_constructor = IsSubclassConstructor(function_kind_); + Variable* var = variables_.Declare( + this, ast_value_factory_->this_string(), + subclass_constructor ? CONST : VAR, Variable::THIS, + subclass_constructor ? kNeedsInitialization : kCreatedInitialized); + receiver_ = var; + } + + if (is_function_scope() && !is_arrow_scope()) { + // Declare 'arguments' variable which exists in all non arrow functions. + // Note that it might never be accessed, in which case it won't be + // allocated during variable allocation. + variables_.Declare(this, ast_value_factory_->arguments_string(), VAR, + Variable::ARGUMENTS, kCreatedInitialized); + + variables_.Declare(this, ast_value_factory_->new_target_string(), CONST, + Variable::NORMAL, kCreatedInitialized); + + if (IsConciseMethod(function_kind_) || IsClassConstructor(function_kind_) || + IsAccessorFunction(function_kind_)) { + variables_.Declare(this, ast_value_factory_->this_function_string(), + CONST, Variable::NORMAL, kCreatedInitialized); + } + } +} + + +Scope* Scope::FinalizeBlockScope() { + DCHECK(is_block_scope()); + DCHECK(temps_.is_empty()); + DCHECK(params_.is_empty()); + + if (num_var_or_const() > 0 || + (is_declaration_scope() && calls_sloppy_eval())) { + return this; + } + + // Remove this scope from outer scope. + outer_scope()->RemoveInnerScope(this); + + // Reparent inner scopes. + for (int i = 0; i < inner_scopes_.length(); i++) { + outer_scope()->AddInnerScope(inner_scopes_[i]); + } + + // Move unresolved variables + for (int i = 0; i < unresolved_.length(); i++) { + outer_scope()->unresolved_.Add(unresolved_[i], zone()); + } + + PropagateUsageFlagsToScope(outer_scope_); + + return NULL; +} + + +void Scope::ReplaceOuterScope(Scope* outer) { + DCHECK_NOT_NULL(outer); + DCHECK_NOT_NULL(outer_scope_); + DCHECK(!already_resolved()); + DCHECK(!outer->already_resolved()); + DCHECK(!outer_scope_->already_resolved()); + outer_scope_->RemoveInnerScope(this); + outer->AddInnerScope(this); + outer_scope_ = outer; +} + + +void Scope::PropagateUsageFlagsToScope(Scope* other) { + DCHECK_NOT_NULL(other); + DCHECK(!already_resolved()); + DCHECK(!other->already_resolved()); + if (uses_arguments()) other->RecordArgumentsUsage(); + if (uses_super_property()) other->RecordSuperPropertyUsage(); + if (calls_eval()) other->RecordEvalCall(); + if (scope_contains_with_) other->RecordWithStatement(); +} + + +Variable* Scope::LookupLocal(const AstRawString* name) { + Variable* result = variables_.Lookup(name); + if (result != NULL || scope_info_.is_null()) { + return result; + } + Handle<String> name_handle = name->string(); + // The Scope is backed up by ScopeInfo. This means it cannot operate in a + // heap-independent mode, and all strings must be internalized immediately. So + // it's ok to get the Handle<String> here. + // If we have a serialized scope info, we might find the variable there. + // There should be no local slot with the given name. + DCHECK(scope_info_->StackSlotIndex(*name_handle) < 0 || is_block_scope()); + + // Check context slot lookup. + VariableMode mode; + VariableLocation location = VariableLocation::CONTEXT; + InitializationFlag init_flag; + MaybeAssignedFlag maybe_assigned_flag; + int index = ScopeInfo::ContextSlotIndex(scope_info_, name_handle, &mode, + &init_flag, &maybe_assigned_flag); + if (index < 0) { + location = VariableLocation::GLOBAL; + index = ScopeInfo::ContextGlobalSlotIndex(scope_info_, name_handle, &mode, + &init_flag, &maybe_assigned_flag); + } + if (index < 0) { + // Check parameters. + index = scope_info_->ParameterIndex(*name_handle); + if (index < 0) return NULL; + + mode = DYNAMIC; + location = VariableLocation::LOOKUP; + init_flag = kCreatedInitialized; + // Be conservative and flag parameters as maybe assigned. Better information + // would require ScopeInfo to serialize the maybe_assigned bit also for + // parameters. + maybe_assigned_flag = kMaybeAssigned; + } else { + DCHECK(location != VariableLocation::GLOBAL || + (is_script_scope() && IsDeclaredVariableMode(mode) && + !IsLexicalVariableMode(mode))); + } + + Variable::Kind kind = Variable::NORMAL; + if (location == VariableLocation::CONTEXT && + index == scope_info_->ReceiverContextSlotIndex()) { + kind = Variable::THIS; + } + // TODO(marja, rossberg): Correctly declare FUNCTION, CLASS, NEW_TARGET, and + // ARGUMENTS bindings as their corresponding Variable::Kind. + + Variable* var = variables_.Declare(this, name, mode, kind, init_flag, + maybe_assigned_flag); + var->AllocateTo(location, index); + return var; +} + + +Variable* Scope::LookupFunctionVar(const AstRawString* name, + AstNodeFactory* factory) { + if (function_ != NULL && function_->proxy()->raw_name() == name) { + return function_->proxy()->var(); + } else if (!scope_info_.is_null()) { + // If we are backed by a scope info, try to lookup the variable there. + VariableMode mode; + int index = scope_info_->FunctionContextSlotIndex(*(name->string()), &mode); + if (index < 0) return NULL; + Variable* var = new (zone()) + Variable(this, name, mode, Variable::NORMAL, kCreatedInitialized); + VariableProxy* proxy = factory->NewVariableProxy(var); + VariableDeclaration* declaration = factory->NewVariableDeclaration( + proxy, mode, this, RelocInfo::kNoPosition); + DeclareFunctionVar(declaration); + var->AllocateTo(VariableLocation::CONTEXT, index); + return var; + } else { + return NULL; + } +} + + +Variable* Scope::Lookup(const AstRawString* name) { + for (Scope* scope = this; + scope != NULL; + scope = scope->outer_scope()) { + Variable* var = scope->LookupLocal(name); + if (var != NULL) return var; + } + return NULL; +} + + +Variable* Scope::DeclareParameter( + const AstRawString* name, VariableMode mode, + bool is_optional, bool is_rest, bool* is_duplicate) { + DCHECK(!already_resolved()); + DCHECK(is_function_scope()); + DCHECK(!is_optional || !is_rest); + Variable* var; + if (mode == TEMPORARY) { + var = NewTemporary(name); + } else { + var = variables_.Declare(this, name, mode, Variable::NORMAL, + kCreatedInitialized); + // TODO(wingo): Avoid O(n^2) check. + *is_duplicate = IsDeclaredParameter(name); + } + if (!is_optional && !is_rest && arity_ == params_.length()) { + ++arity_; + } + if (is_rest) { + DCHECK_NULL(rest_parameter_); + rest_parameter_ = var; + rest_index_ = num_parameters(); + } + params_.Add(var, zone()); + return var; +} + + +Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode, + InitializationFlag init_flag, Variable::Kind kind, + MaybeAssignedFlag maybe_assigned_flag, + int declaration_group_start) { + DCHECK(!already_resolved()); + // This function handles VAR, LET, and CONST modes. DYNAMIC variables are + // introduces during variable allocation, and TEMPORARY variables are + // allocated via NewTemporary(). + DCHECK(IsDeclaredVariableMode(mode)); + ++num_var_or_const_; + return variables_.Declare(this, name, mode, kind, init_flag, + maybe_assigned_flag, declaration_group_start); +} + + +Variable* Scope::DeclareDynamicGlobal(const AstRawString* name) { + DCHECK(is_script_scope()); + return variables_.Declare(this, + name, + DYNAMIC_GLOBAL, + Variable::NORMAL, + kCreatedInitialized); +} + + +bool Scope::RemoveUnresolved(VariableProxy* var) { + // Most likely (always?) any variable we want to remove + // was just added before, so we search backwards. + for (int i = unresolved_.length(); i-- > 0;) { + if (unresolved_[i] == var) { + unresolved_.Remove(i); + return true; + } + } + return false; +} + + +Variable* Scope::NewTemporary(const AstRawString* name) { + DCHECK(!already_resolved()); + Scope* scope = this->ClosureScope(); + Variable* var = new(zone()) Variable(scope, + name, + TEMPORARY, + Variable::NORMAL, + kCreatedInitialized); + scope->AddTemporary(var); + return var; +} + + +bool Scope::RemoveTemporary(Variable* var) { + // Most likely (always?) any temporary variable we want to remove + // was just added before, so we search backwards. + for (int i = temps_.length(); i-- > 0;) { + if (temps_[i] == var) { + temps_.Remove(i); + return true; + } + } + return false; +} + + +void Scope::AddDeclaration(Declaration* declaration) { + decls_.Add(declaration, zone()); +} + + +void Scope::SetIllegalRedeclaration(Expression* expression) { + // Record only the first illegal redeclaration. + if (!HasIllegalRedeclaration()) { + illegal_redecl_ = expression; + } + DCHECK(HasIllegalRedeclaration()); +} + + +Expression* Scope::GetIllegalRedeclaration() { + DCHECK(HasIllegalRedeclaration()); + return illegal_redecl_; +} + + +Declaration* Scope::CheckConflictingVarDeclarations() { + int length = decls_.length(); + for (int i = 0; i < length; i++) { + Declaration* decl = decls_[i]; + // We don't create a separate scope to hold the function name of a function + // expression, so we have to make sure not to consider it when checking for + // conflicts (since it's conceptually "outside" the declaration scope). + if (is_function_scope() && decl == function()) continue; + if (IsLexicalVariableMode(decl->mode()) && !is_block_scope()) continue; + const AstRawString* name = decl->proxy()->raw_name(); + + // Iterate through all scopes until and including the declaration scope. + Scope* previous = NULL; + Scope* current = decl->scope(); + // Lexical vs lexical conflicts within the same scope have already been + // captured in Parser::Declare. The only conflicts we still need to check + // are lexical vs VAR, or any declarations within a declaration block scope + // vs lexical declarations in its surrounding (function) scope. + if (IsLexicalVariableMode(decl->mode())) current = current->outer_scope_; + do { + // There is a conflict if there exists a non-VAR binding. + Variable* other_var = current->variables_.Lookup(name); + if (other_var != NULL && IsLexicalVariableMode(other_var->mode())) { + return decl; + } + previous = current; + current = current->outer_scope_; + } while (!previous->is_declaration_scope()); + } + return NULL; +} + + +class VarAndOrder { + public: + VarAndOrder(Variable* var, int order) : var_(var), order_(order) { } + Variable* var() const { return var_; } + int order() const { return order_; } + static int Compare(const VarAndOrder* a, const VarAndOrder* b) { + return a->order_ - b->order_; + } + + private: + Variable* var_; + int order_; +}; + + +void Scope::CollectStackAndContextLocals( + ZoneList<Variable*>* stack_locals, ZoneList<Variable*>* context_locals, + ZoneList<Variable*>* context_globals, + ZoneList<Variable*>* strong_mode_free_variables) { + DCHECK(stack_locals != NULL); + DCHECK(context_locals != NULL); + DCHECK(context_globals != NULL); + + // Collect temporaries which are always allocated on the stack, unless the + // context as a whole has forced context allocation. + for (int i = 0; i < temps_.length(); i++) { + Variable* var = temps_[i]; + if (var->is_used()) { + if (var->IsContextSlot()) { + DCHECK(has_forced_context_allocation()); + context_locals->Add(var, zone()); + } else if (var->IsStackLocal()) { + stack_locals->Add(var, zone()); + } else { + DCHECK(var->IsParameter()); + } + } + } + + // Collect declared local variables. + ZoneList<VarAndOrder> vars(variables_.occupancy(), zone()); + for (VariableMap::Entry* p = variables_.Start(); + p != NULL; + p = variables_.Next(p)) { + Variable* var = reinterpret_cast<Variable*>(p->value); + if (strong_mode_free_variables && var->has_strong_mode_reference() && + var->mode() == DYNAMIC_GLOBAL) { + strong_mode_free_variables->Add(var, zone()); + } + + if (var->is_used()) { + vars.Add(VarAndOrder(var, p->order), zone()); + } + } + vars.Sort(VarAndOrder::Compare); + int var_count = vars.length(); + for (int i = 0; i < var_count; i++) { + Variable* var = vars[i].var(); + if (var->IsStackLocal()) { + stack_locals->Add(var, zone()); + } else if (var->IsContextSlot()) { + context_locals->Add(var, zone()); + } else if (var->IsGlobalSlot()) { + context_globals->Add(var, zone()); + } + } +} + + +bool Scope::AllocateVariables(ParseInfo* info, AstNodeFactory* factory) { + // 1) Propagate scope information. + bool outer_scope_calls_sloppy_eval = false; + if (outer_scope_ != NULL) { + outer_scope_calls_sloppy_eval = + outer_scope_->outer_scope_calls_sloppy_eval() | + outer_scope_->calls_sloppy_eval(); + } + PropagateScopeInfo(outer_scope_calls_sloppy_eval); + + // 2) Resolve variables. + if (!ResolveVariablesRecursively(info, factory)) return false; + + // 3) Allocate variables. + AllocateVariablesRecursively(info->isolate()); + + return true; +} + + +bool Scope::HasTrivialContext() const { + // A function scope has a trivial context if it always is the global + // context. We iteratively scan out the context chain to see if + // there is anything that makes this scope non-trivial; otherwise we + // return true. + for (const Scope* scope = this; scope != NULL; scope = scope->outer_scope_) { + if (scope->is_eval_scope()) return false; + if (scope->scope_inside_with_) return false; + if (scope->ContextLocalCount() > 0) return false; + if (scope->ContextGlobalCount() > 0) return false; + } + return true; +} + + +bool Scope::HasTrivialOuterContext() const { + Scope* outer = outer_scope_; + if (outer == NULL) return true; + // Note that the outer context may be trivial in general, but the current + // scope may be inside a 'with' statement in which case the outer context + // for this scope is not trivial. + return !scope_inside_with_ && outer->HasTrivialContext(); +} + + +bool Scope::AllowsLazyParsing() const { + // If we are inside a block scope, we must parse eagerly to find out how + // to allocate variables on the block scope. At this point, declarations may + // not have yet been parsed. + for (const Scope* scope = this; scope != NULL; scope = scope->outer_scope_) { + if (scope->is_block_scope()) return false; + } + return AllowsLazyCompilation(); +} + + +bool Scope::AllowsLazyCompilation() const { return !force_eager_compilation_; } + + +bool Scope::AllowsLazyCompilationWithoutContext() const { + return !force_eager_compilation_ && HasTrivialOuterContext(); +} + + +int Scope::ContextChainLength(Scope* scope) { + int n = 0; + for (Scope* s = this; s != scope; s = s->outer_scope_) { + DCHECK(s != NULL); // scope must be in the scope chain + if (s->NeedsContext()) n++; + } + return n; +} + + +int Scope::MaxNestedContextChainLength() { + int max_context_chain_length = 0; + for (int i = 0; i < inner_scopes_.length(); i++) { + Scope* scope = inner_scopes_[i]; + max_context_chain_length = std::max(scope->MaxNestedContextChainLength(), + max_context_chain_length); + } + if (NeedsContext()) { + max_context_chain_length += 1; + } + return max_context_chain_length; +} + + +Scope* Scope::DeclarationScope() { + Scope* scope = this; + while (!scope->is_declaration_scope()) { + scope = scope->outer_scope(); + } + return scope; +} + + +Scope* Scope::ClosureScope() { + Scope* scope = this; + while (!scope->is_declaration_scope() || scope->is_block_scope()) { + scope = scope->outer_scope(); + } + return scope; +} + + +Scope* Scope::ReceiverScope() { + Scope* scope = this; + while (!scope->is_script_scope() && + (!scope->is_function_scope() || scope->is_arrow_scope())) { + scope = scope->outer_scope(); + } + return scope; +} + + + +Handle<ScopeInfo> Scope::GetScopeInfo(Isolate* isolate) { + if (scope_info_.is_null()) { + scope_info_ = ScopeInfo::Create(isolate, zone(), this); + } + return scope_info_; +} + + +void Scope::GetNestedScopeChain(Isolate* isolate, + List<Handle<ScopeInfo> >* chain, int position) { + if (!is_eval_scope()) chain->Add(Handle<ScopeInfo>(GetScopeInfo(isolate))); + + for (int i = 0; i < inner_scopes_.length(); i++) { + Scope* scope = inner_scopes_[i]; + int beg_pos = scope->start_position(); + int end_pos = scope->end_position(); + DCHECK(beg_pos >= 0 && end_pos >= 0); + if (beg_pos <= position && position < end_pos) { + scope->GetNestedScopeChain(isolate, chain, position); + return; + } + } +} + + +void Scope::CollectNonLocals(HashMap* non_locals) { + // Collect non-local variables referenced in the scope. + // TODO(yangguo): store non-local variables explicitly if we can no longer + // rely on unresolved_ to find them. + for (int i = 0; i < unresolved_.length(); i++) { + VariableProxy* proxy = unresolved_[i]; + if (proxy->is_resolved() && proxy->var()->IsStackAllocated()) continue; + Handle<String> name = proxy->name(); + void* key = reinterpret_cast<void*>(name.location()); + HashMap::Entry* entry = non_locals->LookupOrInsert(key, name->Hash()); + entry->value = key; + } + for (int i = 0; i < inner_scopes_.length(); i++) { + inner_scopes_[i]->CollectNonLocals(non_locals); + } +} + + +void Scope::ReportMessage(int start_position, int end_position, + MessageTemplate::Template message, + const AstRawString* arg) { + // Propagate the error to the topmost scope targeted by this scope analysis + // phase. + Scope* top = this; + while (!top->is_script_scope() && !top->outer_scope()->already_resolved()) { + top = top->outer_scope(); + } + + top->pending_error_handler_.ReportMessageAt(start_position, end_position, + message, arg, kReferenceError); +} + + +#ifdef DEBUG +static const char* Header(ScopeType scope_type, FunctionKind function_kind, + bool is_declaration_scope) { + switch (scope_type) { + case EVAL_SCOPE: return "eval"; + // TODO(adamk): Should we print concise method scopes specially? + case FUNCTION_SCOPE: + return IsArrowFunction(function_kind) ? "arrow" : "function"; + case MODULE_SCOPE: return "module"; + case SCRIPT_SCOPE: return "global"; + case CATCH_SCOPE: return "catch"; + case BLOCK_SCOPE: return is_declaration_scope ? "varblock" : "block"; + case WITH_SCOPE: return "with"; + } + UNREACHABLE(); + return NULL; +} + + +static void Indent(int n, const char* str) { + PrintF("%*s%s", n, "", str); +} + + +static void PrintName(const AstRawString* name) { + PrintF("%.*s", name->length(), name->raw_data()); +} + + +static void PrintLocation(Variable* var) { + switch (var->location()) { + case VariableLocation::UNALLOCATED: + break; + case VariableLocation::PARAMETER: + PrintF("parameter[%d]", var->index()); + break; + case VariableLocation::LOCAL: + PrintF("local[%d]", var->index()); + break; + case VariableLocation::CONTEXT: + PrintF("context[%d]", var->index()); + break; + case VariableLocation::GLOBAL: + PrintF("global[%d]", var->index()); + break; + case VariableLocation::LOOKUP: + PrintF("lookup"); + break; + } +} + + +static void PrintVar(int indent, Variable* var) { + if (var->is_used() || !var->IsUnallocated()) { + Indent(indent, Variable::Mode2String(var->mode())); + PrintF(" "); + if (var->raw_name()->IsEmpty()) + PrintF(".%p", reinterpret_cast<void*>(var)); + else + PrintName(var->raw_name()); + PrintF("; // "); + PrintLocation(var); + bool comma = !var->IsUnallocated(); + if (var->has_forced_context_allocation()) { + if (comma) PrintF(", "); + PrintF("forced context allocation"); + comma = true; + } + if (var->maybe_assigned() == kMaybeAssigned) { + if (comma) PrintF(", "); + PrintF("maybe assigned"); + } + PrintF("\n"); + } +} + + +static void PrintMap(int indent, VariableMap* map) { + for (VariableMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) { + Variable* var = reinterpret_cast<Variable*>(p->value); + if (var == NULL) { + Indent(indent, "<?>\n"); + } else { + PrintVar(indent, var); + } + } +} + + +void Scope::Print(int n) { + int n0 = (n > 0 ? n : 0); + int n1 = n0 + 2; // indentation + + // Print header. + Indent(n0, Header(scope_type_, function_kind_, is_declaration_scope())); + if (scope_name_ != nullptr && !scope_name_->IsEmpty()) { + PrintF(" "); + PrintName(scope_name_); + } + + // Print parameters, if any. + if (is_function_scope()) { + PrintF(" ("); + for (int i = 0; i < params_.length(); i++) { + if (i > 0) PrintF(", "); + const AstRawString* name = params_[i]->raw_name(); + if (name->IsEmpty()) + PrintF(".%p", reinterpret_cast<void*>(params_[i])); + else + PrintName(name); + } + PrintF(")"); + } + + PrintF(" { // (%d, %d)\n", start_position(), end_position()); + + // Function name, if any (named function literals, only). + if (function_ != NULL) { + Indent(n1, "// (local) function name: "); + PrintName(function_->proxy()->raw_name()); + PrintF("\n"); + } + + // Scope info. + if (HasTrivialOuterContext()) { + Indent(n1, "// scope has trivial outer context\n"); + } + if (is_strong(language_mode())) { + Indent(n1, "// strong mode scope\n"); + } else if (is_strict(language_mode())) { + Indent(n1, "// strict mode scope\n"); + } + if (scope_inside_with_) Indent(n1, "// scope inside 'with'\n"); + if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n"); + if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n"); + if (scope_uses_arguments_) Indent(n1, "// scope uses 'arguments'\n"); + if (scope_uses_super_property_) + Indent(n1, "// scope uses 'super' property\n"); + if (outer_scope_calls_sloppy_eval_) { + Indent(n1, "// outer scope calls 'eval' in sloppy context\n"); + } + if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n"); + if (num_stack_slots_ > 0) { + Indent(n1, "// "); + PrintF("%d stack slots\n", num_stack_slots_); + } + if (num_heap_slots_ > 0) { + Indent(n1, "// "); + PrintF("%d heap slots (including %d global slots)\n", num_heap_slots_, + num_global_slots_); + } + + // Print locals. + if (function_ != NULL) { + Indent(n1, "// function var:\n"); + PrintVar(n1, function_->proxy()->var()); + } + + if (temps_.length() > 0) { + Indent(n1, "// temporary vars:\n"); + for (int i = 0; i < temps_.length(); i++) { + PrintVar(n1, temps_[i]); + } + } + + if (variables_.Start() != NULL) { + Indent(n1, "// local vars:\n"); + PrintMap(n1, &variables_); + } + + if (dynamics_ != NULL) { + Indent(n1, "// dynamic vars:\n"); + PrintMap(n1, dynamics_->GetMap(DYNAMIC)); + PrintMap(n1, dynamics_->GetMap(DYNAMIC_LOCAL)); + PrintMap(n1, dynamics_->GetMap(DYNAMIC_GLOBAL)); + } + + // Print inner scopes (disable by providing negative n). + if (n >= 0) { + for (int i = 0; i < inner_scopes_.length(); i++) { + PrintF("\n"); + inner_scopes_[i]->Print(n1); + } + } + + Indent(n0, "}\n"); +} +#endif // DEBUG + + +Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) { + if (dynamics_ == NULL) dynamics_ = new (zone()) DynamicScopePart(zone()); + VariableMap* map = dynamics_->GetMap(mode); + Variable* var = map->Lookup(name); + if (var == NULL) { + // Declare a new non-local. + InitializationFlag init_flag = (mode == VAR) + ? kCreatedInitialized : kNeedsInitialization; + var = map->Declare(NULL, + name, + mode, + Variable::NORMAL, + init_flag); + // Allocate it by giving it a dynamic lookup. + var->AllocateTo(VariableLocation::LOOKUP, -1); + } + return var; +} + + +Variable* Scope::LookupRecursive(VariableProxy* proxy, + BindingKind* binding_kind, + AstNodeFactory* factory) { + DCHECK(binding_kind != NULL); + if (already_resolved() && is_with_scope()) { + // Short-cut: if the scope is deserialized from a scope info, variable + // allocation is already fixed. We can simply return with dynamic lookup. + *binding_kind = DYNAMIC_LOOKUP; + return NULL; + } + + // Try to find the variable in this scope. + Variable* var = LookupLocal(proxy->raw_name()); + + // We found a variable and we are done. (Even if there is an 'eval' in + // this scope which introduces the same variable again, the resulting + // variable remains the same.) + if (var != NULL) { + *binding_kind = BOUND; + return var; + } + + // We did not find a variable locally. Check against the function variable, + // if any. We can do this for all scopes, since the function variable is + // only present - if at all - for function scopes. + *binding_kind = UNBOUND; + var = LookupFunctionVar(proxy->raw_name(), factory); + if (var != NULL) { + *binding_kind = BOUND; + } else if (outer_scope_ != NULL) { + var = outer_scope_->LookupRecursive(proxy, binding_kind, factory); + if (*binding_kind == BOUND && (is_function_scope() || is_with_scope())) { + var->ForceContextAllocation(); + } + } else { + DCHECK(is_script_scope()); + } + + // "this" can't be shadowed by "eval"-introduced bindings or by "with" scopes. + // TODO(wingo): There are other variables in this category; add them. + bool name_can_be_shadowed = var == nullptr || !var->is_this(); + + if (is_with_scope() && name_can_be_shadowed) { + DCHECK(!already_resolved()); + // The current scope is a with scope, so the variable binding can not be + // statically resolved. However, note that it was necessary to do a lookup + // in the outer scope anyway, because if a binding exists in an outer scope, + // the associated variable has to be marked as potentially being accessed + // from inside of an inner with scope (the property may not be in the 'with' + // object). + if (var != NULL && proxy->is_assigned()) var->set_maybe_assigned(); + *binding_kind = DYNAMIC_LOOKUP; + return NULL; + } else if (calls_sloppy_eval() && !is_script_scope() && + name_can_be_shadowed) { + // A variable binding may have been found in an outer scope, but the current + // scope makes a sloppy 'eval' call, so the found variable may not be + // the correct one (the 'eval' may introduce a binding with the same name). + // In that case, change the lookup result to reflect this situation. + if (*binding_kind == BOUND) { + *binding_kind = BOUND_EVAL_SHADOWED; + } else if (*binding_kind == UNBOUND) { + *binding_kind = UNBOUND_EVAL_SHADOWED; + } + } + return var; +} + + +bool Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy, + AstNodeFactory* factory) { + DCHECK(info->script_scope()->is_script_scope()); + + // If the proxy is already resolved there's nothing to do + // (functions and consts may be resolved by the parser). + if (proxy->is_resolved()) return true; + + // Otherwise, try to resolve the variable. + BindingKind binding_kind; + Variable* var = LookupRecursive(proxy, &binding_kind, factory); + +#ifdef DEBUG + if (info->script_is_native()) { + // To avoid polluting the global object in native scripts + // - Variables must not be allocated to the global scope. + CHECK_NOT_NULL(outer_scope()); + // - Variables must be bound locally or unallocated. + if (BOUND != binding_kind) { + // The following variable name may be minified. If so, disable + // minification in js2c.py for better output. + Handle<String> name = proxy->raw_name()->string(); + V8_Fatal(__FILE__, __LINE__, "Unbound variable: '%s' in native script.", + name->ToCString().get()); + } + VariableLocation location = var->location(); + CHECK(location == VariableLocation::LOCAL || + location == VariableLocation::CONTEXT || + location == VariableLocation::PARAMETER || + location == VariableLocation::UNALLOCATED); + } +#endif + + switch (binding_kind) { + case BOUND: + // We found a variable binding. + if (is_strong(language_mode())) { + if (!CheckStrongModeDeclaration(proxy, var)) return false; + } + break; + + case BOUND_EVAL_SHADOWED: + // We either found a variable binding that might be shadowed by eval or + // gave up on it (e.g. by encountering a local with the same in the outer + // scope which was not promoted to a context, this can happen if we use + // debugger to evaluate arbitrary expressions at a break point). + if (var->IsGlobalObjectProperty()) { + var = NonLocal(proxy->raw_name(), DYNAMIC_GLOBAL); + } else if (var->is_dynamic()) { + var = NonLocal(proxy->raw_name(), DYNAMIC); + } else { + Variable* invalidated = var; + var = NonLocal(proxy->raw_name(), DYNAMIC_LOCAL); + var->set_local_if_not_shadowed(invalidated); + } + break; + + case UNBOUND: + // No binding has been found. Declare a variable on the global object. + var = info->script_scope()->DeclareDynamicGlobal(proxy->raw_name()); + break; + + case UNBOUND_EVAL_SHADOWED: + // No binding has been found. But some scope makes a sloppy 'eval' call. + var = NonLocal(proxy->raw_name(), DYNAMIC_GLOBAL); + break; + + case DYNAMIC_LOOKUP: + // The variable could not be resolved statically. + var = NonLocal(proxy->raw_name(), DYNAMIC); + break; + } + + DCHECK(var != NULL); + if (proxy->is_assigned()) var->set_maybe_assigned(); + + if (is_strong(language_mode())) { + // Record that the variable is referred to from strong mode. Also, record + // the position. + var->RecordStrongModeReference(proxy->position(), proxy->end_position()); + } + + proxy->BindTo(var); + + return true; +} + + +bool Scope::CheckStrongModeDeclaration(VariableProxy* proxy, Variable* var) { + // Check for declaration-after use (for variables) in strong mode. Note that + // we can only do this in the case where we have seen the declaration. And we + // always allow referencing functions (for now). + + // This might happen during lazy compilation; we don't keep track of + // initializer positions for variables stored in ScopeInfo, so we cannot check + // bindings against them. TODO(marja, rossberg): remove this hack. + if (var->initializer_position() == RelocInfo::kNoPosition) return true; + + // Allow referencing the class name from methods of that class, even though + // the initializer position for class names is only after the body. + Scope* scope = this; + while (scope) { + if (scope->ClassVariableForMethod() == var) return true; + scope = scope->outer_scope(); + } + + // Allow references from methods to classes declared later, if we detect no + // problematic dependency cycles. Note that we can be inside multiple methods + // at the same time, and it's enough if we find one where the reference is + // allowed. + if (var->is_class() && + var->AsClassVariable()->declaration_group_start() >= 0) { + for (scope = this; scope && scope != var->scope(); + scope = scope->outer_scope()) { + ClassVariable* class_var = scope->ClassVariableForMethod(); + // A method is referring to some other class, possibly declared + // later. Referring to a class declared earlier is always OK and covered + // by the code outside this if. Here we only need to allow special cases + // for referring to a class which is declared later. + + // Referring to a class C declared later is OK under the following + // circumstances: + + // 1. The class declarations are in a consecutive group with no other + // declarations or statements in between, and + + // 2. There is no dependency cycle where the first edge is an + // initialization time dependency (computed property name or extends + // clause) from C to something that depends on this class directly or + // transitively. + if (class_var && + class_var->declaration_group_start() == + var->AsClassVariable()->declaration_group_start()) { + return true; + } + + // TODO(marja,rossberg): implement the dependency cycle detection. Here we + // undershoot the target and allow referring to any class in the same + // consectuive declaration group. + + // The cycle detection can work roughly like this: 1) detect init-time + // references here (they are free variables which are inside the class + // scope but not inside a method scope - no parser changes needed to + // detect them) 2) if we encounter an init-time reference here, allow it, + // but record it for a later dependency cycle check 3) also record + // non-init-time references here 4) after scope analysis is done, analyse + // the dependency cycles: an illegal cycle is one starting with an + // init-time reference and leading back to the starting point with either + // non-init-time and init-time references. + } + } + + // If both the use and the declaration are inside an eval scope (possibly + // indirectly), or one of them is, we need to check whether they are inside + // the same eval scope or different ones. + + // TODO(marja,rossberg): Detect errors across different evals (depends on the + // future of eval in strong mode). + const Scope* eval_for_use = NearestOuterEvalScope(); + const Scope* eval_for_declaration = var->scope()->NearestOuterEvalScope(); + + if (proxy->position() != RelocInfo::kNoPosition && + proxy->position() < var->initializer_position() && !var->is_function() && + eval_for_use == eval_for_declaration) { + DCHECK(proxy->end_position() != RelocInfo::kNoPosition); + ReportMessage(proxy->position(), proxy->end_position(), + MessageTemplate::kStrongUseBeforeDeclaration, + proxy->raw_name()); + return false; + } + return true; +} + + +ClassVariable* Scope::ClassVariableForMethod() const { + // TODO(marja, rossberg): This fails to find a class variable in the following + // cases: + // let A = class { ... } + // It needs to be investigated whether this causes any practical problems. + if (!is_function_scope()) return nullptr; + if (IsInObjectLiteral(function_kind_)) return nullptr; + if (!IsConciseMethod(function_kind_) && !IsClassConstructor(function_kind_) && + !IsAccessorFunction(function_kind_)) { + return nullptr; + } + DCHECK_NOT_NULL(outer_scope_); + // The class scope contains at most one variable, the class name. + DCHECK(outer_scope_->variables_.occupancy() <= 1); + if (outer_scope_->variables_.occupancy() == 0) return nullptr; + VariableMap::Entry* p = outer_scope_->variables_.Start(); + Variable* var = reinterpret_cast<Variable*>(p->value); + if (!var->is_class()) return nullptr; + return var->AsClassVariable(); +} + + +bool Scope::ResolveVariablesRecursively(ParseInfo* info, + AstNodeFactory* factory) { + DCHECK(info->script_scope()->is_script_scope()); + + // Resolve unresolved variables for this scope. + for (int i = 0; i < unresolved_.length(); i++) { + if (!ResolveVariable(info, unresolved_[i], factory)) return false; + } + + // Resolve unresolved variables for inner scopes. + for (int i = 0; i < inner_scopes_.length(); i++) { + if (!inner_scopes_[i]->ResolveVariablesRecursively(info, factory)) + return false; + } + + return true; +} + + +void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) { + if (outer_scope_calls_sloppy_eval) { + outer_scope_calls_sloppy_eval_ = true; + } + + bool calls_sloppy_eval = + this->calls_sloppy_eval() || outer_scope_calls_sloppy_eval_; + for (int i = 0; i < inner_scopes_.length(); i++) { + Scope* inner = inner_scopes_[i]; + inner->PropagateScopeInfo(calls_sloppy_eval); + if (inner->scope_calls_eval_ || inner->inner_scope_calls_eval_) { + inner_scope_calls_eval_ = true; + } + if (inner->force_eager_compilation_) { + force_eager_compilation_ = true; + } + if (asm_module_ && inner->scope_type() == FUNCTION_SCOPE) { + inner->asm_function_ = true; + } + } +} + + +bool Scope::MustAllocate(Variable* var) { + // Give var a read/write use if there is a chance it might be accessed + // via an eval() call. This is only possible if the variable has a + // visible name. + if ((var->is_this() || !var->raw_name()->IsEmpty()) && + (var->has_forced_context_allocation() || scope_calls_eval_ || + inner_scope_calls_eval_ || scope_contains_with_ || is_catch_scope() || + is_block_scope() || is_module_scope() || is_script_scope())) { + var->set_is_used(); + if (scope_calls_eval_ || inner_scope_calls_eval_) var->set_maybe_assigned(); + } + // Global variables do not need to be allocated. + return !var->IsGlobalObjectProperty() && var->is_used(); +} + + +bool Scope::MustAllocateInContext(Variable* var) { + // If var is accessed from an inner scope, or if there is a possibility + // that it might be accessed from the current or an inner scope (through + // an eval() call or a runtime with lookup), it must be allocated in the + // context. + // + // Exceptions: If the scope as a whole has forced context allocation, all + // variables will have context allocation, even temporaries. Otherwise + // temporary variables are always stack-allocated. Catch-bound variables are + // always context-allocated. + if (has_forced_context_allocation()) return true; + if (var->mode() == TEMPORARY) return false; + if (is_catch_scope() || is_module_scope()) return true; + if (is_script_scope() && IsLexicalVariableMode(var->mode())) return true; + return var->has_forced_context_allocation() || + scope_calls_eval_ || + inner_scope_calls_eval_ || + scope_contains_with_; +} + + +bool Scope::HasArgumentsParameter(Isolate* isolate) { + for (int i = 0; i < params_.length(); i++) { + if (params_[i]->name().is_identical_to( + isolate->factory()->arguments_string())) { + return true; + } + } + return false; +} + + +void Scope::AllocateStackSlot(Variable* var) { + if (is_block_scope()) { + outer_scope()->DeclarationScope()->AllocateStackSlot(var); + } else { + var->AllocateTo(VariableLocation::LOCAL, num_stack_slots_++); + } +} + + +void Scope::AllocateHeapSlot(Variable* var) { + var->AllocateTo(VariableLocation::CONTEXT, num_heap_slots_++); +} + + +void Scope::AllocateParameterLocals(Isolate* isolate) { + DCHECK(is_function_scope()); + Variable* arguments = LookupLocal(ast_value_factory_->arguments_string()); + // Functions have 'arguments' declared implicitly in all non arrow functions. + DCHECK(arguments != nullptr || is_arrow_scope()); + + bool uses_sloppy_arguments = false; + + if (arguments != nullptr && MustAllocate(arguments) && + !HasArgumentsParameter(isolate)) { + // 'arguments' is used. Unless there is also a parameter called + // 'arguments', we must be conservative and allocate all parameters to + // the context assuming they will be captured by the arguments object. + // If we have a parameter named 'arguments', a (new) value is always + // assigned to it via the function invocation. Then 'arguments' denotes + // that specific parameter value and cannot be used to access the + // parameters, which is why we don't need to allocate an arguments + // object in that case. + + // We are using 'arguments'. Tell the code generator that is needs to + // allocate the arguments object by setting 'arguments_'. + arguments_ = arguments; + + // In strict mode 'arguments' does not alias formal parameters. + // Therefore in strict mode we allocate parameters as if 'arguments' + // were not used. + // If the parameter list is not simple, arguments isn't sloppy either. + uses_sloppy_arguments = + is_sloppy(language_mode()) && has_simple_parameters(); + } + + if (rest_parameter_ && !MustAllocate(rest_parameter_)) { + rest_parameter_ = NULL; + } + + // The same parameter may occur multiple times in the parameters_ list. + // If it does, and if it is not copied into the context object, it must + // receive the highest parameter index for that parameter; thus iteration + // order is relevant! + for (int i = params_.length() - 1; i >= 0; --i) { + Variable* var = params_[i]; + if (var == rest_parameter_) continue; + + DCHECK(var->scope() == this); + if (uses_sloppy_arguments || has_forced_context_allocation()) { + // Force context allocation of the parameter. + var->ForceContextAllocation(); + } + AllocateParameter(var, i); + } +} + + +void Scope::AllocateParameter(Variable* var, int index) { + if (MustAllocate(var)) { + if (MustAllocateInContext(var)) { + DCHECK(var->IsUnallocated() || var->IsContextSlot()); + if (var->IsUnallocated()) { + AllocateHeapSlot(var); + } + } else { + DCHECK(var->IsUnallocated() || var->IsParameter()); + if (var->IsUnallocated()) { + var->AllocateTo(VariableLocation::PARAMETER, index); + } + } + } else { + DCHECK(!var->IsGlobalSlot()); + } +} + + +void Scope::AllocateReceiver() { + DCHECK_NOT_NULL(receiver()); + DCHECK_EQ(receiver()->scope(), this); + + if (has_forced_context_allocation()) { + // Force context allocation of the receiver. + receiver()->ForceContextAllocation(); + } + AllocateParameter(receiver(), -1); +} + + +void Scope::AllocateNonParameterLocal(Isolate* isolate, Variable* var) { + DCHECK(var->scope() == this); + DCHECK(!var->IsVariable(isolate->factory()->dot_result_string()) || + !var->IsStackLocal()); + if (var->IsUnallocated() && MustAllocate(var)) { + if (MustAllocateInContext(var)) { + AllocateHeapSlot(var); + } else { + AllocateStackSlot(var); + } + } +} + + +void Scope::AllocateDeclaredGlobal(Isolate* isolate, Variable* var) { + DCHECK(var->scope() == this); + DCHECK(!var->IsVariable(isolate->factory()->dot_result_string()) || + !var->IsStackLocal()); + if (var->IsUnallocated()) { + if (var->IsStaticGlobalObjectProperty()) { + DCHECK_EQ(-1, var->index()); + DCHECK(var->name()->IsString()); + var->AllocateTo(VariableLocation::GLOBAL, num_heap_slots_++); + num_global_slots_++; + } else { + // There must be only DYNAMIC_GLOBAL in the script scope. + DCHECK(!is_script_scope() || DYNAMIC_GLOBAL == var->mode()); + } + } +} + + +void Scope::AllocateNonParameterLocalsAndDeclaredGlobals(Isolate* isolate) { + // All variables that have no rewrite yet are non-parameter locals. + for (int i = 0; i < temps_.length(); i++) { + AllocateNonParameterLocal(isolate, temps_[i]); + } + + ZoneList<VarAndOrder> vars(variables_.occupancy(), zone()); + for (VariableMap::Entry* p = variables_.Start(); + p != NULL; + p = variables_.Next(p)) { + Variable* var = reinterpret_cast<Variable*>(p->value); + vars.Add(VarAndOrder(var, p->order), zone()); + } + vars.Sort(VarAndOrder::Compare); + int var_count = vars.length(); + for (int i = 0; i < var_count; i++) { + AllocateNonParameterLocal(isolate, vars[i].var()); + } + + if (FLAG_global_var_shortcuts) { + for (int i = 0; i < var_count; i++) { + AllocateDeclaredGlobal(isolate, vars[i].var()); + } + } + + // For now, function_ must be allocated at the very end. If it gets + // allocated in the context, it must be the last slot in the context, + // because of the current ScopeInfo implementation (see + // ScopeInfo::ScopeInfo(FunctionScope* scope) constructor). + if (function_ != nullptr) { + AllocateNonParameterLocal(isolate, function_->proxy()->var()); + } + + if (rest_parameter_ != nullptr) { + AllocateNonParameterLocal(isolate, rest_parameter_); + } + + Variable* new_target_var = + LookupLocal(ast_value_factory_->new_target_string()); + if (new_target_var != nullptr && MustAllocate(new_target_var)) { + new_target_ = new_target_var; + } + + Variable* this_function_var = + LookupLocal(ast_value_factory_->this_function_string()); + if (this_function_var != nullptr && MustAllocate(this_function_var)) { + this_function_ = this_function_var; + } +} + + +void Scope::AllocateVariablesRecursively(Isolate* isolate) { + if (!already_resolved()) { + num_stack_slots_ = 0; + } + // Allocate variables for inner scopes. + for (int i = 0; i < inner_scopes_.length(); i++) { + inner_scopes_[i]->AllocateVariablesRecursively(isolate); + } + + // If scope is already resolved, we still need to allocate + // variables in inner scopes which might not had been resolved yet. + if (already_resolved()) return; + // The number of slots required for variables. + num_heap_slots_ = Context::MIN_CONTEXT_SLOTS; + + // Allocate variables for this scope. + // Parameters must be allocated first, if any. + if (is_function_scope()) AllocateParameterLocals(isolate); + if (has_this_declaration()) AllocateReceiver(); + AllocateNonParameterLocalsAndDeclaredGlobals(isolate); + + // Force allocation of a context for this scope if necessary. For a 'with' + // scope and for a function scope that makes an 'eval' call we need a context, + // even if no local variables were statically allocated in the scope. + // Likewise for modules. + bool must_have_context = + is_with_scope() || is_module_scope() || + (is_function_scope() && calls_sloppy_eval()) || + (is_block_scope() && is_declaration_scope() && calls_sloppy_eval()); + + // If we didn't allocate any locals in the local context, then we only + // need the minimal number of slots if we must have a context. + if (num_heap_slots_ == Context::MIN_CONTEXT_SLOTS && !must_have_context) { + num_heap_slots_ = 0; + } + + // Allocation done. + DCHECK(num_heap_slots_ == 0 || num_heap_slots_ >= Context::MIN_CONTEXT_SLOTS); +} + + +int Scope::StackLocalCount() const { + return num_stack_slots() - + (function_ != NULL && function_->proxy()->var()->IsStackLocal() ? 1 : 0); +} + + +int Scope::ContextLocalCount() const { + if (num_heap_slots() == 0) return 0; + bool is_function_var_in_context = + function_ != NULL && function_->proxy()->var()->IsContextSlot(); + return num_heap_slots() - Context::MIN_CONTEXT_SLOTS - num_global_slots() - + (is_function_var_in_context ? 1 : 0); +} + + +int Scope::ContextGlobalCount() const { return num_global_slots(); } + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/scopes.h b/deps/v8/src/ast/scopes.h new file mode 100644 index 0000000000..6c261f63c3 --- /dev/null +++ b/deps/v8/src/ast/scopes.h @@ -0,0 +1,849 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_SCOPES_H_ +#define V8_AST_SCOPES_H_ + +#include "src/ast/ast.h" +#include "src/hashmap.h" +#include "src/pending-compilation-error-handler.h" +#include "src/zone.h" + +namespace v8 { +namespace internal { + +class ParseInfo; + +// A hash map to support fast variable declaration and lookup. +class VariableMap: public ZoneHashMap { + public: + explicit VariableMap(Zone* zone); + + virtual ~VariableMap(); + + Variable* Declare(Scope* scope, const AstRawString* name, VariableMode mode, + Variable::Kind kind, InitializationFlag initialization_flag, + MaybeAssignedFlag maybe_assigned_flag = kNotAssigned, + int declaration_group_start = -1); + + Variable* Lookup(const AstRawString* name); + + Zone* zone() const { return zone_; } + + private: + Zone* zone_; +}; + + +// The dynamic scope part holds hash maps for the variables that will +// be looked up dynamically from within eval and with scopes. The objects +// are allocated on-demand from Scope::NonLocal to avoid wasting memory +// and setup time for scopes that don't need them. +class DynamicScopePart : public ZoneObject { + public: + explicit DynamicScopePart(Zone* zone) { + for (int i = 0; i < 3; i++) + maps_[i] = new(zone->New(sizeof(VariableMap))) VariableMap(zone); + } + + VariableMap* GetMap(VariableMode mode) { + int index = mode - DYNAMIC; + DCHECK(index >= 0 && index < 3); + return maps_[index]; + } + + private: + VariableMap *maps_[3]; +}; + + +// Sloppy block-scoped function declarations to var-bind +class SloppyBlockFunctionMap : public ZoneHashMap { + public: + explicit SloppyBlockFunctionMap(Zone* zone); + + virtual ~SloppyBlockFunctionMap(); + + void Declare(const AstRawString* name, + SloppyBlockFunctionStatement* statement); + + typedef ZoneVector<SloppyBlockFunctionStatement*> Vector; + + private: + Zone* zone_; +}; + + +// Global invariants after AST construction: Each reference (i.e. identifier) +// to a JavaScript variable (including global properties) is represented by a +// VariableProxy node. Immediately after AST construction and before variable +// allocation, most VariableProxy nodes are "unresolved", i.e. not bound to a +// corresponding variable (though some are bound during parse time). Variable +// allocation binds each unresolved VariableProxy to one Variable and assigns +// a location. Note that many VariableProxy nodes may refer to the same Java- +// Script variable. + +class Scope: public ZoneObject { + public: + // --------------------------------------------------------------------------- + // Construction + + Scope(Zone* zone, Scope* outer_scope, ScopeType scope_type, + AstValueFactory* value_factory, + FunctionKind function_kind = kNormalFunction); + + // Compute top scope and allocate variables. For lazy compilation the top + // scope only contains the single lazily compiled function, so this + // doesn't re-allocate variables repeatedly. + static bool Analyze(ParseInfo* info); + + static Scope* DeserializeScopeChain(Isolate* isolate, Zone* zone, + Context* context, Scope* script_scope); + + // The scope name is only used for printing/debugging. + void SetScopeName(const AstRawString* scope_name) { + scope_name_ = scope_name; + } + + void Initialize(); + + // Checks if the block scope is redundant, i.e. it does not contain any + // block scoped declarations. In that case it is removed from the scope + // tree and its children are reparented. + Scope* FinalizeBlockScope(); + + // Inserts outer_scope into this scope's scope chain (and removes this + // from the current outer_scope_'s inner_scopes_). + // Assumes outer_scope_ is non-null. + void ReplaceOuterScope(Scope* outer_scope); + + // Propagates any eagerly-gathered scope usage flags (such as calls_eval()) + // to the passed-in scope. + void PropagateUsageFlagsToScope(Scope* other); + + Zone* zone() const { return zone_; } + + // --------------------------------------------------------------------------- + // Declarations + + // Lookup a variable in this scope. Returns the variable or NULL if not found. + Variable* LookupLocal(const AstRawString* name); + + // This lookup corresponds to a lookup in the "intermediate" scope sitting + // between this scope and the outer scope. (ECMA-262, 3rd., requires that + // the name of named function literal is kept in an intermediate scope + // in between this scope and the next outer scope.) + Variable* LookupFunctionVar(const AstRawString* name, + AstNodeFactory* factory); + + // Lookup a variable in this scope or outer scopes. + // Returns the variable or NULL if not found. + Variable* Lookup(const AstRawString* name); + + // Declare the function variable for a function literal. This variable + // is in an intermediate scope between this function scope and the the + // outer scope. Only possible for function scopes; at most one variable. + void DeclareFunctionVar(VariableDeclaration* declaration) { + DCHECK(is_function_scope()); + // Handle implicit declaration of the function name in named function + // expressions before other declarations. + decls_.InsertAt(0, declaration, zone()); + function_ = declaration; + } + + // Declare a parameter in this scope. When there are duplicated + // parameters the rightmost one 'wins'. However, the implementation + // expects all parameters to be declared and from left to right. + Variable* DeclareParameter( + const AstRawString* name, VariableMode mode, + bool is_optional, bool is_rest, bool* is_duplicate); + + // Declare a local variable in this scope. If the variable has been + // declared before, the previously declared variable is returned. + Variable* DeclareLocal(const AstRawString* name, VariableMode mode, + InitializationFlag init_flag, Variable::Kind kind, + MaybeAssignedFlag maybe_assigned_flag = kNotAssigned, + int declaration_group_start = -1); + + // Declare an implicit global variable in this scope which must be a + // script scope. The variable was introduced (possibly from an inner + // scope) by a reference to an unresolved variable with no intervening + // with statements or eval calls. + Variable* DeclareDynamicGlobal(const AstRawString* name); + + // Create a new unresolved variable. + VariableProxy* NewUnresolved(AstNodeFactory* factory, + const AstRawString* name, + Variable::Kind kind = Variable::NORMAL, + int start_position = RelocInfo::kNoPosition, + int end_position = RelocInfo::kNoPosition) { + // Note that we must not share the unresolved variables with + // the same name because they may be removed selectively via + // RemoveUnresolved(). + DCHECK(!already_resolved()); + VariableProxy* proxy = + factory->NewVariableProxy(name, kind, start_position, end_position); + unresolved_.Add(proxy, zone_); + return proxy; + } + + void AddUnresolved(VariableProxy* proxy) { + DCHECK(!already_resolved()); + DCHECK(!proxy->is_resolved()); + unresolved_.Add(proxy, zone_); + } + + // Remove a unresolved variable. During parsing, an unresolved variable + // may have been added optimistically, but then only the variable name + // was used (typically for labels). If the variable was not declared, the + // addition introduced a new unresolved variable which may end up being + // allocated globally as a "ghost" variable. RemoveUnresolved removes + // such a variable again if it was added; otherwise this is a no-op. + bool RemoveUnresolved(VariableProxy* var); + + // Creates a new temporary variable in this scope's TemporaryScope. The + // name is only used for printing and cannot be used to find the variable. + // In particular, the only way to get hold of the temporary is by keeping the + // Variable* around. The name should not clash with a legitimate variable + // names. + Variable* NewTemporary(const AstRawString* name); + + // Remove a temporary variable. This is for adjusting the scope of + // temporaries used when desugaring parameter initializers. + bool RemoveTemporary(Variable* var); + + // Adds a temporary variable in this scope's TemporaryScope. This is for + // adjusting the scope of temporaries used when desugaring parameter + // initializers. + void AddTemporary(Variable* var) { temps_.Add(var, zone()); } + + // Adds the specific declaration node to the list of declarations in + // this scope. The declarations are processed as part of entering + // the scope; see codegen.cc:ProcessDeclarations. + void AddDeclaration(Declaration* declaration); + + // --------------------------------------------------------------------------- + // Illegal redeclaration support. + + // Set an expression node that will be executed when the scope is + // entered. We only keep track of one illegal redeclaration node per + // scope - the first one - so if you try to set it multiple times + // the additional requests will be silently ignored. + void SetIllegalRedeclaration(Expression* expression); + + // Retrieve the illegal redeclaration expression. Do not call if the + // scope doesn't have an illegal redeclaration node. + Expression* GetIllegalRedeclaration(); + + // Check if the scope has (at least) one illegal redeclaration. + bool HasIllegalRedeclaration() const { return illegal_redecl_ != NULL; } + + // For harmony block scoping mode: Check if the scope has conflicting var + // declarations, i.e. a var declaration that has been hoisted from a nested + // scope over a let binding of the same name. + Declaration* CheckConflictingVarDeclarations(); + + // --------------------------------------------------------------------------- + // Scope-specific info. + + // Inform the scope that the corresponding code contains a with statement. + void RecordWithStatement() { scope_contains_with_ = true; } + + // Inform the scope that the corresponding code contains an eval call. + void RecordEvalCall() { scope_calls_eval_ = true; } + + // Inform the scope that the corresponding code uses "arguments". + void RecordArgumentsUsage() { scope_uses_arguments_ = true; } + + // Inform the scope that the corresponding code uses "super". + void RecordSuperPropertyUsage() { scope_uses_super_property_ = true; } + + // Set the language mode flag (unless disabled by a global flag). + void SetLanguageMode(LanguageMode language_mode) { + language_mode_ = language_mode; + } + + // Set the ASM module flag. + void SetAsmModule() { asm_module_ = true; } + + // Inform the scope that the scope may execute declarations nonlinearly. + // Currently, the only nonlinear scope is a switch statement. The name is + // more general in case something else comes up with similar control flow, + // for example the ability to break out of something which does not have + // its own lexical scope. + // The bit does not need to be stored on the ScopeInfo because none of + // the three compilers will perform hole check elimination on a variable + // located in VariableLocation::CONTEXT. So, direct eval and closures + // will not expose holes. + void SetNonlinear() { scope_nonlinear_ = true; } + + // Position in the source where this scope begins and ends. + // + // * For the scope of a with statement + // with (obj) stmt + // start position: start position of first token of 'stmt' + // end position: end position of last token of 'stmt' + // * For the scope of a block + // { stmts } + // start position: start position of '{' + // end position: end position of '}' + // * For the scope of a function literal or decalaration + // function fun(a,b) { stmts } + // start position: start position of '(' + // end position: end position of '}' + // * For the scope of a catch block + // try { stms } catch(e) { stmts } + // start position: start position of '(' + // end position: end position of ')' + // * For the scope of a for-statement + // for (let x ...) stmt + // start position: start position of '(' + // end position: end position of last token of 'stmt' + // * For the scope of a switch statement + // switch (tag) { cases } + // start position: start position of '{' + // end position: end position of '}' + int start_position() const { return start_position_; } + void set_start_position(int statement_pos) { + start_position_ = statement_pos; + } + int end_position() const { return end_position_; } + void set_end_position(int statement_pos) { + end_position_ = statement_pos; + } + + // In some cases we want to force context allocation for a whole scope. + void ForceContextAllocation() { + DCHECK(!already_resolved()); + force_context_allocation_ = true; + } + bool has_forced_context_allocation() const { + return force_context_allocation_; + } + + // --------------------------------------------------------------------------- + // Predicates. + + // Specific scope types. + bool is_eval_scope() const { return scope_type_ == EVAL_SCOPE; } + bool is_function_scope() const { return scope_type_ == FUNCTION_SCOPE; } + bool is_module_scope() const { return scope_type_ == MODULE_SCOPE; } + bool is_script_scope() const { return scope_type_ == SCRIPT_SCOPE; } + bool is_catch_scope() const { return scope_type_ == CATCH_SCOPE; } + bool is_block_scope() const { return scope_type_ == BLOCK_SCOPE; } + bool is_with_scope() const { return scope_type_ == WITH_SCOPE; } + bool is_arrow_scope() const { + return is_function_scope() && IsArrowFunction(function_kind_); + } + bool is_declaration_scope() const { return is_declaration_scope_; } + + void set_is_declaration_scope() { is_declaration_scope_ = true; } + + // Information about which scopes calls eval. + bool calls_eval() const { return scope_calls_eval_; } + bool calls_sloppy_eval() const { + return scope_calls_eval_ && is_sloppy(language_mode_); + } + bool outer_scope_calls_sloppy_eval() const { + return outer_scope_calls_sloppy_eval_; + } + bool asm_module() const { return asm_module_; } + bool asm_function() const { return asm_function_; } + + // Is this scope inside a with statement. + bool inside_with() const { return scope_inside_with_; } + + // Does this scope access "arguments". + bool uses_arguments() const { return scope_uses_arguments_; } + // Does this scope access "super" property (super.foo). + bool uses_super_property() const { return scope_uses_super_property_; } + // Does this scope have the potential to execute declarations non-linearly? + bool is_nonlinear() const { return scope_nonlinear_; } + + // Whether this needs to be represented by a runtime context. + bool NeedsContext() const { + // Catch and module scopes always have heap slots. + DCHECK(!is_catch_scope() || num_heap_slots() > 0); + DCHECK(!is_module_scope() || num_heap_slots() > 0); + return is_with_scope() || num_heap_slots() > 0; + } + + bool NeedsHomeObject() const { + return scope_uses_super_property_ || + ((scope_calls_eval_ || inner_scope_calls_eval_) && + (IsConciseMethod(function_kind()) || + IsAccessorFunction(function_kind()) || + IsClassConstructor(function_kind()))); + } + + const Scope* NearestOuterEvalScope() const { + if (is_eval_scope()) return this; + if (outer_scope() == nullptr) return nullptr; + return outer_scope()->NearestOuterEvalScope(); + } + + // --------------------------------------------------------------------------- + // Accessors. + + // The type of this scope. + ScopeType scope_type() const { return scope_type_; } + + FunctionKind function_kind() const { return function_kind_; } + + // The language mode of this scope. + LanguageMode language_mode() const { return language_mode_; } + + // The variable corresponding to the 'this' value. + Variable* receiver() { + DCHECK(has_this_declaration()); + DCHECK_NOT_NULL(receiver_); + return receiver_; + } + + // TODO(wingo): Add a GLOBAL_SCOPE scope type which will lexically allocate + // "this" (and no other variable) on the native context. Script scopes then + // will not have a "this" declaration. + bool has_this_declaration() const { + return (is_function_scope() && !is_arrow_scope()) || is_module_scope(); + } + + // The variable corresponding to the 'new.target' value. + Variable* new_target_var() { return new_target_; } + + // The variable holding the function literal for named function + // literals, or NULL. Only valid for function scopes. + VariableDeclaration* function() const { + DCHECK(is_function_scope()); + return function_; + } + + // Parameters. The left-most parameter has index 0. + // Only valid for function scopes. + Variable* parameter(int index) const { + DCHECK(is_function_scope()); + return params_[index]; + } + + // Returns the default function arity excluding default or rest parameters. + int default_function_length() const { return arity_; } + + int num_parameters() const { return params_.length(); } + + // A function can have at most one rest parameter. Returns Variable* or NULL. + Variable* rest_parameter(int* index) const { + *index = rest_index_; + if (rest_index_ < 0) return NULL; + return rest_parameter_; + } + + bool has_rest_parameter() const { return rest_index_ >= 0; } + + bool has_simple_parameters() const { + return has_simple_parameters_; + } + + // TODO(caitp): manage this state in a better way. PreParser must be able to + // communicate that the scope is non-simple, without allocating any parameters + // as the Parser does. This is necessary to ensure that TC39's proposed early + // error can be reported consistently regardless of whether lazily parsed or + // not. + void SetHasNonSimpleParameters() { + DCHECK(is_function_scope()); + has_simple_parameters_ = false; + } + + // Retrieve `IsSimpleParameterList` of current or outer function. + bool HasSimpleParameters() { + Scope* scope = ClosureScope(); + return !scope->is_function_scope() || scope->has_simple_parameters(); + } + + // The local variable 'arguments' if we need to allocate it; NULL otherwise. + Variable* arguments() const { + DCHECK(!is_arrow_scope() || arguments_ == nullptr); + return arguments_; + } + + Variable* this_function_var() const { + // This is only used in derived constructors atm. + DCHECK(this_function_ == nullptr || + (is_function_scope() && (IsClassConstructor(function_kind()) || + IsConciseMethod(function_kind()) || + IsAccessorFunction(function_kind())))); + return this_function_; + } + + // Declarations list. + ZoneList<Declaration*>* declarations() { return &decls_; } + + // Inner scope list. + ZoneList<Scope*>* inner_scopes() { return &inner_scopes_; } + + // The scope immediately surrounding this scope, or NULL. + Scope* outer_scope() const { return outer_scope_; } + + // The ModuleDescriptor for this scope; only for module scopes. + ModuleDescriptor* module() const { return module_descriptor_; } + + + void set_class_declaration_group_start(int position) { + class_declaration_group_start_ = position; + } + + int class_declaration_group_start() const { + return class_declaration_group_start_; + } + + // --------------------------------------------------------------------------- + // Variable allocation. + + // Collect stack and context allocated local variables in this scope. Note + // that the function variable - if present - is not collected and should be + // handled separately. + void CollectStackAndContextLocals( + ZoneList<Variable*>* stack_locals, ZoneList<Variable*>* context_locals, + ZoneList<Variable*>* context_globals, + ZoneList<Variable*>* strong_mode_free_variables = nullptr); + + // Current number of var or const locals. + int num_var_or_const() { return num_var_or_const_; } + + // Result of variable allocation. + int num_stack_slots() const { return num_stack_slots_; } + int num_heap_slots() const { return num_heap_slots_; } + int num_global_slots() const { return num_global_slots_; } + + int StackLocalCount() const; + int ContextLocalCount() const; + int ContextGlobalCount() const; + + // Make sure this scope and all outer scopes are eagerly compiled. + void ForceEagerCompilation() { force_eager_compilation_ = true; } + + // Determine if we can parse a function literal in this scope lazily. + bool AllowsLazyParsing() const; + + // Determine if we can use lazy compilation for this scope. + bool AllowsLazyCompilation() const; + + // Determine if we can use lazy compilation for this scope without a context. + bool AllowsLazyCompilationWithoutContext() const; + + // True if the outer context of this scope is always the native context. + bool HasTrivialOuterContext() const; + + // The number of contexts between this and scope; zero if this == scope. + int ContextChainLength(Scope* scope); + + // The maximum number of nested contexts required for this scope and any inner + // scopes. + int MaxNestedContextChainLength(); + + // Find the first function, script, eval or (declaration) block scope. This is + // the scope where var declarations will be hoisted to in the implementation. + Scope* DeclarationScope(); + + // Find the first non-block declaration scope. This should be either a script, + // function, or eval scope. Same as DeclarationScope(), but skips + // declaration "block" scopes. Used for differentiating associated + // function objects (i.e., the scope for which a function prologue allocates + // a context) or declaring temporaries. + Scope* ClosureScope(); + + // Find the first (non-arrow) function or script scope. This is where + // 'this' is bound, and what determines the function kind. + Scope* ReceiverScope(); + + Handle<ScopeInfo> GetScopeInfo(Isolate* isolate); + + // Get the chain of nested scopes within this scope for the source statement + // position. The scopes will be added to the list from the outermost scope to + // the innermost scope. Only nested block, catch or with scopes are tracked + // and will be returned, but no inner function scopes. + void GetNestedScopeChain(Isolate* isolate, List<Handle<ScopeInfo> >* chain, + int statement_position); + + void CollectNonLocals(HashMap* non_locals); + + // --------------------------------------------------------------------------- + // Strict mode support. + bool IsDeclared(const AstRawString* name) { + // During formal parameter list parsing the scope only contains + // two variables inserted at initialization: "this" and "arguments". + // "this" is an invalid parameter name and "arguments" is invalid parameter + // name in strict mode. Therefore looking up with the map which includes + // "this" and "arguments" in addition to all formal parameters is safe. + return variables_.Lookup(name) != NULL; + } + + bool IsDeclaredParameter(const AstRawString* name) { + // If IsSimpleParameterList is false, duplicate parameters are not allowed, + // however `arguments` may be allowed if function is not strict code. Thus, + // the assumptions explained above do not hold. + return params_.Contains(variables_.Lookup(name)); + } + + SloppyBlockFunctionMap* sloppy_block_function_map() { + return &sloppy_block_function_map_; + } + + // Error handling. + void ReportMessage(int start_position, int end_position, + MessageTemplate::Template message, + const AstRawString* arg); + + // --------------------------------------------------------------------------- + // Debugging. + +#ifdef DEBUG + void Print(int n = 0); // n = indentation; n < 0 => don't print recursively +#endif + + // --------------------------------------------------------------------------- + // Implementation. + private: + // Scope tree. + Scope* outer_scope_; // the immediately enclosing outer scope, or NULL + ZoneList<Scope*> inner_scopes_; // the immediately enclosed inner scopes + + // The scope type. + ScopeType scope_type_; + // If the scope is a function scope, this is the function kind. + FunctionKind function_kind_; + + // Debugging support. + const AstRawString* scope_name_; + + // The variables declared in this scope: + // + // All user-declared variables (incl. parameters). For script scopes + // variables may be implicitly 'declared' by being used (possibly in + // an inner scope) with no intervening with statements or eval calls. + VariableMap variables_; + // Compiler-allocated (user-invisible) temporaries. + ZoneList<Variable*> temps_; + // Parameter list in source order. + ZoneList<Variable*> params_; + // Variables that must be looked up dynamically. + DynamicScopePart* dynamics_; + // Unresolved variables referred to from this scope. + ZoneList<VariableProxy*> unresolved_; + // Declarations. + ZoneList<Declaration*> decls_; + // Convenience variable. + Variable* receiver_; + // Function variable, if any; function scopes only. + VariableDeclaration* function_; + // new.target variable, function scopes only. + Variable* new_target_; + // Convenience variable; function scopes only. + Variable* arguments_; + // Convenience variable; Subclass constructor only + Variable* this_function_; + // Module descriptor; module scopes only. + ModuleDescriptor* module_descriptor_; + + // Map of function names to lists of functions defined in sloppy blocks + SloppyBlockFunctionMap sloppy_block_function_map_; + + // Illegal redeclaration. + Expression* illegal_redecl_; + + // Scope-specific information computed during parsing. + // + // This scope is inside a 'with' of some outer scope. + bool scope_inside_with_; + // This scope contains a 'with' statement. + bool scope_contains_with_; + // This scope or a nested catch scope or with scope contain an 'eval' call. At + // the 'eval' call site this scope is the declaration scope. + bool scope_calls_eval_; + // This scope uses "arguments". + bool scope_uses_arguments_; + // This scope uses "super" property ('super.foo'). + bool scope_uses_super_property_; + // This scope contains an "use asm" annotation. + bool asm_module_; + // This scope's outer context is an asm module. + bool asm_function_; + // This scope's declarations might not be executed in order (e.g., switch). + bool scope_nonlinear_; + // The language mode of this scope. + LanguageMode language_mode_; + // Source positions. + int start_position_; + int end_position_; + + // Computed via PropagateScopeInfo. + bool outer_scope_calls_sloppy_eval_; + bool inner_scope_calls_eval_; + bool force_eager_compilation_; + bool force_context_allocation_; + + // True if it doesn't need scope resolution (e.g., if the scope was + // constructed based on a serialized scope info or a catch context). + bool already_resolved_; + + // True if it holds 'var' declarations. + bool is_declaration_scope_; + + // Computed as variables are declared. + int num_var_or_const_; + + // Computed via AllocateVariables; function, block and catch scopes only. + int num_stack_slots_; + int num_heap_slots_; + int num_global_slots_; + + // Info about the parameter list of a function. + int arity_; + bool has_simple_parameters_; + Variable* rest_parameter_; + int rest_index_; + + // Serialized scope info support. + Handle<ScopeInfo> scope_info_; + bool already_resolved() { return already_resolved_; } + + // Create a non-local variable with a given name. + // These variables are looked up dynamically at runtime. + Variable* NonLocal(const AstRawString* name, VariableMode mode); + + // Variable resolution. + // Possible results of a recursive variable lookup telling if and how a + // variable is bound. These are returned in the output parameter *binding_kind + // of the LookupRecursive function. + enum BindingKind { + // The variable reference could be statically resolved to a variable binding + // which is returned. There is no 'with' statement between the reference and + // the binding and no scope between the reference scope (inclusive) and + // binding scope (exclusive) makes a sloppy 'eval' call. + BOUND, + + // The variable reference could be statically resolved to a variable binding + // which is returned. There is no 'with' statement between the reference and + // the binding, but some scope between the reference scope (inclusive) and + // binding scope (exclusive) makes a sloppy 'eval' call, that might + // possibly introduce variable bindings shadowing the found one. Thus the + // found variable binding is just a guess. + BOUND_EVAL_SHADOWED, + + // The variable reference could not be statically resolved to any binding + // and thus should be considered referencing a global variable. NULL is + // returned. The variable reference is not inside any 'with' statement and + // no scope between the reference scope (inclusive) and script scope + // (exclusive) makes a sloppy 'eval' call. + UNBOUND, + + // The variable reference could not be statically resolved to any binding + // NULL is returned. The variable reference is not inside any 'with' + // statement, but some scope between the reference scope (inclusive) and + // script scope (exclusive) makes a sloppy 'eval' call, that might + // possibly introduce a variable binding. Thus the reference should be + // considered referencing a global variable unless it is shadowed by an + // 'eval' introduced binding. + UNBOUND_EVAL_SHADOWED, + + // The variable could not be statically resolved and needs to be looked up + // dynamically. NULL is returned. There are two possible reasons: + // * A 'with' statement has been encountered and there is no variable + // binding for the name between the variable reference and the 'with'. + // The variable potentially references a property of the 'with' object. + // * The code is being executed as part of a call to 'eval' and the calling + // context chain contains either a variable binding for the name or it + // contains a 'with' context. + DYNAMIC_LOOKUP + }; + + // Lookup a variable reference given by name recursively starting with this + // scope. If the code is executed because of a call to 'eval', the context + // parameter should be set to the calling context of 'eval'. + Variable* LookupRecursive(VariableProxy* proxy, BindingKind* binding_kind, + AstNodeFactory* factory); + MUST_USE_RESULT + bool ResolveVariable(ParseInfo* info, VariableProxy* proxy, + AstNodeFactory* factory); + MUST_USE_RESULT + bool ResolveVariablesRecursively(ParseInfo* info, AstNodeFactory* factory); + + bool CheckStrongModeDeclaration(VariableProxy* proxy, Variable* var); + + // If this scope is a method scope of a class, return the corresponding + // class variable, otherwise nullptr. + ClassVariable* ClassVariableForMethod() const; + + // Scope analysis. + void PropagateScopeInfo(bool outer_scope_calls_sloppy_eval); + bool HasTrivialContext() const; + + // Predicates. + bool MustAllocate(Variable* var); + bool MustAllocateInContext(Variable* var); + bool HasArgumentsParameter(Isolate* isolate); + + // Variable allocation. + void AllocateStackSlot(Variable* var); + void AllocateHeapSlot(Variable* var); + void AllocateParameterLocals(Isolate* isolate); + void AllocateNonParameterLocal(Isolate* isolate, Variable* var); + void AllocateDeclaredGlobal(Isolate* isolate, Variable* var); + void AllocateNonParameterLocalsAndDeclaredGlobals(Isolate* isolate); + void AllocateVariablesRecursively(Isolate* isolate); + void AllocateParameter(Variable* var, int index); + void AllocateReceiver(); + + // Resolve and fill in the allocation information for all variables + // in this scopes. Must be called *after* all scopes have been + // processed (parsed) to ensure that unresolved variables can be + // resolved properly. + // + // In the case of code compiled and run using 'eval', the context + // parameter is the context in which eval was called. In all other + // cases the context parameter is an empty handle. + MUST_USE_RESULT + bool AllocateVariables(ParseInfo* info, AstNodeFactory* factory); + + // Construct a scope based on the scope info. + Scope(Zone* zone, Scope* inner_scope, ScopeType type, + Handle<ScopeInfo> scope_info, AstValueFactory* value_factory); + + // Construct a catch scope with a binding for the name. + Scope(Zone* zone, Scope* inner_scope, const AstRawString* catch_variable_name, + AstValueFactory* value_factory); + + void AddInnerScope(Scope* inner_scope) { + if (inner_scope != NULL) { + inner_scopes_.Add(inner_scope, zone_); + inner_scope->outer_scope_ = this; + } + } + + void RemoveInnerScope(Scope* inner_scope) { + DCHECK_NOT_NULL(inner_scope); + for (int i = 0; i < inner_scopes_.length(); i++) { + if (inner_scopes_[i] == inner_scope) { + inner_scopes_.Remove(i); + break; + } + } + } + + void SetDefaults(ScopeType type, Scope* outer_scope, + Handle<ScopeInfo> scope_info, + FunctionKind function_kind = kNormalFunction); + + AstValueFactory* ast_value_factory_; + Zone* zone_; + + PendingCompilationErrorHandler pending_error_handler_; + + // For tracking which classes are declared consecutively. Needed for strong + // mode. + int class_declaration_group_start_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_AST_SCOPES_H_ diff --git a/deps/v8/src/ast/variables.cc b/deps/v8/src/ast/variables.cc new file mode 100644 index 0000000000..8e00782386 --- /dev/null +++ b/deps/v8/src/ast/variables.cc @@ -0,0 +1,82 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast/variables.h" + +#include "src/ast/ast.h" +#include "src/ast/scopes.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// Implementation Variable. + +const char* Variable::Mode2String(VariableMode mode) { + switch (mode) { + case VAR: return "VAR"; + case CONST_LEGACY: return "CONST_LEGACY"; + case LET: return "LET"; + case CONST: return "CONST"; + case IMPORT: return "IMPORT"; + case DYNAMIC: return "DYNAMIC"; + case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL"; + case DYNAMIC_LOCAL: return "DYNAMIC_LOCAL"; + case TEMPORARY: return "TEMPORARY"; + } + UNREACHABLE(); + return NULL; +} + + +Variable::Variable(Scope* scope, const AstRawString* name, VariableMode mode, + Kind kind, InitializationFlag initialization_flag, + MaybeAssignedFlag maybe_assigned_flag) + : scope_(scope), + name_(name), + mode_(mode), + kind_(kind), + location_(VariableLocation::UNALLOCATED), + index_(-1), + initializer_position_(RelocInfo::kNoPosition), + has_strong_mode_reference_(false), + strong_mode_reference_start_position_(RelocInfo::kNoPosition), + strong_mode_reference_end_position_(RelocInfo::kNoPosition), + local_if_not_shadowed_(NULL), + is_from_eval_(false), + force_context_allocation_(false), + is_used_(false), + initialization_flag_(initialization_flag), + maybe_assigned_(maybe_assigned_flag) { + // Var declared variables never need initialization. + DCHECK(!(mode == VAR && initialization_flag == kNeedsInitialization)); +} + + +bool Variable::IsGlobalObjectProperty() const { + // Temporaries are never global, they must always be allocated in the + // activation frame. + return (IsDynamicVariableMode(mode_) || + (IsDeclaredVariableMode(mode_) && !IsLexicalVariableMode(mode_))) && + scope_ != NULL && scope_->is_script_scope() && !is_this(); +} + + +bool Variable::IsStaticGlobalObjectProperty() const { + // Temporaries are never global, they must always be allocated in the + // activation frame. + return (IsDeclaredVariableMode(mode_) && !IsLexicalVariableMode(mode_)) && + scope_ != NULL && scope_->is_script_scope() && !is_this(); +} + + +int Variable::CompareIndex(Variable* const* v, Variable* const* w) { + int x = (*v)->index(); + int y = (*w)->index(); + // Consider sorting them according to type as well? + return x - y; +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/ast/variables.h b/deps/v8/src/ast/variables.h new file mode 100644 index 0000000000..ca5d1cdd40 --- /dev/null +++ b/deps/v8/src/ast/variables.h @@ -0,0 +1,218 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_VARIABLES_H_ +#define V8_AST_VARIABLES_H_ + +#include "src/ast/ast-value-factory.h" +#include "src/zone.h" + +namespace v8 { +namespace internal { + +// The AST refers to variables via VariableProxies - placeholders for the actual +// variables. Variables themselves are never directly referred to from the AST, +// they are maintained by scopes, and referred to from VariableProxies and Slots +// after binding and variable allocation. + +class ClassVariable; + +class Variable: public ZoneObject { + public: + enum Kind { NORMAL, FUNCTION, CLASS, THIS, ARGUMENTS }; + + Variable(Scope* scope, const AstRawString* name, VariableMode mode, Kind kind, + InitializationFlag initialization_flag, + MaybeAssignedFlag maybe_assigned_flag = kNotAssigned); + + virtual ~Variable() {} + + // Printing support + static const char* Mode2String(VariableMode mode); + + // The source code for an eval() call may refer to a variable that is + // in an outer scope about which we don't know anything (it may not + // be the script scope). scope() is NULL in that case. Currently the + // scope is only used to follow the context chain length. + Scope* scope() const { return scope_; } + + // This is for adjusting the scope of temporaries used when desugaring + // parameter initializers. + void set_scope(Scope* scope) { scope_ = scope; } + + Handle<String> name() const { return name_->string(); } + const AstRawString* raw_name() const { return name_; } + VariableMode mode() const { return mode_; } + bool has_forced_context_allocation() const { + return force_context_allocation_; + } + void ForceContextAllocation() { + force_context_allocation_ = true; + } + bool is_used() { return is_used_; } + void set_is_used() { is_used_ = true; } + MaybeAssignedFlag maybe_assigned() const { return maybe_assigned_; } + void set_maybe_assigned() { maybe_assigned_ = kMaybeAssigned; } + + int initializer_position() { return initializer_position_; } + void set_initializer_position(int pos) { initializer_position_ = pos; } + + bool IsVariable(Handle<String> n) const { + return !is_this() && name().is_identical_to(n); + } + + bool IsUnallocated() const { + return location_ == VariableLocation::UNALLOCATED; + } + bool IsParameter() const { return location_ == VariableLocation::PARAMETER; } + bool IsStackLocal() const { return location_ == VariableLocation::LOCAL; } + bool IsStackAllocated() const { return IsParameter() || IsStackLocal(); } + bool IsContextSlot() const { return location_ == VariableLocation::CONTEXT; } + bool IsGlobalSlot() const { return location_ == VariableLocation::GLOBAL; } + bool IsUnallocatedOrGlobalSlot() const { + return IsUnallocated() || IsGlobalSlot(); + } + bool IsLookupSlot() const { return location_ == VariableLocation::LOOKUP; } + bool IsGlobalObjectProperty() const; + bool IsStaticGlobalObjectProperty() const; + + bool is_dynamic() const { return IsDynamicVariableMode(mode_); } + bool is_const_mode() const { return IsImmutableVariableMode(mode_); } + bool binding_needs_init() const { + return initialization_flag_ == kNeedsInitialization; + } + + bool is_function() const { return kind_ == FUNCTION; } + bool is_class() const { return kind_ == CLASS; } + bool is_this() const { return kind_ == THIS; } + bool is_arguments() const { return kind_ == ARGUMENTS; } + + // For script scopes, the "this" binding is provided by a ScriptContext added + // to the global's ScriptContextTable. This binding might not statically + // resolve to a Variable::THIS binding, instead being DYNAMIC_LOCAL. However + // any variable named "this" does indeed refer to a Variable::THIS binding; + // the grammar ensures this to be the case. So wherever a "this" binding + // might be provided by the global, use HasThisName instead of is_this(). + bool HasThisName(Isolate* isolate) const { + return is_this() || *name() == *isolate->factory()->this_string(); + } + + ClassVariable* AsClassVariable() { + DCHECK(is_class()); + return reinterpret_cast<ClassVariable*>(this); + } + + // True if the variable is named eval and not known to be shadowed. + bool is_possibly_eval(Isolate* isolate) const { + return IsVariable(isolate->factory()->eval_string()); + } + + Variable* local_if_not_shadowed() const { + DCHECK(mode_ == DYNAMIC_LOCAL && local_if_not_shadowed_ != NULL); + return local_if_not_shadowed_; + } + + void set_local_if_not_shadowed(Variable* local) { + local_if_not_shadowed_ = local; + } + + VariableLocation location() const { return location_; } + int index() const { return index_; } + InitializationFlag initialization_flag() const { + return initialization_flag_; + } + + void AllocateTo(VariableLocation location, int index) { + location_ = location; + index_ = index; + } + + void SetFromEval() { is_from_eval_ = true; } + + static int CompareIndex(Variable* const* v, Variable* const* w); + + void RecordStrongModeReference(int start_position, int end_position) { + // Record the earliest reference to the variable. Used in error messages for + // strong mode references to undeclared variables. + if (has_strong_mode_reference_ && + strong_mode_reference_start_position_ < start_position) + return; + has_strong_mode_reference_ = true; + strong_mode_reference_start_position_ = start_position; + strong_mode_reference_end_position_ = end_position; + } + + bool has_strong_mode_reference() const { return has_strong_mode_reference_; } + int strong_mode_reference_start_position() const { + return strong_mode_reference_start_position_; + } + int strong_mode_reference_end_position() const { + return strong_mode_reference_end_position_; + } + PropertyAttributes DeclarationPropertyAttributes() const { + int property_attributes = NONE; + if (IsImmutableVariableMode(mode_)) { + property_attributes |= READ_ONLY; + } + if (is_from_eval_) { + property_attributes |= EVAL_DECLARED; + } + return static_cast<PropertyAttributes>(property_attributes); + } + + private: + Scope* scope_; + const AstRawString* name_; + VariableMode mode_; + Kind kind_; + VariableLocation location_; + int index_; + int initializer_position_; + // Tracks whether the variable is bound to a VariableProxy which is in strong + // mode, and if yes, the source location of the reference. + bool has_strong_mode_reference_; + int strong_mode_reference_start_position_; + int strong_mode_reference_end_position_; + + // If this field is set, this variable references the stored locally bound + // variable, but it might be shadowed by variable bindings introduced by + // sloppy 'eval' calls between the reference scope (inclusive) and the + // binding scope (exclusive). + Variable* local_if_not_shadowed_; + + // True if this variable is introduced by a sloppy eval + bool is_from_eval_; + + // Usage info. + bool force_context_allocation_; // set by variable resolver + bool is_used_; + InitializationFlag initialization_flag_; + MaybeAssignedFlag maybe_assigned_; +}; + +class ClassVariable : public Variable { + public: + ClassVariable(Scope* scope, const AstRawString* name, VariableMode mode, + InitializationFlag initialization_flag, + MaybeAssignedFlag maybe_assigned_flag = kNotAssigned, + int declaration_group_start = -1) + : Variable(scope, name, mode, Variable::CLASS, initialization_flag, + maybe_assigned_flag), + declaration_group_start_(declaration_group_start) {} + + int declaration_group_start() const { return declaration_group_start_; } + void set_declaration_group_start(int declaration_group_start) { + declaration_group_start_ = declaration_group_start; + } + + private: + // For classes we keep track of consecutive groups of delcarations. They are + // needed for strong mode scoping checks. TODO(marja, rossberg): Implement + // checks for functions too. + int declaration_group_start_; +}; +} // namespace internal +} // namespace v8 + +#endif // V8_AST_VARIABLES_H_ |