| Index: src/interpreter/bytecode-generator.cc
|
| diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
|
| index 6a23812ae906566308e34373a9f4d98d59ef906c..015bed30ac144da2267cc3dee8cd7df59c9c94be 100644
|
| --- a/src/interpreter/bytecode-generator.cc
|
| +++ b/src/interpreter/bytecode-generator.cc
|
| @@ -721,7 +721,10 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
|
| case VariableLocation::PARAMETER:
|
| case VariableLocation::LOCAL: {
|
| VisitForAccumulatorValue(decl->fun());
|
| - VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
|
| + DCHECK(variable->mode() == LET || variable->mode() == VAR ||
|
| + variable->mode() == CONST);
|
| + VisitVariableAssignment(variable, Token::INIT,
|
| + FeedbackVectorSlot::Invalid());
|
| break;
|
| }
|
| case VariableLocation::CONTEXT: {
|
| @@ -1007,7 +1010,7 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr,
|
| switch (assign_type) {
|
| case VARIABLE: {
|
| Variable* variable = expr->AsVariableProxy()->var();
|
| - VisitVariableAssignment(variable, slot);
|
| + VisitVariableAssignment(variable, Token::ASSIGN, slot);
|
| break;
|
| }
|
| case NAMED_PROPERTY: {
|
| @@ -1260,7 +1263,7 @@ void BytecodeGenerator::VisitClassLiteralContents(ClassLiteral* expr) {
|
| FeedbackVectorSlot slot = expr->NeedsProxySlot()
|
| ? expr->ProxySlot()
|
| : FeedbackVectorSlot::Invalid();
|
| - VisitVariableAssignment(var, slot);
|
| + VisitVariableAssignment(var, Token::INIT, slot);
|
| }
|
| execution_result()->SetResultInAccumulator();
|
| }
|
| @@ -1674,14 +1677,27 @@ void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
|
| VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot());
|
| }
|
|
|
| +void BytecodeGenerator::BuildHoleCheckForVariableLoad(VariableMode mode,
|
| + Handle<String> name) {
|
| + if (mode == CONST_LEGACY) {
|
| + BytecodeLabel end_label;
|
| + builder()->JumpIfNotHole(&end_label);
|
| + builder()->LoadUndefined();
|
| + builder()->Bind(&end_label);
|
| + } else if (mode == LET || mode == CONST) {
|
| + BuildThrowIfHole(name);
|
| + }
|
| +}
|
|
|
| void BytecodeGenerator::VisitVariableLoad(Variable* variable,
|
| FeedbackVectorSlot slot,
|
| TypeofMode typeof_mode) {
|
| + VariableMode mode = variable->mode();
|
| switch (variable->location()) {
|
| case VariableLocation::LOCAL: {
|
| Register source(Register(variable->index()));
|
| builder()->LoadAccumulatorWithRegister(source);
|
| + BuildHoleCheckForVariableLoad(mode, variable->name());
|
| execution_result()->SetResultInAccumulator();
|
| break;
|
| }
|
| @@ -1690,6 +1706,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
|
| // index -1 but is parameter index 0 in BytecodeArrayBuilder).
|
| Register source = builder()->Parameter(variable->index() + 1);
|
| builder()->LoadAccumulatorWithRegister(source);
|
| + BuildHoleCheckForVariableLoad(mode, variable->name());
|
| execution_result()->SetResultInAccumulator();
|
| break;
|
| }
|
| @@ -1722,10 +1739,10 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
|
| .StoreAccumulatorInRegister(context_reg);
|
| }
|
| }
|
| +
|
| builder()->LoadContextSlot(context_reg, variable->index());
|
| + BuildHoleCheckForVariableLoad(mode, variable->name());
|
| execution_result()->SetResultInAccumulator();
|
| - // TODO(rmcilroy): Perform check for uninitialized legacy const, const and
|
| - // let variables.
|
| break;
|
| }
|
| case VariableLocation::LOOKUP: {
|
| @@ -1751,20 +1768,115 @@ Register BytecodeGenerator::VisitVariableLoadForRegisterValue(
|
| return register_scope.ResultRegister();
|
| }
|
|
|
| +void BytecodeGenerator::BuildThrowIfHole(Handle<String> name) {
|
| + Register name_reg = register_allocator()->NewRegister();
|
| + BytecodeLabel end_label;
|
| + // TODO(mythria): This will be replaced by a new bytecode that throws an
|
| + // error if the value is the hole.
|
| + builder()
|
| + ->JumpIfNotHole(&end_label)
|
| + .LoadLiteral(name)
|
| + .StoreAccumulatorInRegister(name_reg)
|
| + .CallRuntime(Runtime::kThrowReferenceError, name_reg, 1)
|
| + .Bind(&end_label);
|
| +}
|
| +
|
| +void BytecodeGenerator::BuildThrowIfNotHole(Handle<String> name) {
|
| + Register name_reg = register_allocator()->NewRegister();
|
| + BytecodeLabel end_label;
|
| + // TODO(mythria): This will be replaced by a new bytecode that throws an
|
| + // error if the value is not the hole.
|
| + builder()
|
| + ->JumpIfHole(&end_label)
|
| + .LoadLiteral(name)
|
| + .StoreAccumulatorInRegister(name_reg)
|
| + .CallRuntime(Runtime::kThrowReferenceError, name_reg, 1)
|
| + .Bind(&end_label);
|
| +}
|
| +
|
| +void BytecodeGenerator::BuildThrowReassignConstant(Handle<String> name) {
|
| + Register name_reg = register_allocator()->NewRegister();
|
| + BytecodeLabel else_label;
|
| + // TODO(mythria): This will be replaced by a new bytecode that throws an
|
| + // appropriate error depending on the whether the value is a hole or not.
|
| + builder()
|
| + ->JumpIfNotHole(&else_label)
|
| + .LoadLiteral(name)
|
| + .StoreAccumulatorInRegister(name_reg)
|
| + .CallRuntime(Runtime::kThrowReferenceError, name_reg, 1)
|
| + .Bind(&else_label)
|
| + .CallRuntime(Runtime::kThrowConstAssignError, Register(), 0);
|
| +}
|
| +
|
| +void BytecodeGenerator::BuildHoleCheckForVariableAssignment(Variable* variable,
|
| + Token::Value op) {
|
| + VariableMode mode = variable->mode();
|
| + DCHECK(mode != CONST_LEGACY);
|
| + if (mode == CONST && op != Token::INIT) {
|
| + // Non-intializing assignments to constant is not allowed.
|
| + BuildThrowReassignConstant(variable->name());
|
| + } else if (mode == LET && op != Token::INIT) {
|
| + // Perform an initialization check for let declared variables.
|
| + // E.g. let x = (x = 20); is not allowed.
|
| + BuildThrowIfHole(variable->name());
|
| + } else {
|
| + DCHECK(variable->is_this() && mode == CONST && op == Token::INIT);
|
| + // Perform an initialization check for 'this'. 'this' variable is the
|
| + // only variable able to trigger bind operations outside the TDZ
|
| + // via 'super' calls.
|
| + BuildThrowIfNotHole(variable->name());
|
| + }
|
| +}
|
|
|
| void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
|
| + Token::Value op,
|
| FeedbackVectorSlot slot) {
|
| + VariableMode mode = variable->mode();
|
| + RegisterAllocationScope assignment_register_scope(this);
|
| + BytecodeLabel end_label;
|
| + bool hole_check_required =
|
| + (mode == CONST_LEGACY) || (mode == LET && op != Token::INIT) ||
|
| + (mode == CONST && op != Token::INIT) ||
|
| + (mode == CONST && op == Token::INIT && variable->is_this());
|
| switch (variable->location()) {
|
| + case VariableLocation::PARAMETER:
|
| case VariableLocation::LOCAL: {
|
| - // TODO(rmcilroy): support const mode initialization.
|
| - Register destination(variable->index());
|
| - builder()->StoreAccumulatorInRegister(destination);
|
| - break;
|
| - }
|
| - case VariableLocation::PARAMETER: {
|
| - // 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));
|
| + Register destination;
|
| + if (VariableLocation::PARAMETER == variable->location()) {
|
| + destination = Register(builder()->Parameter(variable->index() + 1));
|
| + } else {
|
| + destination = Register(variable->index());
|
| + }
|
| +
|
| + if (hole_check_required) {
|
| + // Load destination to check for hole.
|
| + Register value_temp = register_allocator()->NewRegister();
|
| + builder()
|
| + ->StoreAccumulatorInRegister(value_temp)
|
| + .LoadAccumulatorWithRegister(destination);
|
| +
|
| + if (mode == CONST_LEGACY && op == Token::INIT) {
|
| + // Perform an intialization check for legacy constants.
|
| + builder()
|
| + ->JumpIfNotHole(&end_label)
|
| + .MoveRegister(value_temp, destination)
|
| + .Bind(&end_label)
|
| + .LoadAccumulatorWithRegister(value_temp);
|
| + // Break here because the value should not be stored unconditionally.
|
| + break;
|
| + } else if (mode == CONST_LEGACY && op != Token::INIT) {
|
| + DCHECK(!is_strict(language_mode()));
|
| + // Ensure accumulator is in the correct state.
|
| + builder()->LoadAccumulatorWithRegister(value_temp);
|
| + // Break here, non-initializing assignments to legacy constants are
|
| + // ignored.
|
| + break;
|
| + } else {
|
| + BuildHoleCheckForVariableAssignment(variable, op);
|
| + builder()->LoadAccumulatorWithRegister(value_temp);
|
| + }
|
| + }
|
| +
|
| builder()->StoreAccumulatorInRegister(destination);
|
| break;
|
| }
|
| @@ -1775,10 +1887,10 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
|
| break;
|
| }
|
| case VariableLocation::CONTEXT: {
|
| - // TODO(rmcilroy): support const mode initialization.
|
| int depth = execution_context()->ContextChainDepth(variable->scope());
|
| ContextScope* context = execution_context()->Previous(depth);
|
| Register context_reg;
|
| +
|
| if (context) {
|
| context_reg = context->reg();
|
| } else {
|
| @@ -1800,13 +1912,63 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
|
| }
|
| builder()->LoadAccumulatorWithRegister(value_temp);
|
| }
|
| +
|
| + if (hole_check_required) {
|
| + // Load destination to check for hole.
|
| + Register value_temp = register_allocator()->NewRegister();
|
| + builder()
|
| + ->StoreAccumulatorInRegister(value_temp)
|
| + .LoadContextSlot(context_reg, variable->index());
|
| +
|
| + if (mode == CONST_LEGACY && op == Token::INIT) {
|
| + // Perform an intialization check for legacy constants.
|
| + builder()
|
| + ->JumpIfNotHole(&end_label)
|
| + .LoadAccumulatorWithRegister(value_temp)
|
| + .StoreContextSlot(context_reg, variable->index())
|
| + .Bind(&end_label);
|
| + builder()->LoadAccumulatorWithRegister(value_temp);
|
| + // Break here because the value should not be stored unconditionally.
|
| + // The above code performs the store conditionally.
|
| + break;
|
| + } else if (mode == CONST_LEGACY && op != Token::INIT) {
|
| + DCHECK(!is_strict(language_mode()));
|
| + // Ensure accumulator is in the correct state.
|
| + builder()->LoadAccumulatorWithRegister(value_temp);
|
| + // Break here, non-initializing assignments to legacy constants are
|
| + // ignored.
|
| + break;
|
| + } else {
|
| + BuildHoleCheckForVariableAssignment(variable, op);
|
| + builder()->LoadAccumulatorWithRegister(value_temp);
|
| + }
|
| + }
|
| +
|
| builder()->StoreContextSlot(context_reg, variable->index());
|
| break;
|
| }
|
| case VariableLocation::LOOKUP: {
|
| - // TODO(mythria): Use Runtime::kInitializeLegacyConstLookupSlot for
|
| - // initializations of const declarations.
|
| - builder()->StoreLookupSlot(variable->name(), language_mode());
|
| + if (mode == CONST_LEGACY && op == Token::INIT) {
|
| + register_allocator()->PrepareForConsecutiveAllocations(3);
|
| + Register value = register_allocator()->NextConsecutiveRegister();
|
| + Register context = register_allocator()->NextConsecutiveRegister();
|
| + Register name = register_allocator()->NextConsecutiveRegister();
|
| +
|
| + // InitializeLegacyConstLookupSlot runtime call returns the 'value'
|
| + // passed to it. So, accumulator will have its original contents when
|
| + // runtime call returns.
|
| + builder()
|
| + ->StoreAccumulatorInRegister(value)
|
| + .MoveRegister(execution_context()->reg(), context)
|
| + .LoadLiteral(variable->name())
|
| + .StoreAccumulatorInRegister(name)
|
| + .CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, value, 3);
|
| + } else if (mode == CONST_LEGACY && op != Token::INIT) {
|
| + // Non-intializing assignments to legacy constants are ignored.
|
| + DCHECK(!is_strict(language_mode()));
|
| + } else {
|
| + builder()->StoreLookupSlot(variable->name(), language_mode());
|
| + }
|
| break;
|
| }
|
| }
|
| @@ -1899,7 +2061,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
|
| // TODO(oth): The VisitVariableAssignment() call is hard to reason about.
|
| // Is the value in the accumulator safe? Yes, but scary.
|
| Variable* variable = expr->target()->AsVariableProxy()->var();
|
| - VisitVariableAssignment(variable, slot);
|
| + VisitVariableAssignment(variable, expr->op(), slot);
|
| break;
|
| }
|
| case NAMED_PROPERTY:
|
| @@ -2324,7 +2486,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
|
| switch (assign_type) {
|
| case VARIABLE: {
|
| Variable* variable = expr->expression()->AsVariableProxy()->var();
|
| - VisitVariableAssignment(variable, feedback_slot);
|
| + VisitVariableAssignment(variable, expr->op(), feedback_slot);
|
| break;
|
| }
|
| case NAMED_PROPERTY: {
|
| @@ -2601,7 +2763,8 @@ void BytecodeGenerator::VisitArgumentsObject(Variable* variable) {
|
| ? CreateArgumentsType::kUnmappedArguments
|
| : CreateArgumentsType::kMappedArguments;
|
| builder()->CreateArguments(type);
|
| - VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
|
| + VisitVariableAssignment(variable, Token::ASSIGN,
|
| + FeedbackVectorSlot::Invalid());
|
| }
|
|
|
| void BytecodeGenerator::VisitRestArgumentsArray(Variable* rest) {
|
| @@ -2611,7 +2774,7 @@ void BytecodeGenerator::VisitRestArgumentsArray(Variable* rest) {
|
| // variable.
|
| builder()->CreateArguments(CreateArgumentsType::kRestParameter);
|
| DCHECK(rest->IsContextSlot() || rest->IsStackAllocated());
|
| - VisitVariableAssignment(rest, FeedbackVectorSlot::Invalid());
|
| + VisitVariableAssignment(rest, Token::ASSIGN, FeedbackVectorSlot::Invalid());
|
| }
|
|
|
| void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) {
|
| @@ -2622,7 +2785,7 @@ void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) {
|
|
|
| // Store the closure we were called with in the given variable.
|
| builder()->LoadAccumulatorWithRegister(Register::function_closure());
|
| - VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
|
| + VisitVariableAssignment(variable, Token::INIT, FeedbackVectorSlot::Invalid());
|
| }
|
|
|
|
|
| @@ -2631,7 +2794,7 @@ void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
|
|
|
| // Store the new target we were called with in the given variable.
|
| builder()->LoadAccumulatorWithRegister(Register::new_target());
|
| - VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
|
| + VisitVariableAssignment(variable, Token::INIT, FeedbackVectorSlot::Invalid());
|
| }
|
|
|
|
|
|
|