Index: src/interpreter/bytecode-generator.cc |
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc |
index a4efcce1da5666fd827a87ad739051af6deeef5e..713fa4401d0e82326d1fb6203d84444fdb6c9383 100644 |
--- a/src/interpreter/bytecode-generator.cc |
+++ b/src/interpreter/bytecode-generator.cc |
@@ -23,39 +23,54 @@ namespace interpreter { |
// popping of the current {context_register} during visitation. |
class BytecodeGenerator::ContextScope BASE_EMBEDDED { |
public: |
- explicit ContextScope(BytecodeGenerator* generator, |
- bool is_function_context = false) |
+ ContextScope(BytecodeGenerator* generator, Scope* scope, |
+ bool should_pop_context = true) |
: generator_(generator), |
- outer_(generator_->current_context()), |
- is_function_context_(is_function_context) { |
- DCHECK(!is_function_context || |
- outer_.index() == Register::function_context().index()); |
- Register new_context_reg = NewContextRegister(); |
- generator_->builder()->PushContext(new_context_reg); |
- generator_->set_current_context(new_context_reg); |
+ scope_(scope), |
+ outer_(generator_->execution_context()), |
+ register_(generator_->NextContextRegister()), |
+ depth_(0), |
+ should_pop_context_(should_pop_context) { |
+ if (outer_) { |
+ depth_ = outer_->depth_ + 1; |
+ generator_->builder()->PushContext(register_); |
+ } |
+ generator_->set_execution_context(this); |
} |
~ContextScope() { |
- if (!is_function_context_) { |
- generator_->builder()->PopContext(outer_); |
+ if (outer_ && should_pop_context_) { |
+ generator_->builder()->PopContext(outer_->reg()); |
} |
- generator_->set_current_context(outer_); |
+ generator_->set_execution_context(outer_); |
} |
- private: |
- Register NewContextRegister() const { |
- if (outer_.index() == Register::function_context().index()) { |
- return generator_->builder()->first_context_register(); |
- } else { |
- DCHECK_LT(outer_.index(), |
- generator_->builder()->last_context_register().index()); |
- return Register(outer_.index() + 1); |
+ // Returns the execution context for the given |scope| if it is a function |
+ // local execution context, otherwise returns nullptr. |
+ ContextScope* Previous(Scope* scope) { |
+ int depth = scope_->ContextChainLength(scope); |
+ if (depth > depth_) { |
+ return nullptr; |
+ } |
+ |
+ ContextScope* previous = this; |
+ for (int i = depth; i > 0; --i) { |
+ previous = previous->outer_; |
} |
+ DCHECK_EQ(previous->scope_, scope); |
+ return previous; |
} |
+ Scope* scope() const { return scope_; } |
+ Register reg() const { return register_; } |
+ |
+ private: |
BytecodeGenerator* generator_; |
- Register outer_; |
- bool is_function_context_; |
+ Scope* scope_; |
+ ContextScope* outer_; |
+ Register register_; |
+ int depth_; |
+ bool should_pop_context_; |
}; |
@@ -64,10 +79,10 @@ class BytecodeGenerator::ContextScope BASE_EMBEDDED { |
class BytecodeGenerator::ControlScope BASE_EMBEDDED { |
public: |
explicit ControlScope(BytecodeGenerator* generator) |
- : generator_(generator), outer_(generator->control_scope()) { |
- generator_->set_control_scope(this); |
+ : generator_(generator), outer_(generator->execution_control()) { |
+ generator_->set_execution_control(this); |
} |
- virtual ~ControlScope() { generator_->set_control_scope(outer()); } |
+ virtual ~ControlScope() { generator_->set_execution_control(outer()); } |
void Break(Statement* stmt) { PerformCommand(CMD_BREAK, stmt); } |
void Continue(Statement* stmt) { PerformCommand(CMD_CONTINUE, stmt); } |
@@ -138,8 +153,8 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) |
info_(nullptr), |
scope_(nullptr), |
globals_(0, zone), |
- control_scope_(nullptr), |
- current_context_(Register::function_context()) { |
+ execution_control_(nullptr), |
+ execution_context_(nullptr) { |
InitializeAstVisitor(isolate); |
} |
@@ -151,6 +166,9 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { |
set_info(info); |
set_scope(info->scope()); |
+ // Initialize the incoming context. |
+ ContextScope incoming_context(this, scope(), false); |
+ |
builder()->set_parameter_count(info->num_parameters_including_this()); |
builder()->set_locals_count(scope()->num_stack_slots()); |
builder()->set_context_count(scope()->MaxNestedContextChainLength()); |
@@ -159,7 +177,8 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { |
if (scope()->NeedsContext()) { |
// Push a new inner context scope for the function. |
VisitNewLocalFunctionContext(); |
- ContextScope top_context(this, true); |
+ ContextScope local_function_context(this, scope(), false); |
+ VisitBuildLocalActivationContext(); |
MakeBytecodeBody(); |
} else { |
MakeBytecodeBody(); |
@@ -180,18 +199,21 @@ void BytecodeGenerator::MakeBytecodeBody() { |
} |
-void BytecodeGenerator::VisitBlock(Block* node) { |
+void BytecodeGenerator::VisitBlock(Block* stmt) { |
builder()->EnterBlock(); |
- if (node->scope() == NULL) { |
+ if (stmt->scope() == NULL) { |
// Visit statements in the same scope, no declarations. |
- VisitStatements(node->statements()); |
+ VisitStatements(stmt->statements()); |
} else { |
// Visit declarations and statements in a block scope. |
- if (node->scope()->ContextLocalCount() > 0) { |
- UNIMPLEMENTED(); |
+ if (stmt->scope()->NeedsContext()) { |
+ VisitNewLocalBlockContext(stmt->scope()); |
+ ContextScope scope(this, stmt->scope()); |
+ VisitDeclarations(stmt->scope()->declarations()); |
+ VisitStatements(stmt->statements()); |
} else { |
- VisitDeclarations(node->scope()->declarations()); |
- VisitStatements(node->statements()); |
+ VisitDeclarations(stmt->scope()->declarations()); |
+ VisitStatements(stmt->statements()); |
} |
} |
builder()->LeaveBlock(); |
@@ -201,10 +223,9 @@ void BytecodeGenerator::VisitBlock(Block* node) { |
void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) { |
Variable* variable = decl->proxy()->var(); |
VariableMode mode = decl->mode(); |
+ // Const and let variables are initialized with the hole so that we can |
+ // check that they are only assigned once. |
bool hole_init = mode == CONST || mode == CONST_LEGACY || mode == LET; |
- if (hole_init) { |
- UNIMPLEMENTED(); |
- } |
switch (variable->location()) { |
case VariableLocation::GLOBAL: |
case VariableLocation::UNALLOCATED: { |
@@ -215,11 +236,26 @@ void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) { |
globals()->push_back(value); |
break; |
} |
- case VariableLocation::PARAMETER: |
case VariableLocation::LOCAL: |
- // Details stored in scope, i.e. variable index. |
+ if (hole_init) { |
+ Register destination(variable->index()); |
+ builder()->LoadTheHole().StoreAccumulatorInRegister(destination); |
+ } |
+ break; |
+ case VariableLocation::PARAMETER: |
+ if (hole_init) { |
+ // The parameter indices are shifted by 1 (receiver is variable |
+ // index -1 but is parameter index 0 in BytecodeArrayBuilder). |
+ Register destination(builder()->Parameter(variable->index() + 1)); |
+ builder()->LoadTheHole().StoreAccumulatorInRegister(destination); |
+ } |
break; |
case VariableLocation::CONTEXT: |
+ if (hole_init) { |
+ builder()->LoadTheHole().StoreContextSlot(execution_context()->reg(), |
+ variable->index()); |
+ } |
+ break; |
case VariableLocation::LOOKUP: |
UNIMPLEMENTED(); |
break; |
@@ -326,12 +362,12 @@ void BytecodeGenerator::VisitSloppyBlockFunctionStatement( |
void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { |
- control_scope()->Continue(stmt->target()); |
+ execution_control()->Continue(stmt->target()); |
} |
void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) { |
- control_scope()->Break(stmt->target()); |
+ execution_control()->Break(stmt->target()); |
} |
@@ -356,7 +392,7 @@ void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { UNIMPLEMENTED(); } |
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { |
LoopBuilder loop_builder(builder()); |
- ControlScopeForIteration control_scope(this, stmt, &loop_builder); |
+ ControlScopeForIteration execution_control(this, stmt, &loop_builder); |
BytecodeLabel body_label, condition_label, done_label; |
builder()->Bind(&body_label); |
@@ -373,7 +409,7 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { |
void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { |
LoopBuilder loop_builder(builder()); |
- ControlScopeForIteration control_scope(this, stmt, &loop_builder); |
+ ControlScopeForIteration execution_control(this, stmt, &loop_builder); |
BytecodeLabel body_label, condition_label, done_label; |
builder()->Jump(&condition_label); |
@@ -391,7 +427,7 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { |
void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { |
LoopBuilder loop_builder(builder()); |
- ControlScopeForIteration control_scope(this, stmt, &loop_builder); |
+ ControlScopeForIteration execution_control(this, stmt, &loop_builder); |
if (stmt->init() != nullptr) { |
Visit(stmt->init()); |
@@ -595,8 +631,8 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { |
} |
} |
- // Create nodes to define accessors, using only a single call to the runtime |
- // for each pair of corresponding getters and setters. |
+ // 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) { |
TemporaryRegisterScope inner_temporary_register_scope(builder()); |
@@ -698,8 +734,8 @@ void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { |
TemporaryRegisterScope temporary_register_scope(builder()); |
Register index, literal; |
- // Create nodes to evaluate all the non-constant subexpressions and to store |
- // them into the newly cloned array. |
+ // Evaluate all the non-constant subexpressions and store them into the |
+ // newly cloned array. |
bool literal_in_accumulator = true; |
for (int array_index = 0; array_index < expr->values()->length(); |
array_index++) { |
@@ -744,6 +780,8 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, |
case VariableLocation::LOCAL: { |
Register source(variable->index()); |
builder()->LoadAccumulatorWithRegister(source); |
+ // TODO(rmcilroy): Perform check for uninitialized legacy const, const and |
+ // let variables. |
break; |
} |
case VariableLocation::PARAMETER: { |
@@ -765,14 +803,24 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, |
case VariableLocation::UNALLOCATED: { |
TemporaryRegisterScope temporary_register_scope(builder()); |
Register obj = temporary_register_scope.NewRegister(); |
- builder()->LoadContextSlot(current_context(), |
+ builder()->LoadContextSlot(execution_context()->reg(), |
Context::GLOBAL_OBJECT_INDEX); |
builder()->StoreAccumulatorInRegister(obj); |
builder()->LoadLiteral(variable->name()); |
builder()->LoadNamedProperty(obj, feedback_index(slot), language_mode()); |
break; |
} |
- case VariableLocation::CONTEXT: |
+ case VariableLocation::CONTEXT: { |
+ ContextScope* context = execution_context()->Previous(variable->scope()); |
+ if (context) { |
+ builder()->LoadContextSlot(context->reg(), variable->index()); |
+ } else { |
+ UNIMPLEMENTED(); |
+ } |
+ // TODO(rmcilroy): Perform check for uninitialized legacy const, const and |
+ // let variables. |
+ break; |
+ } |
case VariableLocation::LOOKUP: |
UNIMPLEMENTED(); |
} |
@@ -812,7 +860,7 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable, |
// TODO(rmcilroy): Investigate whether we can avoid having to stash the |
// value in a register. |
builder()->StoreAccumulatorInRegister(value); |
- builder()->LoadContextSlot(current_context(), |
+ builder()->LoadContextSlot(execution_context()->reg(), |
Context::GLOBAL_OBJECT_INDEX); |
builder()->StoreAccumulatorInRegister(obj); |
builder()->LoadLiteral(variable->name()); |
@@ -822,7 +870,16 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable, |
language_mode()); |
break; |
} |
- case VariableLocation::CONTEXT: |
+ case VariableLocation::CONTEXT: { |
+ // TODO(rmcilroy): support const mode initialization. |
+ ContextScope* context = execution_context()->Previous(variable->scope()); |
+ if (context) { |
+ builder()->StoreContextSlot(context->reg(), variable->index()); |
+ } else { |
+ UNIMPLEMENTED(); |
+ } |
+ break; |
+ } |
case VariableLocation::LOOKUP: |
UNIMPLEMENTED(); |
} |
@@ -1141,6 +1198,11 @@ void BytecodeGenerator::VisitNewLocalFunctionContext() { |
builder()->CallRuntime(Runtime::kNewFunctionContext, |
Register::function_closure(), 1); |
} |
+} |
+ |
+ |
+void BytecodeGenerator::VisitBuildLocalActivationContext() { |
+ Scope* scope = this->scope(); |
if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) { |
UNIMPLEMENTED(); |
@@ -1150,13 +1212,37 @@ void BytecodeGenerator::VisitNewLocalFunctionContext() { |
int num_parameters = scope->num_parameters(); |
for (int i = 0; i < num_parameters; i++) { |
Variable* variable = scope->parameter(i); |
- if (variable->IsContextSlot()) { |
- UNIMPLEMENTED(); |
- } |
+ if (!variable->IsContextSlot()) continue; |
+ |
+ // The parameter indices are shifted by 1 (receiver is variable |
+ // index -1 but is parameter index 0 in BytecodeArrayBuilder). |
+ Register parameter(builder()->Parameter(i + 1)); |
+ // Context variable (at bottom of the context chain). |
+ DCHECK_EQ(0, scope->ContextChainLength(variable->scope())); |
+ builder()->LoadAccumulatorWithRegister(parameter) |
+ .StoreContextSlot(execution_context()->reg(), variable->index()); |
} |
} |
+void BytecodeGenerator::VisitNewLocalBlockContext(Scope* scope) { |
+ DCHECK(scope->is_block_scope()); |
+ |
+ // Allocate a new local block context. |
+ TemporaryRegisterScope temporary_register_scope(builder()); |
+ Register scope_info = temporary_register_scope.NewRegister(); |
+ Register closure = temporary_register_scope.NewRegister(); |
+ DCHECK(Register::AreContiguous(scope_info, closure)); |
+ builder() |
+ ->LoadLiteral(scope->GetScopeInfo(isolate())) |
+ .StoreAccumulatorInRegister(scope_info); |
+ VisitFunctionClosureForContext(); |
+ builder() |
+ ->StoreAccumulatorInRegister(closure) |
+ .CallRuntime(Runtime::kPushBlockContext, scope_info, 2); |
+} |
+ |
+ |
void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* binop) { |
Token::Value op = binop->op(); |
Expression* left = binop->left(); |
@@ -1172,41 +1258,6 @@ void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* binop) { |
} |
-void BytecodeGenerator::VisitObjectLiteralAccessor( |
- Register home_object, ObjectLiteralProperty* property, Register value_out) { |
- // TODO(rmcilroy): Replace value_out with VisitForRegister(); |
- if (property == nullptr) { |
- builder()->LoadNull().StoreAccumulatorInRegister(value_out); |
- } else { |
- Visit(property->value()); |
- builder()->StoreAccumulatorInRegister(value_out); |
- VisitSetHomeObject(value_out, home_object, property); |
- } |
-} |
- |
- |
-void BytecodeGenerator::VisitSetHomeObject(Register value, Register home_object, |
- ObjectLiteralProperty* property, |
- int slot_number) { |
- Expression* expr = property->value(); |
- if (!FunctionLiteral::NeedsHomeObject(expr)) return; |
- |
- // TODO(rmcilroy): Remove UNIMPLEMENTED once we have tests for setting the |
- // home object. |
- UNIMPLEMENTED(); |
- |
- TemporaryRegisterScope temporary_register_scope(builder()); |
- Register name = temporary_register_scope.NewRegister(); |
- isolate()->factory()->home_object_symbol(); |
- builder() |
- ->LoadLiteral(isolate()->factory()->home_object_symbol()) |
- .StoreAccumulatorInRegister(name) |
- .StoreNamedProperty(home_object, name, |
- feedback_index(property->GetSlot(slot_number)), |
- language_mode()); |
-} |
- |
- |
void BytecodeGenerator::VisitCommaExpression(BinaryOperation* binop) { |
Expression* left = binop->left(); |
Expression* right = binop->right(); |
@@ -1254,6 +1305,74 @@ void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) { |
} |
+void BytecodeGenerator::VisitObjectLiteralAccessor( |
+ Register home_object, ObjectLiteralProperty* property, Register value_out) { |
+ // TODO(rmcilroy): Replace value_out with VisitForRegister(); |
+ if (property == nullptr) { |
+ builder()->LoadNull().StoreAccumulatorInRegister(value_out); |
+ } else { |
+ Visit(property->value()); |
+ builder()->StoreAccumulatorInRegister(value_out); |
+ VisitSetHomeObject(value_out, home_object, property); |
+ } |
+} |
+ |
+ |
+void BytecodeGenerator::VisitSetHomeObject(Register value, Register home_object, |
+ ObjectLiteralProperty* property, |
+ int slot_number) { |
+ Expression* expr = property->value(); |
+ if (!FunctionLiteral::NeedsHomeObject(expr)) return; |
+ |
+ // TODO(rmcilroy): Remove UNIMPLEMENTED once we have tests for setting the |
+ // home object. |
+ UNIMPLEMENTED(); |
+ |
+ TemporaryRegisterScope temporary_register_scope(builder()); |
+ Register name = temporary_register_scope.NewRegister(); |
+ isolate()->factory()->home_object_symbol(); |
+ builder() |
+ ->LoadLiteral(isolate()->factory()->home_object_symbol()) |
+ .StoreAccumulatorInRegister(name) |
+ .StoreNamedProperty(home_object, name, |
+ feedback_index(property->GetSlot(slot_number)), |
+ language_mode()); |
+} |
+ |
+ |
+void BytecodeGenerator::VisitFunctionClosureForContext() { |
+ Scope* closure_scope = execution_context()->scope()->ClosureScope(); |
+ if (closure_scope->is_script_scope() || |
+ closure_scope->is_module_scope()) { |
+ // Contexts nested in the native context have a canonical empty function as |
+ // their closure, not the anonymous closure containing the global code. |
+ // Pass a SMI sentinel and let the runtime look up the empty function. |
+ builder()->LoadLiteral(Smi::FromInt(0)); |
+ } else { |
+ DCHECK(closure_scope->is_function_scope()); |
+ builder()->LoadAccumulatorWithRegister(Register::function_closure()); |
+ } |
+} |
+ |
+ |
+Register BytecodeGenerator::NextContextRegister() const { |
+ if (execution_context() == nullptr) { |
+ // Return the incoming function context for the outermost execution context. |
+ return Register::function_context(); |
+ } |
+ Register previous = execution_context()->reg(); |
+ if (previous == Register::function_context()) { |
+ // If the previous context was the incoming function context, then the next |
+ // context register is the first local context register. |
+ return builder_.first_context_register(); |
+ } else { |
+ // Otherwise use the next local context register. |
+ DCHECK_LT(previous.index(), builder_.last_context_register().index()); |
+ return Register(previous.index() + 1); |
+ } |
+} |
+ |
+ |
LanguageMode BytecodeGenerator::language_mode() const { |
return info()->language_mode(); |
} |