| Index: src/compiler/ast-graph-builder.cc
|
| diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..47adab368527c29a4fb795e8fc20e9b3f82f15fb
|
| --- /dev/null
|
| +++ b/src/compiler/ast-graph-builder.cc
|
| @@ -0,0 +1,1990 @@
|
| +// 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.
|
| +
|
| +#include "src/compiler/ast-graph-builder.h"
|
| +
|
| +#include "src/compiler.h"
|
| +#include "src/compiler/control-builders.h"
|
| +#include "src/compiler/node-properties.h"
|
| +#include "src/compiler/node-properties-inl.h"
|
| +#include "src/full-codegen.h"
|
| +#include "src/parser.h"
|
| +#include "src/scopes.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +namespace compiler {
|
| +
|
| +AstGraphBuilder::AstGraphBuilder(CompilationInfo* info, JSGraph* jsgraph,
|
| + SourcePositionTable* source_positions)
|
| + : StructuredGraphBuilder(jsgraph->graph(), jsgraph->common()),
|
| + info_(info),
|
| + jsgraph_(jsgraph),
|
| + source_positions_(source_positions),
|
| + globals_(0, info->zone()),
|
| + breakable_(NULL),
|
| + execution_context_(NULL) {
|
| + InitializeAstVisitor(info->zone());
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::GetFunctionClosure() {
|
| + if (!function_closure_.is_set()) {
|
| + // Parameter -1 is special for the function closure
|
| + Operator* op = common()->Parameter(-1);
|
| + Node* node = NewNode(op);
|
| + function_closure_.set(node);
|
| + }
|
| + return function_closure_.get();
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::GetFunctionContext() {
|
| + if (!function_context_.is_set()) {
|
| + // Parameter (arity + 1) is special for the outer context of the function
|
| + Operator* op = common()->Parameter(info()->num_parameters() + 1);
|
| + Node* node = NewNode(op);
|
| + function_context_.set(node);
|
| + }
|
| + return function_context_.get();
|
| +}
|
| +
|
| +
|
| +bool AstGraphBuilder::CreateGraph() {
|
| + Scope* scope = info()->scope();
|
| + ASSERT(graph() != NULL);
|
| +
|
| + SourcePositionTable::Scope start_pos(
|
| + source_positions(),
|
| + SourcePosition(info()->shared_info()->start_position()));
|
| +
|
| + // Set up the basic structure of the graph.
|
| + graph()->SetStart(graph()->NewNode(common()->Start()));
|
| +
|
| + // Initialize the top-level environment.
|
| + Environment env(this, scope, graph()->start());
|
| + set_environment(&env);
|
| +
|
| + // Build node to initialize local function context.
|
| + Node* closure = GetFunctionClosure();
|
| + Node* outer = GetFunctionContext();
|
| + Node* inner = BuildLocalFunctionContext(outer, closure);
|
| +
|
| + // Push top-level function scope for the function body.
|
| + ContextScope top_context(this, scope, inner);
|
| +
|
| + // Build the arguments object if it is used.
|
| + BuildArgumentsObject(scope->arguments());
|
| +
|
| + // Emit tracing call if requested to do so.
|
| + if (FLAG_trace) {
|
| + NewNode(javascript()->Runtime(Runtime::kTraceEnter, 0));
|
| + }
|
| +
|
| + // Visit implicit declaration of the function name.
|
| + if (scope->is_function_scope() && scope->function() != NULL) {
|
| + VisitVariableDeclaration(scope->function());
|
| + }
|
| +
|
| + // Visit declarations within the function scope.
|
| + VisitDeclarations(scope->declarations());
|
| +
|
| + // TODO(mstarzinger): This should do an inlined stack check.
|
| + NewNode(javascript()->Runtime(Runtime::kStackGuard, 0));
|
| +
|
| + // Visit statements in the function body.
|
| + VisitStatements(info()->function()->body());
|
| + if (HasStackOverflow()) return false;
|
| +
|
| + SourcePositionTable::Scope end_pos(
|
| + source_positions(),
|
| + SourcePosition(info()->shared_info()->end_position() - 1));
|
| +
|
| + // Emit tracing call if requested to do so.
|
| + if (FLAG_trace) {
|
| + // TODO(mstarzinger): Only traces implicit return.
|
| + Node* return_value = jsgraph()->UndefinedConstant();
|
| + NewNode(javascript()->Runtime(Runtime::kTraceExit, 1), return_value);
|
| + }
|
| +
|
| + // Return 'undefined' in case we can fall off the end.
|
| + Node* control = NewNode(common()->Return(), jsgraph()->UndefinedConstant());
|
| + UpdateControlDependencyToLeaveFunction(control);
|
| +
|
| + // Finish the basic structure of the graph.
|
| + environment()->UpdateControlDependency(exit_control());
|
| + graph()->SetEnd(NewNode(common()->End()));
|
| +
|
| + return true;
|
| +}
|
| +
|
| +
|
| +// Left-hand side can only be a property, a global or a variable slot.
|
| +enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
|
| +
|
| +
|
| +// Determine the left-hand side kind of an assignment.
|
| +static LhsKind DetermineLhsKind(Expression* expr) {
|
| + Property* property = expr->AsProperty();
|
| + ASSERT(expr->IsValidReferenceExpression());
|
| + LhsKind lhs_kind =
|
| + (property == NULL) ? VARIABLE : (property->key()->IsPropertyName())
|
| + ? NAMED_PROPERTY
|
| + : KEYED_PROPERTY;
|
| + return lhs_kind;
|
| +}
|
| +
|
| +
|
| +// Helper to find an existing shared function info in the baseline code for the
|
| +// given function literal. Used to canonicalize SharedFunctionInfo objects.
|
| +static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
|
| + Code* unoptimized_code, FunctionLiteral* expr) {
|
| + int start_position = expr->start_position();
|
| + for (RelocIterator it(unoptimized_code); !it.done(); it.next()) {
|
| + RelocInfo* rinfo = it.rinfo();
|
| + if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
|
| + Object* obj = rinfo->target_object();
|
| + if (obj->IsSharedFunctionInfo()) {
|
| + SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
|
| + if (shared->start_position() == start_position) {
|
| + return Handle<SharedFunctionInfo>(shared);
|
| + }
|
| + }
|
| + }
|
| + return Handle<SharedFunctionInfo>();
|
| +}
|
| +
|
| +
|
| +StructuredGraphBuilder::Environment* AstGraphBuilder::CopyEnvironment(
|
| + StructuredGraphBuilder::Environment* env) {
|
| + return new (zone()) Environment(*reinterpret_cast<Environment*>(env));
|
| +}
|
| +
|
| +
|
| +AstGraphBuilder::Environment::Environment(AstGraphBuilder* builder,
|
| + Scope* scope,
|
| + Node* control_dependency)
|
| + : StructuredGraphBuilder::Environment(builder, control_dependency),
|
| + parameters_count_(scope->num_parameters() + 1),
|
| + locals_count_(scope->num_stack_slots()),
|
| + parameters_node_(NULL),
|
| + locals_node_(NULL),
|
| + stack_node_(NULL),
|
| + parameters_dirty_(false),
|
| + locals_dirty_(false),
|
| + stack_dirty_(false) {
|
| + ASSERT_EQ(scope->num_parameters() + 1, parameters_count());
|
| +
|
| + // Bind the receiver variable.
|
| + values()->insert(values()->end(), parameters_count(),
|
| + static_cast<Node*>(NULL));
|
| + Node* receiver = builder->graph()->NewNode(common()->Parameter(0));
|
| + Bind(scope->receiver(), receiver);
|
| +
|
| + // Bind all parameter variables. The parameter indices are shifted by 1
|
| + // (receiver is parameter index -1 but environment index 0).
|
| + for (int i = 0; i < scope->num_parameters(); ++i) {
|
| + // Unused parameters are allocated to Variable::UNALLOCATED.
|
| + if (!scope->parameter(i)->IsParameter()) continue;
|
| + Node* parameter = builder->graph()->NewNode(common()->Parameter(i + 1));
|
| + Bind(scope->parameter(i), parameter);
|
| + }
|
| +
|
| + // Bind all local variables to undefined.
|
| + Node* undefined_constant = builder->jsgraph()->UndefinedConstant();
|
| + values()->insert(values()->end(), locals_count(), undefined_constant);
|
| +}
|
| +
|
| +
|
| +AstGraphBuilder::Environment::Environment(const Environment& copy)
|
| + : StructuredGraphBuilder::Environment(
|
| + static_cast<StructuredGraphBuilder::Environment>(copy)),
|
| + parameters_count_(copy.parameters_count_),
|
| + locals_count_(copy.locals_count_),
|
| + parameters_node_(copy.parameters_node_),
|
| + locals_node_(copy.locals_node_),
|
| + stack_node_(copy.stack_node_),
|
| + parameters_dirty_(copy.parameters_dirty_),
|
| + locals_dirty_(copy.locals_dirty_),
|
| + stack_dirty_(copy.stack_dirty_) {}
|
| +
|
| +
|
| +Node* AstGraphBuilder::Environment::Checkpoint(BailoutId ast_id) {
|
| + UNIMPLEMENTED(); // TODO(mstarzinger): Implementation below is incomplete.
|
| + if (parameters_dirty_) {
|
| + Node** parameters = &values()->front();
|
| + parameters_node_ = graph()->NewNode(NULL, parameters_count(), parameters);
|
| + parameters_dirty_ = false;
|
| + }
|
| + if (locals_dirty_) {
|
| + Node** locals = &values()->at(parameters_count_);
|
| + locals_node_ = graph()->NewNode(NULL, locals_count(), locals);
|
| + locals_dirty_ = false;
|
| + }
|
| + FrameStateDescriptor descriptor(ast_id);
|
| + // TODO(jarin): add environment to the node.
|
| + Operator* op = common()->FrameState(descriptor);
|
| +
|
| + return graph()->NewNode(op);
|
| +}
|
| +
|
| +
|
| +AstGraphBuilder::AstContext::AstContext(AstGraphBuilder* own,
|
| + Expression::Context kind)
|
| + : kind_(kind), owner_(own), outer_(own->ast_context()) {
|
| + owner()->set_ast_context(this); // Push.
|
| +#ifdef DEBUG
|
| + original_height_ = environment()->stack_height();
|
| +#endif
|
| +}
|
| +
|
| +
|
| +AstGraphBuilder::AstContext::~AstContext() {
|
| + owner()->set_ast_context(outer_); // Pop.
|
| +}
|
| +
|
| +
|
| +AstGraphBuilder::AstEffectContext::~AstEffectContext() {
|
| + ASSERT(environment()->stack_height() == original_height_);
|
| +}
|
| +
|
| +
|
| +AstGraphBuilder::AstValueContext::~AstValueContext() {
|
| + ASSERT(environment()->stack_height() == original_height_ + 1);
|
| +}
|
| +
|
| +
|
| +AstGraphBuilder::AstTestContext::~AstTestContext() {
|
| + ASSERT(environment()->stack_height() == original_height_ + 1);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::AstEffectContext::ProduceValue(Node* value) {
|
| + // The value is ignored.
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::AstValueContext::ProduceValue(Node* value) {
|
| + environment()->Push(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::AstTestContext::ProduceValue(Node* value) {
|
| + environment()->Push(owner()->BuildToBoolean(value));
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::AstEffectContext::ConsumeValue() { return NULL; }
|
| +
|
| +
|
| +Node* AstGraphBuilder::AstValueContext::ConsumeValue() {
|
| + return environment()->Pop();
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::AstTestContext::ConsumeValue() {
|
| + return environment()->Pop();
|
| +}
|
| +
|
| +
|
| +AstGraphBuilder::BreakableScope* AstGraphBuilder::BreakableScope::FindBreakable(
|
| + BreakableStatement* target) {
|
| + BreakableScope* current = this;
|
| + while (current != NULL && current->target_ != target) {
|
| + owner_->environment()->Drop(current->drop_extra_);
|
| + current = current->next_;
|
| + }
|
| + ASSERT(current != NULL); // Always found (unless stack is malformed).
|
| + return current;
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::BreakableScope::BreakTarget(BreakableStatement* stmt) {
|
| + FindBreakable(stmt)->control_->Break();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::BreakableScope::ContinueTarget(BreakableStatement* stmt) {
|
| + FindBreakable(stmt)->control_->Continue();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitForValueOrNull(Expression* expr) {
|
| + if (expr == NULL) {
|
| + return environment()->Push(jsgraph()->NullConstant());
|
| + }
|
| + VisitForValue(expr);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitForValues(ZoneList<Expression*>* exprs) {
|
| + for (int i = 0; i < exprs->length(); ++i) {
|
| + VisitForValue(exprs->at(i));
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitForValue(Expression* expr) {
|
| + AstValueContext for_value(this);
|
| + if (!HasStackOverflow()) {
|
| + expr->Accept(this);
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitForEffect(Expression* expr) {
|
| + AstEffectContext for_effect(this);
|
| + if (!HasStackOverflow()) {
|
| + expr->Accept(this);
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitForTest(Expression* expr) {
|
| + AstTestContext for_condition(this);
|
| + if (!HasStackOverflow()) {
|
| + expr->Accept(this);
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitVariableDeclaration(VariableDeclaration* decl) {
|
| + Variable* variable = decl->proxy()->var();
|
| + VariableMode mode = decl->mode();
|
| + bool hole_init = mode == CONST || mode == CONST_LEGACY || mode == LET;
|
| + switch (variable->location()) {
|
| + case Variable::UNALLOCATED: {
|
| + Handle<Oddball> value = variable->binding_needs_init()
|
| + ? isolate()->factory()->the_hole_value()
|
| + : isolate()->factory()->undefined_value();
|
| + globals()->Add(variable->name(), zone());
|
| + globals()->Add(value, zone());
|
| + break;
|
| + }
|
| + case Variable::PARAMETER:
|
| + case Variable::LOCAL:
|
| + if (hole_init) {
|
| + Node* value = jsgraph()->TheHoleConstant();
|
| + environment()->Bind(variable, value);
|
| + }
|
| + break;
|
| + case Variable::CONTEXT:
|
| + if (hole_init) {
|
| + Node* value = jsgraph()->TheHoleConstant();
|
| + Operator* op = javascript()->StoreContext(0, variable->index());
|
| + NewNode(op, current_context(), value);
|
| + }
|
| + break;
|
| + case Variable::LOOKUP:
|
| + UNIMPLEMENTED();
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* decl) {
|
| + Variable* variable = decl->proxy()->var();
|
| + switch (variable->location()) {
|
| + case Variable::UNALLOCATED: {
|
| + Handle<SharedFunctionInfo> function =
|
| + Compiler::BuildFunctionInfo(decl->fun(), info()->script());
|
| + // Check for stack-overflow exception.
|
| + if (function.is_null()) return SetStackOverflow();
|
| + globals()->Add(variable->name(), zone());
|
| + globals()->Add(function, zone());
|
| + break;
|
| + }
|
| + case Variable::PARAMETER:
|
| + case Variable::LOCAL: {
|
| + VisitForValue(decl->fun());
|
| + Node* value = environment()->Pop();
|
| + environment()->Bind(variable, value);
|
| + break;
|
| + }
|
| + case Variable::CONTEXT: {
|
| + VisitForValue(decl->fun());
|
| + Node* value = environment()->Pop();
|
| + Operator* op = javascript()->StoreContext(0, variable->index());
|
| + NewNode(op, current_context(), value);
|
| + break;
|
| + }
|
| + case Variable::LOOKUP:
|
| + UNIMPLEMENTED();
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* decl) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitImportDeclaration(ImportDeclaration* decl) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitExportDeclaration(ExportDeclaration* decl) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitModuleLiteral(ModuleLiteral* modl) { UNREACHABLE(); }
|
| +
|
| +
|
| +void AstGraphBuilder::VisitModuleVariable(ModuleVariable* modl) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitModulePath(ModulePath* modl) { UNREACHABLE(); }
|
| +
|
| +
|
| +void AstGraphBuilder::VisitModuleUrl(ModuleUrl* modl) { UNREACHABLE(); }
|
| +
|
| +
|
| +void AstGraphBuilder::VisitBlock(Block* stmt) {
|
| + BlockBuilder block(this);
|
| + BreakableScope scope(this, stmt, &block, 0);
|
| + if (stmt->labels() != NULL) block.BeginBlock();
|
| + if (stmt->scope() == NULL) {
|
| + // Visit statements in the same scope, no declarations.
|
| + VisitStatements(stmt->statements());
|
| + } else {
|
| + Operator* op = javascript()->CreateBlockContext();
|
| + Node* scope_info = jsgraph()->Constant(stmt->scope()->GetScopeInfo());
|
| + Node* context = NewNode(op, scope_info, GetFunctionClosure());
|
| + ContextScope scope(this, stmt->scope(), context);
|
| +
|
| + // Visit declarations and statements in a block scope.
|
| + VisitDeclarations(stmt->scope()->declarations());
|
| + VisitStatements(stmt->statements());
|
| + }
|
| + if (stmt->labels() != NULL) block.EndBlock();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitModuleStatement(ModuleStatement* stmt) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
|
| + VisitForEffect(stmt->expression());
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
|
| + // Do nothing.
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitIfStatement(IfStatement* stmt) {
|
| + IfBuilder compare_if(this);
|
| + VisitForTest(stmt->condition());
|
| + Node* condition = environment()->Pop();
|
| + compare_if.If(condition);
|
| + compare_if.Then();
|
| + Visit(stmt->then_statement());
|
| + compare_if.Else();
|
| + Visit(stmt->else_statement());
|
| + compare_if.End();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
|
| + StructuredGraphBuilder::Environment* env = environment()->CopyAsUnreachable();
|
| + breakable()->ContinueTarget(stmt->target());
|
| + set_environment(env);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
|
| + StructuredGraphBuilder::Environment* env = environment()->CopyAsUnreachable();
|
| + breakable()->BreakTarget(stmt->target());
|
| + set_environment(env);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
|
| + VisitForValue(stmt->expression());
|
| + Node* result = environment()->Pop();
|
| + Node* control = NewNode(common()->Return(), result);
|
| + UpdateControlDependencyToLeaveFunction(control);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitWithStatement(WithStatement* stmt) {
|
| + VisitForValue(stmt->expression());
|
| + Node* value = environment()->Pop();
|
| + Operator* op = javascript()->CreateWithContext();
|
| + Node* context = NewNode(op, value, GetFunctionClosure());
|
| + ContextScope scope(this, stmt->scope(), context);
|
| + Visit(stmt->statement());
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
| + ZoneList<CaseClause*>* clauses = stmt->cases();
|
| + SwitchBuilder compare_switch(this, clauses->length());
|
| + BreakableScope scope(this, stmt, &compare_switch, 0);
|
| + compare_switch.BeginSwitch();
|
| + int default_index = -1;
|
| +
|
| + // Keep the switch value on the stack until a case matches.
|
| + VisitForValue(stmt->tag());
|
| + Node* tag = environment()->Top();
|
| +
|
| + // Iterate over all cases and create nodes for label comparison.
|
| + for (int i = 0; i < clauses->length(); i++) {
|
| + CaseClause* clause = clauses->at(i);
|
| +
|
| + // The default is not a test, remember index.
|
| + if (clause->is_default()) {
|
| + default_index = i;
|
| + continue;
|
| + }
|
| +
|
| + // Create nodes to perform label comparison as if via '==='. The switch
|
| + // value is still on the operand stack while the label is evaluated.
|
| + VisitForValue(clause->label());
|
| + Node* label = environment()->Pop();
|
| + Operator* op = javascript()->StrictEqual();
|
| + Node* condition = NewNode(op, tag, label);
|
| + compare_switch.BeginLabel(i, condition);
|
| +
|
| + // Discard the switch value at label match.
|
| + environment()->Pop();
|
| + compare_switch.EndLabel();
|
| + }
|
| +
|
| + // Discard the switch value and mark the default case.
|
| + environment()->Pop();
|
| + if (default_index >= 0) {
|
| + compare_switch.DefaultAt(default_index);
|
| + }
|
| +
|
| + // Iterate over all cases and create nodes for case bodies.
|
| + for (int i = 0; i < clauses->length(); i++) {
|
| + CaseClause* clause = clauses->at(i);
|
| + compare_switch.BeginCase(i);
|
| + VisitStatements(clause->statements());
|
| + compare_switch.EndCase();
|
| + }
|
| +
|
| + compare_switch.EndSwitch();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
|
| + LoopBuilder while_loop(this);
|
| + while_loop.BeginLoop();
|
| + VisitIterationBody(stmt, &while_loop, 0);
|
| + while_loop.EndBody();
|
| + VisitForTest(stmt->cond());
|
| + Node* condition = environment()->Pop();
|
| + while_loop.BreakUnless(condition);
|
| + while_loop.EndLoop();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
|
| + LoopBuilder while_loop(this);
|
| + while_loop.BeginLoop();
|
| + VisitForTest(stmt->cond());
|
| + Node* condition = environment()->Pop();
|
| + while_loop.BreakUnless(condition);
|
| + VisitIterationBody(stmt, &while_loop, 0);
|
| + while_loop.EndBody();
|
| + while_loop.EndLoop();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitForStatement(ForStatement* stmt) {
|
| + LoopBuilder for_loop(this);
|
| + VisitIfNotNull(stmt->init());
|
| + for_loop.BeginLoop();
|
| + if (stmt->cond() != NULL) {
|
| + VisitForTest(stmt->cond());
|
| + Node* condition = environment()->Pop();
|
| + for_loop.BreakUnless(condition);
|
| + }
|
| + VisitIterationBody(stmt, &for_loop, 0);
|
| + for_loop.EndBody();
|
| + VisitIfNotNull(stmt->next());
|
| + for_loop.EndLoop();
|
| +}
|
| +
|
| +
|
| +// TODO(dcarney): this is a big function. Try to clean up some.
|
| +void AstGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
|
| + VisitForValue(stmt->subject());
|
| + Node* obj = environment()->Pop();
|
| + // Check for undefined or null before entering loop.
|
| + IfBuilder is_undefined(this);
|
| + Node* is_undefined_cond =
|
| + NewNode(javascript()->StrictEqual(), obj, jsgraph()->UndefinedConstant());
|
| + is_undefined.If(is_undefined_cond);
|
| + is_undefined.Then();
|
| + is_undefined.Else();
|
| + {
|
| + IfBuilder is_null(this);
|
| + Node* is_null_cond =
|
| + NewNode(javascript()->StrictEqual(), obj, jsgraph()->NullConstant());
|
| + is_null.If(is_null_cond);
|
| + is_null.Then();
|
| + is_null.Else();
|
| + // Convert object to jsobject.
|
| + // PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
|
| + obj = NewNode(javascript()->ToObject(), obj);
|
| + environment()->Push(obj);
|
| + // TODO(dcarney): should do a fast enum cache check here to skip runtime.
|
| + environment()->Push(obj);
|
| + Node* cache_type = ProcessArguments(
|
| + javascript()->Runtime(Runtime::kGetPropertyNamesFast, 1), 1);
|
| + // TODO(dcarney): these next runtime calls should be removed in favour of
|
| + // a few simplified instructions.
|
| + environment()->Push(obj);
|
| + environment()->Push(cache_type);
|
| + Node* cache_pair =
|
| + ProcessArguments(javascript()->Runtime(Runtime::kForInInit, 2), 2);
|
| + // cache_type may have been replaced.
|
| + Node* cache_array = NewNode(common()->Projection(0), cache_pair);
|
| + cache_type = NewNode(common()->Projection(1), cache_pair);
|
| + environment()->Push(cache_type);
|
| + environment()->Push(cache_array);
|
| + Node* cache_length = ProcessArguments(
|
| + javascript()->Runtime(Runtime::kForInCacheArrayLength, 2), 2);
|
| + {
|
| + // TODO(dcarney): this check is actually supposed to be for the
|
| + // empty enum case only.
|
| + IfBuilder have_no_properties(this);
|
| + Node* empty_array_cond = NewNode(javascript()->StrictEqual(),
|
| + cache_length, jsgraph()->ZeroConstant());
|
| + have_no_properties.If(empty_array_cond);
|
| + have_no_properties.Then();
|
| + // Pop obj and skip loop.
|
| + environment()->Pop();
|
| + have_no_properties.Else();
|
| + {
|
| + // Construct the rest of the environment.
|
| + environment()->Push(cache_type);
|
| + environment()->Push(cache_array);
|
| + environment()->Push(cache_length);
|
| + environment()->Push(jsgraph()->ZeroConstant());
|
| + // PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
|
| + LoopBuilder for_loop(this);
|
| + for_loop.BeginLoop();
|
| + // Check loop termination condition.
|
| + Node* index = environment()->Peek(0);
|
| + Node* exit_cond =
|
| + NewNode(javascript()->LessThan(), index, cache_length);
|
| + for_loop.BreakUnless(exit_cond);
|
| + // TODO(dcarney): this runtime call should be a handful of
|
| + // simplified instructions that
|
| + // basically produce
|
| + // value = array[index]
|
| + environment()->Push(obj);
|
| + environment()->Push(cache_array);
|
| + environment()->Push(cache_type);
|
| + environment()->Push(index);
|
| + Node* pair =
|
| + ProcessArguments(javascript()->Runtime(Runtime::kForInNext, 4), 4);
|
| + Node* value = NewNode(common()->Projection(0), pair);
|
| + Node* should_filter = NewNode(common()->Projection(1), pair);
|
| + environment()->Push(value);
|
| + {
|
| + // Test if FILTER_KEY needs to be called.
|
| + IfBuilder test_should_filter(this);
|
| + Node* should_filter_cond =
|
| + NewNode(javascript()->StrictEqual(), should_filter,
|
| + jsgraph()->TrueConstant());
|
| + test_should_filter.If(should_filter_cond);
|
| + test_should_filter.Then();
|
| + value = environment()->Pop();
|
| + // TODO(dcarney): Better load from function context.
|
| + // See comment in BuildLoadBuiltinsObject.
|
| + Handle<JSFunction> function(JSFunction::cast(
|
| + info()->context()->builtins()->javascript_builtin(
|
| + Builtins::FILTER_KEY)));
|
| + // Callee.
|
| + environment()->Push(jsgraph()->HeapConstant(function));
|
| + // Receiver.
|
| + environment()->Push(obj);
|
| + // Args.
|
| + environment()->Push(value);
|
| + // result is either the string key or Smi(0) indicating the property
|
| + // is gone.
|
| + Node* res = ProcessArguments(
|
| + javascript()->Call(3, NO_CALL_FUNCTION_FLAGS), 3);
|
| + Node* property_missing = NewNode(javascript()->StrictEqual(), res,
|
| + jsgraph()->ZeroConstant());
|
| + {
|
| + IfBuilder is_property_missing(this);
|
| + is_property_missing.If(property_missing);
|
| + is_property_missing.Then();
|
| + // Inc counter and continue.
|
| + Node* index_inc =
|
| + NewNode(javascript()->Add(), index, jsgraph()->OneConstant());
|
| + environment()->Poke(0, index_inc);
|
| + for_loop.Continue();
|
| + is_property_missing.Else();
|
| + is_property_missing.End();
|
| + }
|
| + // Replace 'value' in environment.
|
| + environment()->Push(res);
|
| + test_should_filter.Else();
|
| + test_should_filter.End();
|
| + }
|
| + value = environment()->Pop();
|
| + // Bind value and do loop body.
|
| + VisitForInAssignment(stmt->each(), value);
|
| + VisitIterationBody(stmt, &for_loop, 5);
|
| + // Inc counter and continue.
|
| + Node* index_inc =
|
| + NewNode(javascript()->Add(), index, jsgraph()->OneConstant());
|
| + environment()->Poke(0, index_inc);
|
| + for_loop.EndBody();
|
| + for_loop.EndLoop();
|
| + environment()->Drop(5);
|
| + // PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
|
| + }
|
| + have_no_properties.End();
|
| + }
|
| + is_null.End();
|
| + }
|
| + is_undefined.End();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitForOfStatement(ForOfStatement* stmt) {
|
| + VisitForValue(stmt->subject());
|
| + environment()->Pop();
|
| + // TODO(turbofan): create and use loop builder.
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
|
| + // TODO(turbofan): Do we really need a separate reloc-info for this?
|
| + NewNode(javascript()->Runtime(Runtime::kDebugBreak, 0));
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
|
| + Node* context = current_context();
|
| +
|
| + // Build a new shared function info if we cannot find one in the baseline
|
| + // code. We also have a stack overflow if the recursive compilation did.
|
| + Handle<SharedFunctionInfo> shared_info =
|
| + SearchSharedFunctionInfo(info()->shared_info()->code(), expr);
|
| + if (shared_info.is_null()) {
|
| + shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
|
| + CHECK(!shared_info.is_null()); // TODO(mstarzinger): Set stack overflow?
|
| + }
|
| +
|
| + // Create node to instantiate a new closure.
|
| + Node* info = jsgraph()->Constant(shared_info);
|
| + Node* pretenure = expr->pretenure() ? jsgraph()->TrueConstant()
|
| + : jsgraph()->FalseConstant();
|
| + Operator* op = javascript()->Runtime(Runtime::kNewClosure, 3);
|
| + Node* value = NewNode(op, context, info, pretenure);
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitConditional(Conditional* expr) {
|
| + IfBuilder compare_if(this);
|
| + VisitForTest(expr->condition());
|
| + Node* condition = environment()->Pop();
|
| + compare_if.If(condition);
|
| + compare_if.Then();
|
| + Visit(expr->then_expression());
|
| + compare_if.Else();
|
| + Visit(expr->else_expression());
|
| + compare_if.End();
|
| + ast_context()->ReplaceValue();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
| + Node* value = BuildVariableLoad(expr->var());
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitLiteral(Literal* expr) {
|
| + Node* value = jsgraph()->Constant(expr->value());
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
|
| + Handle<JSFunction> closure = info()->closure();
|
| +
|
| + // Create node to materialize a regular expression literal.
|
| + Node* literals_array = jsgraph()->Constant(handle(closure->literals()));
|
| + Node* literal_index = jsgraph()->Constant(expr->literal_index());
|
| + Node* pattern = jsgraph()->Constant(expr->pattern());
|
| + Node* flags = jsgraph()->Constant(expr->flags());
|
| + Operator* op = javascript()->Runtime(Runtime::kMaterializeRegExpLiteral, 4);
|
| + Node* literal = NewNode(op, literals_array, literal_index, pattern, flags);
|
| + ast_context()->ProduceValue(literal);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
| + Handle<JSFunction> closure = info()->closure();
|
| +
|
| + // Create node to deep-copy the literal boilerplate.
|
| + expr->BuildConstantProperties(isolate());
|
| + Node* literals_array = jsgraph()->Constant(handle(closure->literals()));
|
| + Node* literal_index = jsgraph()->Constant(expr->literal_index());
|
| + Node* constants = jsgraph()->Constant(expr->constant_properties());
|
| + Node* flags = jsgraph()->Constant(expr->ComputeFlags());
|
| + Operator* op = javascript()->Runtime(Runtime::kCreateObjectLiteral, 4);
|
| + Node* literal = NewNode(op, literals_array, literal_index, constants, flags);
|
| +
|
| + // The object is expected on the operand stack during computation of the
|
| + // property values and is the value of the entire expression.
|
| + environment()->Push(literal);
|
| +
|
| + // 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.
|
| + expr->CalculateEmitStore(zone());
|
| +
|
| + // Create nodes to store computed values into the literal.
|
| + AccessorTable accessor_table(zone());
|
| + for (int i = 0; i < expr->properties()->length(); i++) {
|
| + ObjectLiteral::Property* property = expr->properties()->at(i);
|
| + if (property->IsCompileTimeValue()) continue;
|
| +
|
| + Literal* key = property->key();
|
| + switch (property->kind()) {
|
| + case ObjectLiteral::Property::CONSTANT:
|
| + UNREACHABLE();
|
| + case ObjectLiteral::Property::MATERIALIZED_LITERAL:
|
| + ASSERT(!CompileTimeValue::IsCompileTimeValue(property->value()));
|
| + // 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()) {
|
| + VisitForValue(property->value());
|
| + Node* value = environment()->Pop();
|
| + PrintableUnique<Name> name = MakeUnique(key->AsPropertyName());
|
| + NewNode(javascript()->StoreNamed(name), literal, value);
|
| + } else {
|
| + VisitForEffect(property->value());
|
| + }
|
| + break;
|
| + }
|
| + environment()->Push(literal); // Duplicate receiver.
|
| + VisitForValue(property->key());
|
| + VisitForValue(property->value());
|
| + Node* value = environment()->Pop();
|
| + Node* key = environment()->Pop();
|
| + Node* receiver = environment()->Pop();
|
| + if (property->emit_store()) {
|
| + Node* strict = jsgraph()->Constant(SLOPPY);
|
| + Operator* op = javascript()->Runtime(Runtime::kSetProperty, 4);
|
| + NewNode(op, receiver, key, value, strict);
|
| + }
|
| + break;
|
| + }
|
| + case ObjectLiteral::Property::PROTOTYPE: {
|
| + environment()->Push(literal); // Duplicate receiver.
|
| + VisitForValue(property->value());
|
| + Node* value = environment()->Pop();
|
| + Node* receiver = environment()->Pop();
|
| + if (property->emit_store()) {
|
| + Operator* op = javascript()->Runtime(Runtime::kSetPrototype, 2);
|
| + NewNode(op, receiver, value);
|
| + }
|
| + break;
|
| + }
|
| + case ObjectLiteral::Property::GETTER:
|
| + accessor_table.lookup(key)->second->getter = property->value();
|
| + break;
|
| + case ObjectLiteral::Property::SETTER:
|
| + accessor_table.lookup(key)->second->setter = property->value();
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // Create nodes to define accessors, using only a single call to the runtime
|
| + // for each pair of corresponding getters and setters.
|
| + for (AccessorTable::Iterator it = accessor_table.begin();
|
| + it != accessor_table.end(); ++it) {
|
| + VisitForValue(it->first);
|
| + VisitForValueOrNull(it->second->getter);
|
| + VisitForValueOrNull(it->second->setter);
|
| + Node* setter = environment()->Pop();
|
| + Node* getter = environment()->Pop();
|
| + Node* name = environment()->Pop();
|
| + Node* attr = jsgraph()->Constant(NONE);
|
| + Operator* op =
|
| + javascript()->Runtime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
| + NewNode(op, literal, name, getter, setter, attr);
|
| + }
|
| +
|
| + // Transform literals that contain functions to fast properties.
|
| + if (expr->has_function()) {
|
| + Operator* op = javascript()->Runtime(Runtime::kToFastProperties, 1);
|
| + NewNode(op, literal);
|
| + }
|
| +
|
| + ast_context()->ProduceValue(environment()->Pop());
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
|
| + Handle<JSFunction> closure = info()->closure();
|
| +
|
| + // Create node to deep-copy the literal boilerplate.
|
| + expr->BuildConstantElements(isolate());
|
| + Node* literals_array = jsgraph()->Constant(handle(closure->literals()));
|
| + Node* literal_index = jsgraph()->Constant(expr->literal_index());
|
| + Node* constants = jsgraph()->Constant(expr->constant_elements());
|
| + Node* flags = jsgraph()->Constant(expr->ComputeFlags());
|
| + Operator* op = javascript()->Runtime(Runtime::kCreateArrayLiteral, 4);
|
| + Node* literal = NewNode(op, literals_array, literal_index, constants, flags);
|
| +
|
| + // The array and the literal index are both expected on the operand stack
|
| + // during computation of the element values.
|
| + environment()->Push(literal);
|
| + environment()->Push(literal_index);
|
| +
|
| + // Create nodes to evaluate all the non-constant subexpressions and to store
|
| + // them into the newly cloned array.
|
| + for (int i = 0; i < expr->values()->length(); i++) {
|
| + Expression* subexpr = expr->values()->at(i);
|
| + if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
|
| +
|
| + VisitForValue(subexpr);
|
| + Node* value = environment()->Pop();
|
| + Node* index = jsgraph()->Constant(i);
|
| + NewNode(javascript()->StoreProperty(), literal, index, value);
|
| + }
|
| +
|
| + environment()->Pop(); // Array literal index.
|
| + ast_context()->ProduceValue(environment()->Pop());
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value) {
|
| + ASSERT(expr->IsValidReferenceExpression());
|
| +
|
| + // Left-hand side can only be a property, a global or a variable slot.
|
| + Property* property = expr->AsProperty();
|
| + LhsKind assign_type = DetermineLhsKind(expr);
|
| +
|
| + // Evaluate LHS expression and store the value.
|
| + switch (assign_type) {
|
| + case VARIABLE: {
|
| + Variable* var = expr->AsVariableProxy()->var();
|
| + BuildVariableAssignment(var, value, Token::ASSIGN);
|
| + break;
|
| + }
|
| + case NAMED_PROPERTY: {
|
| + environment()->Push(value);
|
| + VisitForValue(property->obj());
|
| + Node* object = environment()->Pop();
|
| + value = environment()->Pop();
|
| + PrintableUnique<Name> name =
|
| + MakeUnique(property->key()->AsLiteral()->AsPropertyName());
|
| + NewNode(javascript()->StoreNamed(name), object, value);
|
| + break;
|
| + }
|
| + case KEYED_PROPERTY: {
|
| + environment()->Push(value);
|
| + VisitForValue(property->obj());
|
| + VisitForValue(property->key());
|
| + Node* key = environment()->Pop();
|
| + Node* object = environment()->Pop();
|
| + value = environment()->Pop();
|
| + NewNode(javascript()->StoreProperty(), object, key, value);
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitAssignment(Assignment* expr) {
|
| + ASSERT(expr->target()->IsValidReferenceExpression());
|
| +
|
| + // Left-hand side can only be a property, a global or a variable slot.
|
| + Property* property = expr->target()->AsProperty();
|
| + LhsKind assign_type = DetermineLhsKind(expr->target());
|
| +
|
| + // Evaluate LHS expression.
|
| + switch (assign_type) {
|
| + case VARIABLE:
|
| + // Nothing to do here.
|
| + break;
|
| + case NAMED_PROPERTY:
|
| + VisitForValue(property->obj());
|
| + break;
|
| + case KEYED_PROPERTY: {
|
| + VisitForValue(property->obj());
|
| + VisitForValue(property->key());
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // Evaluate the value and potentially handle compound assignments by loading
|
| + // the left-hand side value and performing a binary operation.
|
| + if (expr->is_compound()) {
|
| + Node* old_value = NULL;
|
| + switch (assign_type) {
|
| + case VARIABLE: {
|
| + Variable* variable = expr->target()->AsVariableProxy()->var();
|
| + old_value = BuildVariableLoad(variable);
|
| + break;
|
| + }
|
| + case NAMED_PROPERTY: {
|
| + Node* object = environment()->Top();
|
| + PrintableUnique<Name> name =
|
| + MakeUnique(property->key()->AsLiteral()->AsPropertyName());
|
| + old_value = NewNode(javascript()->LoadNamed(name), object);
|
| + break;
|
| + }
|
| + case KEYED_PROPERTY: {
|
| + Node* key = environment()->Top();
|
| + Node* object = environment()->Peek(1);
|
| + old_value = NewNode(javascript()->LoadProperty(), object, key);
|
| + break;
|
| + }
|
| + }
|
| + environment()->Push(old_value);
|
| + VisitForValue(expr->value());
|
| + Node* right = environment()->Pop();
|
| + Node* left = environment()->Pop();
|
| + Node* value = BuildBinaryOp(left, right, expr->binary_op());
|
| + environment()->Push(value);
|
| + } else {
|
| + VisitForValue(expr->value());
|
| + }
|
| +
|
| + // Store the value.
|
| + Node* value = environment()->Pop();
|
| + switch (assign_type) {
|
| + case VARIABLE: {
|
| + Variable* variable = expr->target()->AsVariableProxy()->var();
|
| + BuildVariableAssignment(variable, value, expr->op());
|
| + break;
|
| + }
|
| + case NAMED_PROPERTY: {
|
| + Node* object = environment()->Pop();
|
| + PrintableUnique<Name> name =
|
| + MakeUnique(property->key()->AsLiteral()->AsPropertyName());
|
| + NewNode(javascript()->StoreNamed(name), object, value);
|
| + break;
|
| + }
|
| + case KEYED_PROPERTY: {
|
| + Node* key = environment()->Pop();
|
| + Node* object = environment()->Pop();
|
| + NewNode(javascript()->StoreProperty(), object, key, value);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitYield(Yield* expr) {
|
| + VisitForValue(expr->generator_object());
|
| + VisitForValue(expr->expression());
|
| + environment()->Pop();
|
| + environment()->Pop();
|
| + // TODO(turbofan): VisitYield
|
| + ast_context()->ProduceValue(jsgraph()->UndefinedConstant());
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitThrow(Throw* expr) {
|
| + VisitForValue(expr->exception());
|
| + Node* exception = environment()->Pop();
|
| + Operator* op = javascript()->Runtime(Runtime::kThrow, 1);
|
| + Node* value = NewNode(op, exception);
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitProperty(Property* expr) {
|
| + Node* value;
|
| + if (expr->key()->IsPropertyName()) {
|
| + VisitForValue(expr->obj());
|
| + Node* object = environment()->Pop();
|
| + PrintableUnique<Name> name =
|
| + MakeUnique(expr->key()->AsLiteral()->AsPropertyName());
|
| + value = NewNode(javascript()->LoadNamed(name), object);
|
| + } else {
|
| + VisitForValue(expr->obj());
|
| + VisitForValue(expr->key());
|
| + Node* key = environment()->Pop();
|
| + Node* object = environment()->Pop();
|
| + value = NewNode(javascript()->LoadProperty(), object, key);
|
| + }
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitCall(Call* expr) {
|
| + Expression* callee = expr->expression();
|
| + Call::CallType call_type = expr->GetCallType(isolate());
|
| +
|
| + // Prepare the callee and the receiver to the function call. This depends on
|
| + // the semantics of the underlying call type.
|
| + CallFunctionFlags flags = NO_CALL_FUNCTION_FLAGS;
|
| + Node* receiver_value = NULL;
|
| + Node* callee_value = NULL;
|
| + bool possibly_eval = false;
|
| + switch (call_type) {
|
| + case Call::GLOBAL_CALL: {
|
| + Variable* variable = callee->AsVariableProxy()->var();
|
| + callee_value = BuildVariableLoad(variable);
|
| + receiver_value = jsgraph()->UndefinedConstant();
|
| + break;
|
| + }
|
| + case Call::LOOKUP_SLOT_CALL: {
|
| + Variable* variable = callee->AsVariableProxy()->var();
|
| + ASSERT(variable->location() == Variable::LOOKUP);
|
| + Node* name = jsgraph()->Constant(variable->name());
|
| + Operator* op = javascript()->Runtime(Runtime::kLoadLookupSlot, 2);
|
| + Node* pair = NewNode(op, current_context(), name);
|
| + callee_value = NewNode(common()->Projection(0), pair);
|
| + receiver_value = NewNode(common()->Projection(1), pair);
|
| + break;
|
| + }
|
| + case Call::PROPERTY_CALL: {
|
| + Property* property = callee->AsProperty();
|
| + VisitForValue(property->obj());
|
| + Node* object = environment()->Top();
|
| + if (property->key()->IsPropertyName()) {
|
| + PrintableUnique<Name> name =
|
| + MakeUnique(property->key()->AsLiteral()->AsPropertyName());
|
| + callee_value = NewNode(javascript()->LoadNamed(name), object);
|
| + } else {
|
| + VisitForValue(property->key());
|
| + Node* key = environment()->Pop();
|
| + callee_value = NewNode(javascript()->LoadProperty(), object, key);
|
| + }
|
| + receiver_value = environment()->Pop();
|
| + // Note that a PROPERTY_CALL requires the receiver to be wrapped into an
|
| + // object for sloppy callees. This could also be modeled explicitly here,
|
| + // thereby obsoleting the need for a flag to the call operator.
|
| + flags = CALL_AS_METHOD;
|
| + break;
|
| + }
|
| + case Call::POSSIBLY_EVAL_CALL:
|
| + possibly_eval = true;
|
| + // Fall through.
|
| + case Call::OTHER_CALL:
|
| + VisitForValue(callee);
|
| + callee_value = environment()->Pop();
|
| + receiver_value = jsgraph()->UndefinedConstant();
|
| + break;
|
| + }
|
| +
|
| + // The callee and the receiver both have to be pushed onto the operand stack
|
| + // before arguments are being evaluated.
|
| + environment()->Push(callee_value);
|
| + environment()->Push(receiver_value);
|
| +
|
| + // Evaluate all arguments to the function call,
|
| + ZoneList<Expression*>* args = expr->arguments();
|
| + VisitForValues(args);
|
| +
|
| + // Resolve callee and receiver for a potential direct eval call. This block
|
| + // will mutate the callee and receiver values pushed onto the environment.
|
| + if (possibly_eval && args->length() > 0) {
|
| + int arg_count = args->length();
|
| +
|
| + // Extract callee and source string from the environment.
|
| + Node* callee = environment()->Peek(arg_count + 1);
|
| + Node* source = environment()->Peek(arg_count - 1);
|
| +
|
| + // Create node to ask for help resolving potential eval call. This will
|
| + // provide a fully resolved callee and the corresponding receiver.
|
| + Node* receiver = environment()->Lookup(info()->scope()->receiver());
|
| + Node* strict = jsgraph()->Constant(strict_mode());
|
| + Node* position = jsgraph()->Constant(info()->scope()->start_position());
|
| + Operator* op =
|
| + javascript()->Runtime(Runtime::kResolvePossiblyDirectEval, 5);
|
| + Node* pair = NewNode(op, callee, source, receiver, strict, position);
|
| + Node* new_callee = NewNode(common()->Projection(0), pair);
|
| + Node* new_receiver = NewNode(common()->Projection(1), pair);
|
| +
|
| + // Patch callee and receiver on the environment.
|
| + environment()->Poke(arg_count + 1, new_callee);
|
| + environment()->Poke(arg_count + 0, new_receiver);
|
| + }
|
| +
|
| + // Create node to perform the function call.
|
| + Operator* call = javascript()->Call(args->length() + 2, flags);
|
| + Node* value = ProcessArguments(call, args->length() + 2);
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitCallNew(CallNew* expr) {
|
| + VisitForValue(expr->expression());
|
| +
|
| + // Evaluate all arguments to the construct call.
|
| + ZoneList<Expression*>* args = expr->arguments();
|
| + VisitForValues(args);
|
| +
|
| + // Create node to perform the construct call.
|
| + Operator* call = javascript()->CallNew(args->length() + 1);
|
| + Node* value = ProcessArguments(call, args->length() + 1);
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitCallJSRuntime(CallRuntime* expr) {
|
| + Handle<String> name = expr->name();
|
| +
|
| + // The callee and the receiver both have to be pushed onto the operand stack
|
| + // before arguments are being evaluated.
|
| + CallFunctionFlags flags = NO_CALL_FUNCTION_FLAGS;
|
| + Node* receiver_value = BuildLoadBuiltinsObject();
|
| + PrintableUnique<String> unique = MakeUnique(name);
|
| + Node* callee_value = NewNode(javascript()->LoadNamed(unique), receiver_value);
|
| + environment()->Push(callee_value);
|
| + environment()->Push(receiver_value);
|
| +
|
| + // Evaluate all arguments to the JS runtime call.
|
| + ZoneList<Expression*>* args = expr->arguments();
|
| + VisitForValues(args);
|
| +
|
| + // Create node to perform the JS runtime call.
|
| + Operator* call = javascript()->Call(args->length() + 2, flags);
|
| + Node* value = ProcessArguments(call, args->length() + 2);
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
|
| + const Runtime::Function* function = expr->function();
|
| +
|
| + // Handle calls to runtime functions implemented in JavaScript separately as
|
| + // the call follows JavaScript ABI and the callee is statically unknown.
|
| + if (expr->is_jsruntime()) {
|
| + ASSERT(function == NULL && expr->name()->length() > 0);
|
| + return VisitCallJSRuntime(expr);
|
| + }
|
| +
|
| + // Evaluate all arguments to the runtime call.
|
| + ZoneList<Expression*>* args = expr->arguments();
|
| + VisitForValues(args);
|
| +
|
| + // Create node to perform the runtime call.
|
| + Runtime::FunctionId functionId = function->function_id;
|
| + Operator* call = javascript()->Runtime(functionId, args->length());
|
| + Node* value = ProcessArguments(call, args->length());
|
| + ast_context()->ProduceValue(value);
|
| +
|
| + BuildLazyBailout(value, expr->id());
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
|
| + switch (expr->op()) {
|
| + case Token::DELETE:
|
| + return VisitDelete(expr);
|
| + case Token::VOID:
|
| + return VisitVoid(expr);
|
| + case Token::TYPEOF:
|
| + return VisitTypeof(expr);
|
| + case Token::NOT:
|
| + return VisitNot(expr);
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
| + ASSERT(expr->expression()->IsValidReferenceExpression());
|
| +
|
| + // Left-hand side can only be a property, a global or a variable slot.
|
| + Property* property = expr->expression()->AsProperty();
|
| + LhsKind assign_type = DetermineLhsKind(expr->expression());
|
| +
|
| + // Reserve space for result of postfix operation.
|
| + bool is_postfix = expr->is_postfix() && !ast_context()->IsEffect();
|
| + if (is_postfix) environment()->Push(jsgraph()->UndefinedConstant());
|
| +
|
| + // Evaluate LHS expression and get old value.
|
| + Node* old_value = NULL;
|
| + int stack_depth = -1;
|
| + switch (assign_type) {
|
| + case VARIABLE: {
|
| + Variable* variable = expr->expression()->AsVariableProxy()->var();
|
| + old_value = BuildVariableLoad(variable);
|
| + stack_depth = 0;
|
| + break;
|
| + }
|
| + case NAMED_PROPERTY: {
|
| + VisitForValue(property->obj());
|
| + Node* object = environment()->Top();
|
| + PrintableUnique<Name> name =
|
| + MakeUnique(property->key()->AsLiteral()->AsPropertyName());
|
| + old_value = NewNode(javascript()->LoadNamed(name), object);
|
| + stack_depth = 1;
|
| + break;
|
| + }
|
| + case KEYED_PROPERTY: {
|
| + VisitForValue(property->obj());
|
| + VisitForValue(property->key());
|
| + Node* key = environment()->Top();
|
| + Node* object = environment()->Peek(1);
|
| + old_value = NewNode(javascript()->LoadProperty(), object, key);
|
| + stack_depth = 2;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // Convert old value into a number.
|
| + old_value = NewNode(javascript()->ToNumber(), old_value);
|
| +
|
| + // Save result for postfix expressions at correct stack depth.
|
| + if (is_postfix) environment()->Poke(stack_depth, old_value);
|
| +
|
| + // Create node to perform +1/-1 operation.
|
| + Node* value =
|
| + BuildBinaryOp(old_value, jsgraph()->OneConstant(), expr->binary_op());
|
| +
|
| + // Store the value.
|
| + switch (assign_type) {
|
| + case VARIABLE: {
|
| + Variable* variable = expr->expression()->AsVariableProxy()->var();
|
| + BuildVariableAssignment(variable, value, expr->op());
|
| + break;
|
| + }
|
| + case NAMED_PROPERTY: {
|
| + Node* object = environment()->Pop();
|
| + PrintableUnique<Name> name =
|
| + MakeUnique(property->key()->AsLiteral()->AsPropertyName());
|
| + NewNode(javascript()->StoreNamed(name), object, value);
|
| + break;
|
| + }
|
| + case KEYED_PROPERTY: {
|
| + Node* key = environment()->Pop();
|
| + Node* object = environment()->Pop();
|
| + NewNode(javascript()->StoreProperty(), object, key, value);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // Restore old value for postfix expressions.
|
| + if (is_postfix) value = environment()->Pop();
|
| +
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
|
| + switch (expr->op()) {
|
| + case Token::COMMA:
|
| + return VisitComma(expr);
|
| + case Token::OR:
|
| + case Token::AND:
|
| + return VisitLogicalExpression(expr);
|
| + default: {
|
| + VisitForValue(expr->left());
|
| + VisitForValue(expr->right());
|
| + Node* right = environment()->Pop();
|
| + Node* left = environment()->Pop();
|
| + Node* value = BuildBinaryOp(left, right, expr->op());
|
| + ast_context()->ProduceValue(value);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| + Operator* op;
|
| + switch (expr->op()) {
|
| + case Token::EQ:
|
| + op = javascript()->Equal();
|
| + break;
|
| + case Token::NE:
|
| + op = javascript()->NotEqual();
|
| + break;
|
| + case Token::EQ_STRICT:
|
| + op = javascript()->StrictEqual();
|
| + break;
|
| + case Token::NE_STRICT:
|
| + op = javascript()->StrictNotEqual();
|
| + break;
|
| + case Token::LT:
|
| + op = javascript()->LessThan();
|
| + break;
|
| + case Token::GT:
|
| + op = javascript()->GreaterThan();
|
| + break;
|
| + case Token::LTE:
|
| + op = javascript()->LessThanOrEqual();
|
| + break;
|
| + case Token::GTE:
|
| + op = javascript()->GreaterThanOrEqual();
|
| + break;
|
| + case Token::INSTANCEOF:
|
| + op = javascript()->InstanceOf();
|
| + break;
|
| + case Token::IN:
|
| + op = javascript()->HasProperty();
|
| + break;
|
| + default:
|
| + op = NULL;
|
| + UNREACHABLE();
|
| + }
|
| + VisitForValue(expr->left());
|
| + VisitForValue(expr->right());
|
| + Node* right = environment()->Pop();
|
| + Node* left = environment()->Pop();
|
| + Node* value = NewNode(op, left, right);
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitThisFunction(ThisFunction* expr) {
|
| + Node* value = GetFunctionClosure();
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitCaseClause(CaseClause* expr) { UNREACHABLE(); }
|
| +
|
| +
|
| +void AstGraphBuilder::VisitDeclarations(ZoneList<Declaration*>* declarations) {
|
| + ASSERT(globals()->is_empty());
|
| + AstVisitor::VisitDeclarations(declarations);
|
| + if (globals()->is_empty()) return;
|
| + Handle<FixedArray> data =
|
| + isolate()->factory()->NewFixedArray(globals()->length(), TENURED);
|
| + for (int i = 0; i < globals()->length(); ++i) data->set(i, *globals()->at(i));
|
| + int encoded_flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) |
|
| + DeclareGlobalsNativeFlag::encode(info()->is_native()) |
|
| + DeclareGlobalsStrictMode::encode(info()->strict_mode());
|
| + Node* flags = jsgraph()->Constant(encoded_flags);
|
| + Node* pairs = jsgraph()->Constant(data);
|
| + Operator* op = javascript()->Runtime(Runtime::kDeclareGlobals, 3);
|
| + NewNode(op, current_context(), pairs, flags);
|
| + globals()->Rewind(0);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitIfNotNull(Statement* stmt) {
|
| + if (stmt == NULL) return;
|
| + Visit(stmt);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitIterationBody(IterationStatement* stmt,
|
| + LoopBuilder* loop, int drop_extra) {
|
| + BreakableScope scope(this, stmt, loop, drop_extra);
|
| + Visit(stmt->body());
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitDelete(UnaryOperation* expr) {
|
| + Node* value;
|
| + if (expr->expression()->IsVariableProxy()) {
|
| + // Delete of an unqualified identifier is only allowed in classic mode but
|
| + // deleting "this" is allowed in all language modes.
|
| + Variable* variable = expr->expression()->AsVariableProxy()->var();
|
| + ASSERT(strict_mode() == SLOPPY || variable->is_this());
|
| + value = BuildVariableDelete(variable);
|
| + } else if (expr->expression()->IsProperty()) {
|
| + Property* property = expr->expression()->AsProperty();
|
| + VisitForValue(property->obj());
|
| + VisitForValue(property->key());
|
| + Node* key = environment()->Pop();
|
| + Node* object = environment()->Pop();
|
| + value = NewNode(javascript()->DeleteProperty(strict_mode()), object, key);
|
| + } else {
|
| + VisitForEffect(expr->expression());
|
| + value = jsgraph()->TrueConstant();
|
| + }
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitVoid(UnaryOperation* expr) {
|
| + VisitForEffect(expr->expression());
|
| + Node* value = jsgraph()->UndefinedConstant();
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitTypeof(UnaryOperation* expr) {
|
| + Node* operand;
|
| + if (expr->expression()->IsVariableProxy()) {
|
| + // Typeof does not throw a reference error on global variables, hence we
|
| + // perform a non-contextual load in case the operand is a variable proxy.
|
| + Variable* variable = expr->expression()->AsVariableProxy()->var();
|
| + operand = BuildVariableLoad(variable, NOT_CONTEXTUAL);
|
| + } else {
|
| + VisitForValue(expr->expression());
|
| + operand = environment()->Pop();
|
| + }
|
| + Node* value = NewNode(javascript()->TypeOf(), operand);
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitNot(UnaryOperation* expr) {
|
| + VisitForValue(expr->expression());
|
| + Node* operand = environment()->Pop();
|
| + // TODO(mstarzinger): Possible optimization when we are in effect context.
|
| + Node* value = NewNode(javascript()->UnaryNot(), operand);
|
| + ast_context()->ProduceValue(value);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitComma(BinaryOperation* expr) {
|
| + VisitForEffect(expr->left());
|
| + Visit(expr->right());
|
| + ast_context()->ReplaceValue();
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
|
| + bool is_logical_and = expr->op() == Token::AND;
|
| + IfBuilder compare_if(this);
|
| + VisitForValue(expr->left());
|
| + Node* condition = environment()->Top();
|
| + compare_if.If(BuildToBoolean(condition));
|
| + compare_if.Then();
|
| + if (is_logical_and) {
|
| + environment()->Pop();
|
| + Visit(expr->right());
|
| + } else if (ast_context()->IsEffect()) {
|
| + environment()->Pop();
|
| + }
|
| + compare_if.Else();
|
| + if (!is_logical_and) {
|
| + environment()->Pop();
|
| + Visit(expr->right());
|
| + } else if (ast_context()->IsEffect()) {
|
| + environment()->Pop();
|
| + }
|
| + compare_if.End();
|
| + ast_context()->ReplaceValue();
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::ProcessArguments(Operator* op, int arity) {
|
| + ASSERT(environment()->stack_height() >= arity);
|
| + Node** all = info()->zone()->NewArray<Node*>(arity); // XXX: alloca?
|
| + for (int i = arity - 1; i >= 0; --i) {
|
| + all[i] = environment()->Pop();
|
| + }
|
| + Node* value = NewNode(op, arity, all);
|
| + return value;
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context, Node* closure) {
|
| + int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
|
| + if (heap_slots <= 0) return context;
|
| + set_current_context(context);
|
| +
|
| + // Allocate a new local context.
|
| + Operator* op = javascript()->CreateFunctionContext();
|
| + Node* local_context = NewNode(op, closure);
|
| + set_current_context(local_context);
|
| +
|
| + // Copy parameters into context if necessary.
|
| + int num_parameters = info()->scope()->num_parameters();
|
| + for (int i = 0; i < num_parameters; i++) {
|
| + Variable* variable = info()->scope()->parameter(i);
|
| + if (!variable->IsContextSlot()) continue;
|
| + // Temporary parameter node. The parameter indices are shifted by 1
|
| + // (receiver is parameter index -1 but environment index 0).
|
| + Node* parameter = NewNode(common()->Parameter(i + 1));
|
| + // Context variable (at bottom of the context chain).
|
| + ASSERT_EQ(0, info()->scope()->ContextChainLength(variable->scope()));
|
| + Operator* op = javascript()->StoreContext(0, variable->index());
|
| + NewNode(op, local_context, parameter);
|
| + }
|
| +
|
| + return local_context;
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildArgumentsObject(Variable* arguments) {
|
| + if (arguments == NULL) return NULL;
|
| +
|
| + // Allocate and initialize a new arguments object.
|
| + Node* callee = GetFunctionClosure();
|
| + Operator* op = javascript()->Runtime(Runtime::kNewArguments, 1);
|
| + Node* object = NewNode(op, callee);
|
| +
|
| + // Assign the object to the arguments variable.
|
| + ASSERT(arguments->IsContextSlot() || arguments->IsStackAllocated());
|
| + BuildVariableAssignment(arguments, object, Token::ASSIGN);
|
| +
|
| + return object;
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildHoleCheckSilent(Node* value, Node* for_hole,
|
| + Node* not_hole) {
|
| + IfBuilder hole_check(this);
|
| + Node* the_hole = jsgraph()->TheHoleConstant();
|
| + Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
|
| + hole_check.If(check);
|
| + hole_check.Then();
|
| + environment()->Push(for_hole);
|
| + hole_check.Else();
|
| + environment()->Push(not_hole);
|
| + hole_check.End();
|
| + return environment()->Pop();
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable,
|
| + Node* not_hole) {
|
| + IfBuilder hole_check(this);
|
| + Node* the_hole = jsgraph()->TheHoleConstant();
|
| + Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
|
| + hole_check.If(check);
|
| + hole_check.Then();
|
| + environment()->Push(BuildThrowReferenceError(variable));
|
| + hole_check.Else();
|
| + environment()->Push(not_hole);
|
| + hole_check.End();
|
| + return environment()->Pop();
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
|
| + ContextualMode contextual_mode) {
|
| + Node* the_hole = jsgraph()->TheHoleConstant();
|
| + VariableMode mode = variable->mode();
|
| + switch (variable->location()) {
|
| + case Variable::UNALLOCATED: {
|
| + // Global var, const, or let variable.
|
| + if (!info()->is_native()) {
|
| + // TODO(turbofan): This special case is needed only because we don't
|
| + // use LoadICs yet. Remove this once LoadNamed is lowered to an IC.
|
| + Node* name = jsgraph()->Constant(variable->name());
|
| + Runtime::FunctionId function_id =
|
| + (contextual_mode == CONTEXTUAL)
|
| + ? Runtime::kLoadLookupSlot
|
| + : Runtime::kLoadLookupSlotNoReferenceError;
|
| + Operator* op = javascript()->Runtime(function_id, 2);
|
| + Node* pair = NewNode(op, current_context(), name);
|
| + return NewNode(common()->Projection(0), pair);
|
| + }
|
| + Node* global = BuildLoadGlobalObject();
|
| + PrintableUnique<Name> name = MakeUnique(variable->name());
|
| + Operator* op = javascript()->LoadNamed(name);
|
| + return NewNode(op, global);
|
| + }
|
| + case Variable::PARAMETER:
|
| + case Variable::LOCAL: {
|
| + // Local var, const, or let variable.
|
| + Node* value = environment()->Lookup(variable);
|
| + if (mode == CONST_LEGACY) {
|
| + // Perform check for uninitialized legacy const variables.
|
| + if (value->op() == the_hole->op()) {
|
| + value = jsgraph()->UndefinedConstant();
|
| + } else if (value->opcode() == IrOpcode::kPhi) {
|
| + Node* undefined = jsgraph()->UndefinedConstant();
|
| + value = BuildHoleCheckSilent(value, undefined, value);
|
| + }
|
| + } else if (mode == LET || mode == CONST) {
|
| + // Perform check for uninitialized let/const variables.
|
| + if (value->op() == the_hole->op()) {
|
| + value = BuildThrowReferenceError(variable);
|
| + } else if (value->opcode() == IrOpcode::kPhi) {
|
| + value = BuildHoleCheckThrow(value, variable, value);
|
| + }
|
| + }
|
| + return value;
|
| + }
|
| + case Variable::CONTEXT: {
|
| + // Context variable (potentially up the context chain).
|
| + int depth = current_scope()->ContextChainLength(variable->scope());
|
| + bool immutable = variable->maybe_assigned() == kNotAssigned;
|
| + Operator* op =
|
| + javascript()->LoadContext(depth, variable->index(), immutable);
|
| + Node* value = NewNode(op, current_context());
|
| + // TODO(titzer): initialization checks are redundant for already
|
| + // initialized immutable context loads, but only specialization knows.
|
| + // Maybe specializer should be a parameter to the graph builder?
|
| + if (mode == CONST_LEGACY) {
|
| + // Perform check for uninitialized legacy const variables.
|
| + Node* undefined = jsgraph()->UndefinedConstant();
|
| + value = BuildHoleCheckSilent(value, undefined, value);
|
| + } else if (mode == LET || mode == CONST) {
|
| + // Perform check for uninitialized let/const variables.
|
| + value = BuildHoleCheckThrow(value, variable, value);
|
| + }
|
| + return value;
|
| + }
|
| + case Variable::LOOKUP: {
|
| + // Dynamic lookup of context variable (anywhere in the chain).
|
| + Node* name = jsgraph()->Constant(variable->name());
|
| + Runtime::FunctionId function_id =
|
| + (contextual_mode == CONTEXTUAL)
|
| + ? Runtime::kLoadLookupSlot
|
| + : Runtime::kLoadLookupSlotNoReferenceError;
|
| + Operator* op = javascript()->Runtime(function_id, 2);
|
| + Node* pair = NewNode(op, current_context(), name);
|
| + return NewNode(common()->Projection(0), pair);
|
| + }
|
| + }
|
| + UNREACHABLE();
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildVariableDelete(Variable* variable) {
|
| + switch (variable->location()) {
|
| + case Variable::UNALLOCATED: {
|
| + // Global var, const, or let variable.
|
| + Node* global = BuildLoadGlobalObject();
|
| + Node* name = jsgraph()->Constant(variable->name());
|
| + Operator* op = javascript()->DeleteProperty(strict_mode());
|
| + return NewNode(op, global, name);
|
| + }
|
| + case Variable::PARAMETER:
|
| + case Variable::LOCAL:
|
| + case Variable::CONTEXT:
|
| + // Local var, const, or let variable or context variable.
|
| + return variable->is_this() ? jsgraph()->TrueConstant()
|
| + : jsgraph()->FalseConstant();
|
| + case Variable::LOOKUP: {
|
| + // Dynamic lookup of context variable (anywhere in the chain).
|
| + Node* name = jsgraph()->Constant(variable->name());
|
| + Operator* op = javascript()->Runtime(Runtime::kDeleteLookupSlot, 2);
|
| + return NewNode(op, current_context(), name);
|
| + }
|
| + }
|
| + UNREACHABLE();
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildVariableAssignment(Variable* variable, Node* value,
|
| + Token::Value op) {
|
| + Node* the_hole = jsgraph()->TheHoleConstant();
|
| + VariableMode mode = variable->mode();
|
| + switch (variable->location()) {
|
| + case Variable::UNALLOCATED: {
|
| + // Global var, const, or let variable.
|
| + if (!info()->is_native()) {
|
| + // TODO(turbofan): This special case is needed only because we don't
|
| + // use StoreICs yet. Remove this once StoreNamed is lowered to an IC.
|
| + Node* name = jsgraph()->Constant(variable->name());
|
| + Node* strict = jsgraph()->Constant(strict_mode());
|
| + Operator* op = javascript()->Runtime(Runtime::kStoreLookupSlot, 4);
|
| + return NewNode(op, value, current_context(), name, strict);
|
| + }
|
| + Node* global = BuildLoadGlobalObject();
|
| + PrintableUnique<Name> name = MakeUnique(variable->name());
|
| + Operator* op = javascript()->StoreNamed(name);
|
| + return NewNode(op, global, value);
|
| + }
|
| + case Variable::PARAMETER:
|
| + case Variable::LOCAL:
|
| + // Local var, const, or let variable.
|
| + if (mode == CONST_LEGACY && op == Token::INIT_CONST_LEGACY) {
|
| + // Perform an initialization check for legacy const variables.
|
| + Node* current = environment()->Lookup(variable);
|
| + if (current->op() != the_hole->op()) {
|
| + value = BuildHoleCheckSilent(current, value, current);
|
| + }
|
| + } else if (mode == CONST_LEGACY && op != Token::INIT_CONST_LEGACY) {
|
| + // Non-initializing assignments to legacy const is ignored.
|
| + return value;
|
| + } else if (mode == LET && op != Token::INIT_LET) {
|
| + // Perform an initialization check for let declared variables.
|
| + // Also note that the dynamic hole-check is only done to ensure that
|
| + // this does not break in the presence of do-expressions within the
|
| + // temporal dead zone of a let declared variable.
|
| + Node* current = environment()->Lookup(variable);
|
| + if (current->op() == the_hole->op()) {
|
| + value = BuildThrowReferenceError(variable);
|
| + } else if (value->opcode() == IrOpcode::kPhi) {
|
| + value = BuildHoleCheckThrow(current, variable, value);
|
| + }
|
| + } else if (mode == CONST && op != Token::INIT_CONST) {
|
| + // All assignments to const variables are early errors.
|
| + UNREACHABLE();
|
| + }
|
| + environment()->Bind(variable, value);
|
| + return value;
|
| + case Variable::CONTEXT: {
|
| + // Context variable (potentially up the context chain).
|
| + int depth = current_scope()->ContextChainLength(variable->scope());
|
| + if (mode == CONST_LEGACY && op == Token::INIT_CONST_LEGACY) {
|
| + // Perform an initialization check for legacy const variables.
|
| + Operator* op =
|
| + javascript()->LoadContext(depth, variable->index(), false);
|
| + Node* current = NewNode(op, current_context());
|
| + value = BuildHoleCheckSilent(current, value, current);
|
| + } else if (mode == CONST_LEGACY && op != Token::INIT_CONST_LEGACY) {
|
| + // Non-initializing assignments to legacy const is ignored.
|
| + return value;
|
| + } else if (mode == LET && op != Token::INIT_LET) {
|
| + // Perform an initialization check for let declared variables.
|
| + Operator* op =
|
| + javascript()->LoadContext(depth, variable->index(), false);
|
| + Node* current = NewNode(op, current_context());
|
| + value = BuildHoleCheckThrow(current, variable, value);
|
| + } else if (mode == CONST && op != Token::INIT_CONST) {
|
| + // All assignments to const variables are early errors.
|
| + UNREACHABLE();
|
| + }
|
| + Operator* op = javascript()->StoreContext(depth, variable->index());
|
| + return NewNode(op, current_context(), value);
|
| + }
|
| + case Variable::LOOKUP: {
|
| + // Dynamic lookup of context variable (anywhere in the chain).
|
| + Node* name = jsgraph()->Constant(variable->name());
|
| + Node* strict = jsgraph()->Constant(strict_mode());
|
| + // TODO(mstarzinger): Use Runtime::kInitializeLegacyConstLookupSlot for
|
| + // initializations of const declarations.
|
| + Operator* op = javascript()->Runtime(Runtime::kStoreLookupSlot, 4);
|
| + return NewNode(op, value, current_context(), name, strict);
|
| + }
|
| + }
|
| + UNREACHABLE();
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildLoadBuiltinsObject() {
|
| + // TODO(mstarzinger): Better load from function context, otherwise optimized
|
| + // code cannot be shared across native contexts.
|
| + return jsgraph()->Constant(handle(info()->context()->builtins()));
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildLoadGlobalObject() {
|
| +#if 0
|
| + Node* context = GetFunctionContext();
|
| + // TODO(mstarzinger): Use mid-level operator on FixedArray instead of the
|
| + // JS-level operator that targets JSObject.
|
| + Node* index = jsgraph()->Constant(Context::GLOBAL_OBJECT_INDEX);
|
| + return NewNode(javascript()->LoadProperty(), context, index);
|
| +#else
|
| + // TODO(mstarzinger): Better load from function context, otherwise optimized
|
| + // code cannot be shared across native contexts. See unused code above.
|
| + return jsgraph()->Constant(handle(info()->context()->global_object()));
|
| +#endif
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildToBoolean(Node* value) {
|
| + // TODO(mstarzinger): Possible optimization is to NOP for boolean values.
|
| + return NewNode(javascript()->ToBoolean(), value);
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildThrowReferenceError(Variable* variable) {
|
| + // TODO(mstarzinger): Should be unified with the VisitThrow implementation.
|
| + Node* variable_name = jsgraph()->Constant(variable->name());
|
| + Operator* op = javascript()->Runtime(Runtime::kThrowReferenceError, 1);
|
| + return NewNode(op, variable_name);
|
| +}
|
| +
|
| +
|
| +Node* AstGraphBuilder::BuildBinaryOp(Node* left, Node* right, Token::Value op) {
|
| + Operator* js_op;
|
| + switch (op) {
|
| + case Token::BIT_OR:
|
| + js_op = javascript()->BitwiseOr();
|
| + break;
|
| + case Token::BIT_AND:
|
| + js_op = javascript()->BitwiseAnd();
|
| + break;
|
| + case Token::BIT_XOR:
|
| + js_op = javascript()->BitwiseXor();
|
| + break;
|
| + case Token::SHL:
|
| + js_op = javascript()->ShiftLeft();
|
| + break;
|
| + case Token::SAR:
|
| + js_op = javascript()->ShiftRight();
|
| + break;
|
| + case Token::SHR:
|
| + js_op = javascript()->ShiftRightLogical();
|
| + break;
|
| + case Token::ADD:
|
| + js_op = javascript()->Add();
|
| + break;
|
| + case Token::SUB:
|
| + js_op = javascript()->Subtract();
|
| + break;
|
| + case Token::MUL:
|
| + js_op = javascript()->Multiply();
|
| + break;
|
| + case Token::DIV:
|
| + js_op = javascript()->Divide();
|
| + break;
|
| + case Token::MOD:
|
| + js_op = javascript()->Modulus();
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + js_op = NULL;
|
| + }
|
| + return NewNode(js_op, left, right);
|
| +}
|
| +
|
| +
|
| +void AstGraphBuilder::BuildLazyBailout(Node* node, BailoutId ast_id) {
|
| + if (OperatorProperties::CanLazilyDeoptimize(node->op())) {
|
| + // The deopting node should have an outgoing control dependency.
|
| + ASSERT(GetControlDependency() == node);
|
| +
|
| + StructuredGraphBuilder::Environment* continuation_env =
|
| + environment_internal();
|
| + // Create environment for the deoptimization block, and build the block.
|
| + StructuredGraphBuilder::Environment* deopt_env =
|
| + CopyEnvironment(continuation_env);
|
| + set_environment(deopt_env);
|
| +
|
| + NewNode(common()->LazyDeoptimization());
|
| +
|
| + FrameStateDescriptor stateDescriptor(ast_id);
|
| + Node* state_node = NewNode(common()->FrameState(stateDescriptor));
|
| +
|
| + Node* deoptimize_node = NewNode(common()->Deoptimize(), state_node);
|
| +
|
| + UpdateControlDependencyToLeaveFunction(deoptimize_node);
|
| +
|
| + // Continue with the original environment.
|
| + set_environment(continuation_env);
|
| +
|
| + NewNode(common()->Continuation());
|
| + }
|
| +}
|
| +}
|
| +}
|
| +} // namespace v8::internal::compiler
|
|
|