Commit 90721a51 authored by mythria's avatar mythria Committed by Commit bot

[Interpreter] Adds support for const/let variables to interpreter.

Adds implementation and tests to support const/let variables in the
interpreter.

BUG=v8:4280,v8:4679
LOG=N

Review URL: https://codereview.chromium.org/1634153002

Cr-Commit-Position: refs/heads/master@{#33819}
parent e708dd54
...@@ -1510,6 +1510,20 @@ void BytecodeGraphBuilder::VisitJumpIfUndefinedConstantWide() { ...@@ -1510,6 +1510,20 @@ void BytecodeGraphBuilder::VisitJumpIfUndefinedConstantWide() {
BuildJumpIfEqual(jsgraph()->UndefinedConstant()); BuildJumpIfEqual(jsgraph()->UndefinedConstant());
} }
void BytecodeGraphBuilder::VisitJumpIfHole() {
BuildJumpIfEqual(jsgraph()->TheHoleConstant());
}
void BytecodeGraphBuilder::VisitJumpIfNotHole() {
Node* accumulator = environment()->LookupAccumulator();
Node* condition = NewNode(javascript()->StrictEqual(), accumulator,
jsgraph()->TheHoleConstant());
Node* node =
NewNode(common()->Select(MachineRepresentation::kTagged), condition,
jsgraph()->FalseConstant(), jsgraph()->TrueConstant());
BuildConditionalJump(node);
}
void BytecodeGraphBuilder::VisitStackCheck() { void BytecodeGraphBuilder::VisitStackCheck() {
FrameStateBeforeAndAfter states(this); FrameStateBeforeAndAfter states(this);
Node* node = NewNode(javascript()->StackCheck()); Node* node = NewNode(javascript()->StackCheck());
......
...@@ -661,9 +661,8 @@ Node* InterpreterAssembler::Advance(Node* delta) { ...@@ -661,9 +661,8 @@ Node* InterpreterAssembler::Advance(Node* delta) {
void InterpreterAssembler::Jump(Node* delta) { DispatchTo(Advance(delta)); } void InterpreterAssembler::Jump(Node* delta) { DispatchTo(Advance(delta)); }
void InterpreterAssembler::JumpIfWordEqual(Node* lhs, Node* rhs, Node* delta) { void InterpreterAssembler::JumpConditional(Node* condition, Node* delta) {
RawMachineLabel match, no_match; RawMachineLabel match, no_match;
Node* condition = raw_assembler_->WordEqual(lhs, rhs);
raw_assembler_->Branch(condition, &match, &no_match); raw_assembler_->Branch(condition, &match, &no_match);
raw_assembler_->Bind(&match); raw_assembler_->Bind(&match);
DispatchTo(Advance(delta)); DispatchTo(Advance(delta));
...@@ -671,6 +670,14 @@ void InterpreterAssembler::JumpIfWordEqual(Node* lhs, Node* rhs, Node* delta) { ...@@ -671,6 +670,14 @@ void InterpreterAssembler::JumpIfWordEqual(Node* lhs, Node* rhs, Node* delta) {
Dispatch(); Dispatch();
} }
void InterpreterAssembler::JumpIfWordEqual(Node* lhs, Node* rhs, Node* delta) {
JumpConditional(raw_assembler_->WordEqual(lhs, rhs), delta);
}
void InterpreterAssembler::JumpIfWordNotEqual(Node* lhs, Node* rhs,
Node* delta) {
JumpConditional(raw_assembler_->WordNotEqual(lhs, rhs), delta);
}
void InterpreterAssembler::Dispatch() { void InterpreterAssembler::Dispatch() {
DispatchTo(Advance(interpreter::Bytecodes::Size(bytecode_))); DispatchTo(Advance(interpreter::Bytecodes::Size(bytecode_)));
......
...@@ -147,10 +147,19 @@ class InterpreterAssembler { ...@@ -147,10 +147,19 @@ class InterpreterAssembler {
// Jump relative to the current bytecode by |jump_offset|. // Jump relative to the current bytecode by |jump_offset|.
void Jump(Node* jump_offset); void Jump(Node* jump_offset);
// Jump relative to the current bytecode by |jump_offset| if the
// |condition| is true. Helper function for JumpIfWordEqual and
// JumpIfWordNotEqual.
void JumpConditional(Node* condition, Node* jump_offset);
// Jump relative to the current bytecode by |jump_offset| if the // Jump relative to the current bytecode by |jump_offset| if the
// word values |lhs| and |rhs| are equal. // word values |lhs| and |rhs| are equal.
void JumpIfWordEqual(Node* lhs, Node* rhs, Node* jump_offset); void JumpIfWordEqual(Node* lhs, Node* rhs, Node* jump_offset);
// Jump relative to the current bytecode by |jump_offset| if the
// word values |lhs| and |rhs| are not equal.
void JumpIfWordNotEqual(Node* lhs, Node* rhs, Node* jump_offset);
// Perform a stack guard check. // Perform a stack guard check.
void StackCheck(); void StackCheck();
......
...@@ -807,6 +807,8 @@ Bytecode BytecodeArrayBuilder::GetJumpWithToBoolean(Bytecode jump_bytecode) { ...@@ -807,6 +807,8 @@ Bytecode BytecodeArrayBuilder::GetJumpWithToBoolean(Bytecode jump_bytecode) {
case Bytecode::kJump: case Bytecode::kJump:
case Bytecode::kJumpIfNull: case Bytecode::kJumpIfNull:
case Bytecode::kJumpIfUndefined: case Bytecode::kJumpIfUndefined:
case Bytecode::kJumpIfHole:
case Bytecode::kJumpIfNotHole:
return jump_bytecode; return jump_bytecode;
case Bytecode::kJumpIfTrue: case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfToBooleanTrue; return Bytecode::kJumpIfToBooleanTrue;
...@@ -972,6 +974,15 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck() { ...@@ -972,6 +974,15 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck() {
return *this; return *this;
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfHole(BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfHole, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotHole(
BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfNotHole, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() { BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() {
Output(Bytecode::kThrow); Output(Bytecode::kThrow);
exit_seen_in_block_ = true; exit_seen_in_block_ = true;
......
...@@ -231,6 +231,8 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover { ...@@ -231,6 +231,8 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
BytecodeArrayBuilder& JumpIfFalse(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfFalse(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfHole(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfNotHole(BytecodeLabel* label);
BytecodeArrayBuilder& StackCheck(); BytecodeArrayBuilder& StackCheck();
......
...@@ -721,7 +721,10 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) { ...@@ -721,7 +721,10 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
case VariableLocation::PARAMETER: case VariableLocation::PARAMETER:
case VariableLocation::LOCAL: { case VariableLocation::LOCAL: {
VisitForAccumulatorValue(decl->fun()); VisitForAccumulatorValue(decl->fun());
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); DCHECK(variable->mode() == LET || variable->mode() == VAR ||
variable->mode() == CONST);
VisitVariableAssignment(variable, Token::INIT,
FeedbackVectorSlot::Invalid());
break; break;
} }
case VariableLocation::CONTEXT: { case VariableLocation::CONTEXT: {
...@@ -1007,7 +1010,7 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr, ...@@ -1007,7 +1010,7 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr,
switch (assign_type) { switch (assign_type) {
case VARIABLE: { case VARIABLE: {
Variable* variable = expr->AsVariableProxy()->var(); Variable* variable = expr->AsVariableProxy()->var();
VisitVariableAssignment(variable, slot); VisitVariableAssignment(variable, Token::ASSIGN, slot);
break; break;
} }
case NAMED_PROPERTY: { case NAMED_PROPERTY: {
...@@ -1260,7 +1263,7 @@ void BytecodeGenerator::VisitClassLiteralContents(ClassLiteral* expr) { ...@@ -1260,7 +1263,7 @@ void BytecodeGenerator::VisitClassLiteralContents(ClassLiteral* expr) {
FeedbackVectorSlot slot = expr->NeedsProxySlot() FeedbackVectorSlot slot = expr->NeedsProxySlot()
? expr->ProxySlot() ? expr->ProxySlot()
: FeedbackVectorSlot::Invalid(); : FeedbackVectorSlot::Invalid();
VisitVariableAssignment(var, slot); VisitVariableAssignment(var, Token::INIT, slot);
} }
execution_result()->SetResultInAccumulator(); execution_result()->SetResultInAccumulator();
} }
...@@ -1674,14 +1677,27 @@ void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) { ...@@ -1674,14 +1677,27 @@ void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot()); 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, void BytecodeGenerator::VisitVariableLoad(Variable* variable,
FeedbackVectorSlot slot, FeedbackVectorSlot slot,
TypeofMode typeof_mode) { TypeofMode typeof_mode) {
VariableMode mode = variable->mode();
switch (variable->location()) { switch (variable->location()) {
case VariableLocation::LOCAL: { case VariableLocation::LOCAL: {
Register source(Register(variable->index())); Register source(Register(variable->index()));
builder()->LoadAccumulatorWithRegister(source); builder()->LoadAccumulatorWithRegister(source);
BuildHoleCheckForVariableLoad(mode, variable->name());
execution_result()->SetResultInAccumulator(); execution_result()->SetResultInAccumulator();
break; break;
} }
...@@ -1690,6 +1706,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, ...@@ -1690,6 +1706,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
// index -1 but is parameter index 0 in BytecodeArrayBuilder). // index -1 but is parameter index 0 in BytecodeArrayBuilder).
Register source = builder()->Parameter(variable->index() + 1); Register source = builder()->Parameter(variable->index() + 1);
builder()->LoadAccumulatorWithRegister(source); builder()->LoadAccumulatorWithRegister(source);
BuildHoleCheckForVariableLoad(mode, variable->name());
execution_result()->SetResultInAccumulator(); execution_result()->SetResultInAccumulator();
break; break;
} }
...@@ -1722,10 +1739,10 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, ...@@ -1722,10 +1739,10 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
.StoreAccumulatorInRegister(context_reg); .StoreAccumulatorInRegister(context_reg);
} }
} }
builder()->LoadContextSlot(context_reg, variable->index()); builder()->LoadContextSlot(context_reg, variable->index());
BuildHoleCheckForVariableLoad(mode, variable->name());
execution_result()->SetResultInAccumulator(); execution_result()->SetResultInAccumulator();
// TODO(rmcilroy): Perform check for uninitialized legacy const, const and
// let variables.
break; break;
} }
case VariableLocation::LOOKUP: { case VariableLocation::LOOKUP: {
...@@ -1751,20 +1768,115 @@ Register BytecodeGenerator::VisitVariableLoadForRegisterValue( ...@@ -1751,20 +1768,115 @@ Register BytecodeGenerator::VisitVariableLoadForRegisterValue(
return register_scope.ResultRegister(); 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, void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
Token::Value op,
FeedbackVectorSlot slot) { 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()) { switch (variable->location()) {
case VariableLocation::PARAMETER:
case VariableLocation::LOCAL: { case VariableLocation::LOCAL: {
// TODO(rmcilroy): support const mode initialization. Register destination;
Register destination(variable->index()); if (VariableLocation::PARAMETER == variable->location()) {
builder()->StoreAccumulatorInRegister(destination); 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; break;
} else {
BuildHoleCheckForVariableAssignment(variable, op);
builder()->LoadAccumulatorWithRegister(value_temp);
} }
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));
builder()->StoreAccumulatorInRegister(destination); builder()->StoreAccumulatorInRegister(destination);
break; break;
} }
...@@ -1775,10 +1887,10 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable, ...@@ -1775,10 +1887,10 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
break; break;
} }
case VariableLocation::CONTEXT: { case VariableLocation::CONTEXT: {
// TODO(rmcilroy): support const mode initialization.
int depth = execution_context()->ContextChainDepth(variable->scope()); int depth = execution_context()->ContextChainDepth(variable->scope());
ContextScope* context = execution_context()->Previous(depth); ContextScope* context = execution_context()->Previous(depth);
Register context_reg; Register context_reg;
if (context) { if (context) {
context_reg = context->reg(); context_reg = context->reg();
} else { } else {
...@@ -1800,13 +1912,63 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable, ...@@ -1800,13 +1912,63 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
} }
builder()->LoadAccumulatorWithRegister(value_temp); 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()); builder()->StoreContextSlot(context_reg, variable->index());
break; break;
} }
case VariableLocation::LOOKUP: { case VariableLocation::LOOKUP: {
// TODO(mythria): Use Runtime::kInitializeLegacyConstLookupSlot for if (mode == CONST_LEGACY && op == Token::INIT) {
// initializations of const declarations. 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()); builder()->StoreLookupSlot(variable->name(), language_mode());
}
break; break;
} }
} }
...@@ -1899,7 +2061,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { ...@@ -1899,7 +2061,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
// TODO(oth): The VisitVariableAssignment() call is hard to reason about. // TODO(oth): The VisitVariableAssignment() call is hard to reason about.
// Is the value in the accumulator safe? Yes, but scary. // Is the value in the accumulator safe? Yes, but scary.
Variable* variable = expr->target()->AsVariableProxy()->var(); Variable* variable = expr->target()->AsVariableProxy()->var();
VisitVariableAssignment(variable, slot); VisitVariableAssignment(variable, expr->op(), slot);
break; break;
} }
case NAMED_PROPERTY: case NAMED_PROPERTY:
...@@ -2324,7 +2486,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) { ...@@ -2324,7 +2486,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
switch (assign_type) { switch (assign_type) {
case VARIABLE: { case VARIABLE: {
Variable* variable = expr->expression()->AsVariableProxy()->var(); Variable* variable = expr->expression()->AsVariableProxy()->var();
VisitVariableAssignment(variable, feedback_slot); VisitVariableAssignment(variable, expr->op(), feedback_slot);
break; break;
} }
case NAMED_PROPERTY: { case NAMED_PROPERTY: {
...@@ -2601,7 +2763,8 @@ void BytecodeGenerator::VisitArgumentsObject(Variable* variable) { ...@@ -2601,7 +2763,8 @@ void BytecodeGenerator::VisitArgumentsObject(Variable* variable) {
? CreateArgumentsType::kUnmappedArguments ? CreateArgumentsType::kUnmappedArguments
: CreateArgumentsType::kMappedArguments; : CreateArgumentsType::kMappedArguments;
builder()->CreateArguments(type); builder()->CreateArguments(type);
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); VisitVariableAssignment(variable, Token::ASSIGN,
FeedbackVectorSlot::Invalid());
} }
void BytecodeGenerator::VisitRestArgumentsArray(Variable* rest) { void BytecodeGenerator::VisitRestArgumentsArray(Variable* rest) {
...@@ -2611,7 +2774,7 @@ void BytecodeGenerator::VisitRestArgumentsArray(Variable* rest) { ...@@ -2611,7 +2774,7 @@ void BytecodeGenerator::VisitRestArgumentsArray(Variable* rest) {
// variable. // variable.
builder()->CreateArguments(CreateArgumentsType::kRestParameter); builder()->CreateArguments(CreateArgumentsType::kRestParameter);
DCHECK(rest->IsContextSlot() || rest->IsStackAllocated()); DCHECK(rest->IsContextSlot() || rest->IsStackAllocated());
VisitVariableAssignment(rest, FeedbackVectorSlot::Invalid()); VisitVariableAssignment(rest, Token::ASSIGN, FeedbackVectorSlot::Invalid());
} }
void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) { void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) {
...@@ -2622,7 +2785,7 @@ 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. // Store the closure we were called with in the given variable.
builder()->LoadAccumulatorWithRegister(Register::function_closure()); builder()->LoadAccumulatorWithRegister(Register::function_closure());
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); VisitVariableAssignment(variable, Token::INIT, FeedbackVectorSlot::Invalid());
} }
...@@ -2631,7 +2794,7 @@ void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) { ...@@ -2631,7 +2794,7 @@ void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
// Store the new target we were called with in the given variable. // Store the new target we were called with in the given variable.
builder()->LoadAccumulatorWithRegister(Register::new_target()); builder()->LoadAccumulatorWithRegister(Register::new_target());
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); VisitVariableAssignment(variable, Token::INIT, FeedbackVectorSlot::Invalid());
} }
......
...@@ -76,7 +76,13 @@ class BytecodeGenerator final : public AstVisitor { ...@@ -76,7 +76,13 @@ class BytecodeGenerator final : public AstVisitor {
MUST_USE_RESULT Register MUST_USE_RESULT Register
VisitVariableLoadForRegisterValue(Variable* variable, FeedbackVectorSlot slot, VisitVariableLoadForRegisterValue(Variable* variable, FeedbackVectorSlot slot,
TypeofMode typeof_mode = NOT_INSIDE_TYPEOF); TypeofMode typeof_mode = NOT_INSIDE_TYPEOF);
void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot); void VisitVariableAssignment(Variable* variable, Token::Value op,
FeedbackVectorSlot slot);
void BuildThrowIfHole(Handle<String> name);
void BuildThrowIfNotHole(Handle<String> name);
void BuildThrowReassignConstant(Handle<String> name);
void BuildHoleCheckForVariableLoad(VariableMode mode, Handle<String> name);
void BuildHoleCheckForVariableAssignment(Variable* variable, Token::Value op);
void VisitArgumentsObject(Variable* variable); void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest); void VisitRestArgumentsArray(Variable* rest);
......
...@@ -196,7 +196,9 @@ bool Bytecodes::IsConditionalJumpImmediate(Bytecode bytecode) { ...@@ -196,7 +196,9 @@ bool Bytecodes::IsConditionalJumpImmediate(Bytecode bytecode) {
bytecode == Bytecode::kJumpIfToBooleanTrue || bytecode == Bytecode::kJumpIfToBooleanTrue ||
bytecode == Bytecode::kJumpIfToBooleanFalse || bytecode == Bytecode::kJumpIfToBooleanFalse ||
bytecode == Bytecode::kJumpIfNull || bytecode == Bytecode::kJumpIfNull ||
bytecode == Bytecode::kJumpIfUndefined; bytecode == Bytecode::kJumpIfUndefined ||
bytecode == Bytecode::kJumpIfHole ||
bytecode == Bytecode::kJumpIfNotHole;
} }
......
...@@ -252,6 +252,9 @@ namespace interpreter { ...@@ -252,6 +252,9 @@ namespace interpreter {
V(JumpIfUndefined, OperandType::kImm8) \ V(JumpIfUndefined, OperandType::kImm8) \
V(JumpIfUndefinedConstant, OperandType::kIdx8) \ V(JumpIfUndefinedConstant, OperandType::kIdx8) \
V(JumpIfUndefinedConstantWide, OperandType::kIdx16) \ V(JumpIfUndefinedConstantWide, OperandType::kIdx16) \
/* TODO(mythria): Replace with opcodes that throw on a hole */ \
V(JumpIfHole, OperandType::kImm8) \
V(JumpIfNotHole, OperandType::kImm8) \
\ \
/* Complex flow control For..in */ \ /* Complex flow control For..in */ \
V(ForInPrepare, OperandType::kRegOutTriple8) \ V(ForInPrepare, OperandType::kRegOutTriple8) \
......
...@@ -1605,8 +1605,7 @@ void Interpreter::DoJumpIfNullConstantWide( ...@@ -1605,8 +1605,7 @@ void Interpreter::DoJumpIfNullConstantWide(
DoJumpIfNullConstant(assembler); DoJumpIfNullConstant(assembler);
} }
// JumpIfUndefined <imm8>
// jumpifundefined <imm8>
// //
// Jump by number of bytes represented by an immediate operand if the object // Jump by number of bytes represented by an immediate operand if the object
// referenced by the accumulator is the undefined constant. // referenced by the accumulator is the undefined constant.
...@@ -1644,6 +1643,27 @@ void Interpreter::DoJumpIfUndefinedConstantWide( ...@@ -1644,6 +1643,27 @@ void Interpreter::DoJumpIfUndefinedConstantWide(
DoJumpIfUndefinedConstant(assembler); DoJumpIfUndefinedConstant(assembler);
} }
// JumpIfHole <imm8>
//
// Jump by number of bytes represented by an immediate operand if the object
// referenced by the accumulator is the hole.
void Interpreter::DoJumpIfHole(compiler::InterpreterAssembler* assembler) {
Node* accumulator = __ GetAccumulator();
Node* the_hole_value = __ HeapConstant(isolate_->factory()->the_hole_value());
Node* relative_jump = __ BytecodeOperandImm(0);
__ JumpIfWordEqual(accumulator, the_hole_value, relative_jump);
}
// JumpIfNotHole <imm8>
//
// Jump by number of bytes represented by an immediate operand if the object
// referenced by the accumulator is not the hole.
void Interpreter::DoJumpIfNotHole(compiler::InterpreterAssembler* assembler) {
Node* accumulator = __ GetAccumulator();
Node* the_hole_value = __ HeapConstant(isolate_->factory()->the_hole_value());
Node* relative_jump = __ BytecodeOperandImm(0);
__ JumpIfWordNotEqual(accumulator, the_hole_value, relative_jump);
}
void Interpreter::DoCreateLiteral(Runtime::FunctionId function_id, void Interpreter::DoCreateLiteral(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler) { compiler::InterpreterAssembler* assembler) {
......
...@@ -575,14 +575,6 @@ ...@@ -575,14 +575,6 @@
'test-api/InterceptorShouldThrowOnError': [FAIL], 'test-api/InterceptorShouldThrowOnError': [FAIL],
'test-api/StrongModeAccessCheckAllowed': [FAIL], 'test-api/StrongModeAccessCheckAllowed': [FAIL],
# TODO(rmcilroy,4680): FunctionTester SameValue check fails.
'test-run-variables/StackInitializeVariables': [FAIL],
'test-run-variables/ContextInitializeVariables': [FAIL],
'test-run-variables/ContextStoreVariables': [FAIL],
'test-run-variables/StackStoreVariables': [FAIL],
'test-run-variables/ContextLoadVariables': [FAIL],
'test-run-variables/StackLoadVariables': [FAIL],
# TODO(rmcilroy,4680): The function_data field should be a BytecodeArray on interpreter entry # TODO(rmcilroy,4680): The function_data field should be a BytecodeArray on interpreter entry
'test-api/SetFunctionEntryHook': [FAIL], 'test-api/SetFunctionEntryHook': [FAIL],
...@@ -640,7 +632,6 @@ ...@@ -640,7 +632,6 @@
'test-heap/CompilationCacheCachingBehavior': [FAIL], 'test-heap/CompilationCacheCachingBehavior': [FAIL],
'test-heap/CellsInOptimizedCodeAreWeak': [FAIL], 'test-heap/CellsInOptimizedCodeAreWeak': [FAIL],
'test-run-inlining/InlineTwice': [FAIL], 'test-run-inlining/InlineTwice': [FAIL],
'test-decls/Regress425510': [FAIL],
# TODO(rmcilroy,4680): Fail on some bot configurations. # TODO(rmcilroy,4680): Fail on some bot configurations.
'test-heap/CanonicalSharedFunctionInfo': [PASS, FAIL], 'test-heap/CanonicalSharedFunctionInfo': [PASS, FAIL],
......
...@@ -2649,7 +2649,6 @@ TEST(JumpWithConstantsAndWideConstants) { ...@@ -2649,7 +2649,6 @@ TEST(JumpWithConstantsAndWideConstants) {
TEST(BytecodeGraphBuilderDoExpressions) { TEST(BytecodeGraphBuilderDoExpressions) {
bool old_flag = FLAG_harmony_do_expressions; bool old_flag = FLAG_harmony_do_expressions;
FLAG_harmony_do_expressions = true; FLAG_harmony_do_expressions = true;
HandleAndZoneScope scope; HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate(); Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone(); Zone* zone = scope.main_zone();
...@@ -2716,6 +2715,266 @@ TEST(BytecodeGraphBuilderWithStatement) { ...@@ -2716,6 +2715,266 @@ TEST(BytecodeGraphBuilderWithStatement) {
} }
} }
TEST(BytecodeGraphBuilderConstDeclaration) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<0> snippets[] = {
{"const x = 3; return x;", {handle(Smi::FromInt(3), isolate)}},
{"let x = 10; x = x + 20; return x;",
{handle(Smi::FromInt(30), isolate)}},
{"let x = 10; x = 20; return x;", {handle(Smi::FromInt(20), isolate)}},
{"let x; x = 20; return x;", {handle(Smi::FromInt(20), isolate)}},
{"let x; return x;", {factory->undefined_value()}},
{"var x = 10; { let x = 30; } return x;",
{handle(Smi::FromInt(10), isolate)}},
{"let x = 10; { let x = 20; } return x;",
{handle(Smi::FromInt(10), isolate)}},
{"var x = 10; eval('let x = 20;'); return x;",
{handle(Smi::FromInt(10), isolate)}},
{"var x = 10; eval('const x = 20;'); return x;",
{handle(Smi::FromInt(10), isolate)}},
{"var x = 10; { const x = 20; } return x;",
{handle(Smi::FromInt(10), isolate)}},
{"var x = 10; { const x = 20; return x;} return -1;",
{handle(Smi::FromInt(20), isolate)}},
{"var a = 10;\n"
"for (var i = 0; i < 10; ++i) {\n"
" const x = i;\n" // const declarations are block scoped.
" a = a + x;\n"
"}\n"
"return a;\n",
{handle(Smi::FromInt(55), isolate)}},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(snippets); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(snippets); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() {'use strict'; %s }\n%s();", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
TEST(BytecodeGraphBuilderConstDeclarationLookupSlots) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<0> snippets[] = {
{"const x = 3; function f1() {return x;}; return x;",
{handle(Smi::FromInt(3), isolate)}},
{"let x = 10; x = x + 20; function f1() {return x;}; return x;",
{handle(Smi::FromInt(30), isolate)}},
{"let x; x = 20; function f1() {return x;}; return x;",
{handle(Smi::FromInt(20), isolate)}},
{"let x; function f1() {return x;}; return x;",
{factory->undefined_value()}},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(snippets); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(snippets); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() {'use strict'; %s }\n%s();", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
TEST(BytecodeGraphBuilderConstInLookupContextChain) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
const char* prologue =
"function OuterMost() {\n"
" const outerConst = 10;\n"
" let outerLet = 20;\n"
" function Outer() {\n"
" function Inner() {\n"
" this.innerFunc = function() { ";
const char* epilogue =
" }\n"
" }\n"
" this.getInnerFunc ="
" function() {return new Inner().innerFunc;}\n"
" }\n"
" this.getOuterFunc ="
" function() {return new Outer().getInnerFunc();}"
"}\n"
"var f = new OuterMost().getOuterFunc();\n"
"f();\n";
// Tests for let / constant.
ExpectedSnippet<0> const_decl[] = {
{"return outerConst;", {handle(Smi::FromInt(10), isolate)}},
{"return outerLet;", {handle(Smi::FromInt(20), isolate)}},
{"outerLet = 30; return outerLet;", {handle(Smi::FromInt(30), isolate)}},
{"var outerLet = 40; return outerLet;",
{handle(Smi::FromInt(40), isolate)}},
{"var outerConst = 50; return outerConst;",
{handle(Smi::FromInt(50), isolate)}},
{"try { outerConst = 30 } catch(e) { return -1; }",
{handle(Smi::FromInt(-1), isolate)}}};
for (size_t i = 0; i < arraysize(const_decl); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "%s %s %s", prologue, const_decl[i].code_snippet,
epilogue);
BytecodeGraphTester tester(isolate, zone, script.start(), "*");
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].return_value()));
}
// Tests for Legacy constant.
bool old_flag_legacy_const = FLAG_legacy_const;
FLAG_legacy_const = true;
ExpectedSnippet<0> legacy_const_decl[] = {
{"return outerConst = 23;", {handle(Smi::FromInt(23), isolate)}},
{"outerConst = 30; return outerConst;",
{handle(Smi::FromInt(10), isolate)}},
};
for (size_t i = 0; i < arraysize(legacy_const_decl); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "%s %s %s", prologue, legacy_const_decl[i].code_snippet,
epilogue);
BytecodeGraphTester tester(isolate, zone, script.start(), "*");
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*legacy_const_decl[i].return_value()));
}
FLAG_legacy_const = old_flag_legacy_const;
}
TEST(BytecodeGraphBuilderIllegalConstDeclaration) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
ExpectedSnippet<0, const char*> illegal_const_decl[] = {
{"const x = x = 10 + 3; return x;",
{"Uncaught ReferenceError: x is not defined"}},
{"const x = 10; x = 20; return x;",
{"Uncaught TypeError: Assignment to constant variable."}},
{"const x = 10; { x = 20; } return x;",
{"Uncaught TypeError: Assignment to constant variable."}},
{"const x = 10; eval('x = 20;'); return x;",
{"Uncaught TypeError: Assignment to constant variable."}},
{"let x = x + 10; return x;",
{"Uncaught ReferenceError: x is not defined"}},
{"'use strict'; (function f1() { f1 = 123; })() ",
{"Uncaught TypeError: Assignment to constant variable."}},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(illegal_const_decl); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName,
illegal_const_decl[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
v8::Local<v8::String> message = tester.CheckThrowsReturnMessage()->Get();
v8::Local<v8::String> expected_string =
v8_str(illegal_const_decl[i].return_value());
CHECK(
message->Equals(CcTest::isolate()->GetCurrentContext(), expected_string)
.FromJust());
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(illegal_const_decl); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() {'use strict'; %s }\n%s();", kFunctionName,
illegal_const_decl[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
v8::Local<v8::String> message = tester.CheckThrowsReturnMessage()->Get();
v8::Local<v8::String> expected_string =
v8_str(illegal_const_decl[i].return_value());
CHECK(
message->Equals(CcTest::isolate()->GetCurrentContext(), expected_string)
.FromJust());
}
}
TEST(BytecodeGraphBuilderLegacyConstDeclaration) {
bool old_flag_legacy_const = FLAG_legacy_const;
FLAG_legacy_const = true;
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
ExpectedSnippet<0> snippets[] = {
{"const x = (x = 10) + 3; return x;",
{handle(Smi::FromInt(13), isolate)}},
{"const x = 10; x = 20; return x;", {handle(Smi::FromInt(10), isolate)}},
{"var a = 10;\n"
"for (var i = 0; i < 10; ++i) {\n"
" const x = i;\n" // Legacy constants are not block scoped.
" a = a + x;\n"
"}\n"
"return a;\n",
{handle(Smi::FromInt(10), isolate)}},
{"const x = 20; eval('x = 10;'); return x;",
{handle(Smi::FromInt(20), isolate)}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
FLAG_legacy_const = old_flag_legacy_const;
}
TEST(BytecodeGraphBuilderDebuggerStatement) { TEST(BytecodeGraphBuilderDebuggerStatement) {
FLAG_expose_debug_as = "debug"; FLAG_expose_debug_as = "debug";
HandleAndZoneScope scope; HandleAndZoneScope scope;
......
...@@ -29,7 +29,6 @@ class BytecodeGeneratorHelper { ...@@ -29,7 +29,6 @@ class BytecodeGeneratorHelper {
i::FLAG_ignition_filter = StrDup(kFunctionName); i::FLAG_ignition_filter = StrDup(kFunctionName);
i::FLAG_always_opt = false; i::FLAG_always_opt = false;
i::FLAG_allow_natives_syntax = true; i::FLAG_allow_natives_syntax = true;
i::FLAG_legacy_const = true;
CcTest::i_isolate()->interpreter()->Initialize(); CcTest::i_isolate()->interpreter()->Initialize();
} }
...@@ -2539,7 +2538,7 @@ TEST(BreakableBlocks) { ...@@ -2539,7 +2538,7 @@ TEST(BreakableBlocks) {
"}\n", "}\n",
5 * kPointerSize, 5 * kPointerSize,
1, 1,
40, 51,
{ {
B(StackCheck), // B(StackCheck), //
B(LdaConstant), U8(0), // B(LdaConstant), U8(0), //
...@@ -2555,15 +2554,19 @@ TEST(BreakableBlocks) { ...@@ -2555,15 +2554,19 @@ TEST(BreakableBlocks) {
B(LdaSmi8), U8(10), // B(LdaSmi8), U8(10), //
B(StaContextSlot), R(context), U8(4), // B(StaContextSlot), R(context), U8(4), //
B(Ldar), R(0), // B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(2), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(3), U8(1), //
B(Star), R(1), // B(Star), R(1), //
B(Jump), U8(2), // B(Jump), U8(2), //
B(PopContext), R(2), // B(PopContext), R(2), //
B(LdaUndefined), // B(LdaUndefined), //
B(Return), // B(Return), //
}, },
2, 3,
{InstanceType::FIXED_ARRAY_TYPE, {InstanceType::FIXED_ARRAY_TYPE, InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}}, InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"let x = 1;\n" {"let x = 1;\n"
"outer: {\n" "outer: {\n"
" inner: {\n" " inner: {\n"
...@@ -2576,10 +2579,10 @@ TEST(BreakableBlocks) { ...@@ -2576,10 +2579,10 @@ TEST(BreakableBlocks) {
"x = 4;", "x = 4;",
6 * kPointerSize, 6 * kPointerSize,
1, 1,
73, 131,
{ {
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), // B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
/* */ U8(1), // U8(1), //
B(PushContext), R(2), // B(PushContext), R(2), //
B(LdaTheHole), // B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), // B(StaContextSlot), R(context), U8(4), //
...@@ -2599,22 +2602,46 @@ TEST(BreakableBlocks) { ...@@ -2599,22 +2602,46 @@ TEST(BreakableBlocks) {
B(LdaSmi8), U8(2), // B(LdaSmi8), U8(2), //
B(StaContextSlot), R(context), U8(4), // B(StaContextSlot), R(context), U8(4), //
B(Ldar), R(0), // B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(2), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(4), U8(1), //
B(Star), R(1), // B(Star), R(1), //
B(LdaContextSlot), R(context), U8(4), // B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(3), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(4), U8(1), //
B(JumpIfToBooleanFalse), U8(6), // B(JumpIfToBooleanFalse), U8(6), //
B(PopContext), R(3), // B(PopContext), R(3), //
B(Jump), U8(9), // B(Jump), U8(27), //
B(LdaSmi8), U8(3), // B(LdaSmi8), U8(3), //
B(Star), R(4), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(3), //
B(Star), R(5), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(5), U8(1), //
B(Ldar), R(4), //
B(StaContextSlot), R(context), U8(4), // B(StaContextSlot), R(context), U8(4), //
B(PopContext), R(3), // B(PopContext), R(3), //
B(LdaSmi8), U8(4), // B(LdaSmi8), U8(4), //
B(Star), R(4), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(4), //
B(Star), R(5), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(5), U8(1), //
B(Ldar), R(4), //
B(StaContextSlot), R(context), U8(4), // B(StaContextSlot), R(context), U8(4), //
B(LdaUndefined), // B(LdaUndefined), //
B(Return), // B(Return), //
}, },
2, 5,
{InstanceType::FIXED_ARRAY_TYPE, {InstanceType::FIXED_ARRAY_TYPE, InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}}, InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
}; };
// clang-format on // clang-format on
...@@ -3289,15 +3316,15 @@ TEST(BasicLoops) { ...@@ -3289,15 +3316,15 @@ TEST(BasicLoops) {
" z++;\n" " z++;\n"
" }\n" " }\n"
"}\n", "}\n",
6 * kPointerSize, 7 * kPointerSize,
1, 1,
67, 118,
{ {
B(StackCheck), // B(StackCheck), //
B(LdaZero), // B(LdaZero), //
B(Star), R(1), // B(Star), R(1), //
B(Ldar), R(1), // B(Ldar), R(1), //
B(JumpIfToBooleanFalse), U8(59), // B(JumpIfToBooleanFalse), U8(110), //
B(StackCheck), // B(StackCheck), //
B(LdaConstant), U8(0), // B(LdaConstant), U8(0), //
B(Star), R(4), // B(Star), R(4), //
...@@ -3312,24 +3339,44 @@ TEST(BasicLoops) { ...@@ -3312,24 +3339,44 @@ TEST(BasicLoops) {
B(LdaSmi8), U8(1), // B(LdaSmi8), U8(1), //
B(StaContextSlot), R(context), U8(4), // B(StaContextSlot), R(context), U8(4), //
B(Ldar), R(0), // B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(2), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(4), U8(1), //
B(Star), R(2), // B(Star), R(2), //
B(LdaContextSlot), R(context), U8(4), // B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(3), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(4), U8(1), //
B(JumpIfToBooleanFalse), U8(6), // B(JumpIfToBooleanFalse), U8(6), //
B(PopContext), R(3), // B(PopContext), R(3), //
B(Jump), U8(-45), // B(Jump), U8(-67), //
B(LdaContextSlot), R(context), U8(4), // B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(3), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(4), U8(1), //
B(ToNumber), // B(ToNumber), //
B(Star), R(4), // B(Star), R(4), //
B(Inc), // B(Inc), //
B(Star), R(5), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(3), //
B(Star), R(6), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(6), U8(1), //
B(Ldar), R(5), //
B(StaContextSlot), R(context), U8(4), // B(StaContextSlot), R(context), U8(4), //
B(PopContext), R(3), // B(PopContext), R(3), //
B(Jump), U8(-59), // B(Jump), U8(-110), //
B(LdaUndefined), // B(LdaUndefined), //
B(Return), // B(Return), //
}, },
2, 4,
{InstanceType::FIXED_ARRAY_TYPE, {InstanceType::FIXED_ARRAY_TYPE, InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}}, InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
}; };
// clang-format on // clang-format on
...@@ -6105,6 +6152,9 @@ TEST(CreateRestParameter) { ...@@ -6105,6 +6152,9 @@ TEST(CreateRestParameter) {
} }
TEST(IllegalRedeclaration) { TEST(IllegalRedeclaration) {
bool old_legacy_const_flag = FLAG_legacy_const;
FLAG_legacy_const = true;
InitializedHandleScope handle_scope; InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper; BytecodeGeneratorHelper helper;
...@@ -6136,6 +6186,8 @@ TEST(IllegalRedeclaration) { ...@@ -6136,6 +6186,8 @@ TEST(IllegalRedeclaration) {
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array); CheckBytecodeArrayEqual(snippets[i], bytecode_array);
} }
FLAG_legacy_const = old_legacy_const_flag;
} }
...@@ -7094,28 +7146,39 @@ TEST(ThisFunction) { ...@@ -7094,28 +7146,39 @@ TEST(ThisFunction) {
// clang-format off // clang-format off
ExpectedSnippet<int> snippets[] = { ExpectedSnippet<int> snippets[] = {
{"var f;\n f = function f() { }", {"var f;\n f = function f() { }",
1 * kPointerSize, 2 * kPointerSize,
1, 1,
10, 19,
{ {
B(LdaTheHole), // B(LdaTheHole), //
B(Star), R(0), // B(Star), R(0), //
B(StackCheck), // B(StackCheck), //
B(Ldar), R(closure), // B(Ldar), R(closure), //
B(Star), R(0), // B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(5), //
B(Mov), R(1), R(0), //
B(Ldar), R(1), //
B(LdaUndefined), // B(LdaUndefined), //
B(Return), // B(Return), //
}}, }},
{"var f;\n f = function f() { return f; }", {"var f;\n f = function f() { return f; }",
1 * kPointerSize, 2 * kPointerSize,
1, 1,
9, 23,
{ {
B(LdaTheHole), // B(LdaTheHole), //
B(Star), R(0), // B(Star), R(0), //
B(StackCheck), // B(StackCheck), //
B(Ldar), R(closure), // B(Ldar), R(closure), //
B(Star), R(0), // B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(5), //
B(Mov), R(1), R(0), //
B(Ldar), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(3), //
B(LdaUndefined), //
B(Return), // B(Return), //
}}, }},
}; };
...@@ -7136,31 +7199,42 @@ TEST(NewTarget) { ...@@ -7136,31 +7199,42 @@ TEST(NewTarget) {
int new_target = Register::new_target().index(); int new_target = Register::new_target().index();
// clang-format off // clang-format off
ExpectedSnippet<int> snippets[] = { ExpectedSnippet<InstanceType> snippets[] = {
{"return new.target;", {"return new.target;",
1 * kPointerSize, 2 * kPointerSize,
1, 1,
8, 19,
{ {
B(Ldar), R(new_target), // B(Ldar), R(new_target), //
B(Star), R(0), // B(Star), R(0), //
B(StackCheck), // B(StackCheck), //
B(Ldar), R(0), // B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(0), //
B(Star), R(1), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(1), U8(1), //
B(Return), // B(Return), //
}}, },
1,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"new.target;", {"new.target;",
1 * kPointerSize, 2 * kPointerSize,
1, 1,
9, 20,
{ {
B(Ldar), R(new_target), // B(Ldar), R(new_target), //
B(Star), R(0), // B(Star), R(0), //
B(StackCheck), // B(StackCheck), //
B(Ldar), R(0), // B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(0), //
B(Star), R(1), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(1), U8(1), //
B(LdaUndefined), // B(LdaUndefined), //
B(Return), // B(Return), //
}}, },
}; 1,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}};
// clang-format on // clang-format on
for (size_t i = 0; i < arraysize(snippets); i++) { for (size_t i = 0; i < arraysize(snippets); i++) {
...@@ -8245,6 +8319,550 @@ TEST(WideRegisters) { ...@@ -8245,6 +8319,550 @@ TEST(WideRegisters) {
} }
} }
TEST(ConstVariable) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
// clang-format off
ExpectedSnippet<const char*> snippets[] = {
{"const x = 10;",
1 * kPointerSize,
1,
10,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(0), //
B(LdaUndefined), //
B(Return) //
},
0},
{"const x = 10; return x;",
2 * kPointerSize,
1,
20,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(0), //
B(Star), R(1), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(1), U8(1), //
B(Return) //
},
1,
{"x"}},
{"const x = ( x = 20);",
3 * kPointerSize,
1,
32,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(20), //
B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(0), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1), //
B(CallRuntime), U16(Runtime::kThrowConstAssignError), R(0), //
/* */ U8(0), //
B(Ldar), R(1), //
B(Star), R(0), //
B(LdaUndefined), //
B(Return) //
},
1,
{"x"}},
{"const x = 10; x = 20;",
3 * kPointerSize,
1,
36,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(0), //
B(LdaSmi8), U8(20), //
B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(0), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1), //
B(CallRuntime), U16(Runtime::kThrowConstAssignError), R(0), //
/* */ U8(0), //
B(Ldar), R(1), //
B(Star), R(0), //
B(LdaUndefined), //
B(Return) //
},
1,
{"x"}},
};
// clang-format on
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(LetVariable) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
// clang-format off
ExpectedSnippet<const char*> snippets[] = {
{"let x = 10;",
1 * kPointerSize,
1,
10,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(0), //
B(LdaUndefined), //
B(Return) //
},
0},
{"let x = 10; return x;",
2 * kPointerSize,
1,
20,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(0), //
B(Star), R(1), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(1), U8(1), //
B(Return) //
},
1,
{"x"}},
{"let x = (x = 20);",
3 * kPointerSize,
1,
27,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(20), //
B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(0), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1), //
B(Ldar), R(1), //
B(Star), R(0), //
B(LdaUndefined), //
B(Return) //
},
1,
{"x"}},
{"let x = 10; x = 20;",
3 * kPointerSize,
1,
31,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(0), //
B(LdaSmi8), U8(20), //
B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(0), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1), //
B(Ldar), R(1), //
B(Star), R(0), //
B(LdaUndefined), //
B(Return) //
},
1,
{"x"}},
};
// clang-format on
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(LegacyConstVariable) {
bool old_legacy_const_flag = FLAG_legacy_const;
FLAG_legacy_const = true;
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
// clang-format off
ExpectedSnippet<const char*> snippets[] = {
{"const x = 10;",
2 * kPointerSize,
1,
19,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(5), //
B(Mov), R(1), R(0), //
B(Ldar), R(1), //
B(LdaUndefined), //
B(Return) //
},
0},
{"const x = 10; return x;",
2 * kPointerSize,
1,
23,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(5), //
B(Mov), R(1), R(0), //
B(Ldar), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(3), //
B(LdaUndefined), //
B(Return) //
},
0},
{"const x = ( x = 20);",
2 * kPointerSize,
1,
23,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(20), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Ldar), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(5), //
B(Mov), R(1), R(0), //
B(Ldar), R(1), //
B(LdaUndefined), //
B(Return) //
},
0},
{"const x = 10; x = 20;",
2 * kPointerSize,
1,
27,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(Star), R(1), //
B(Ldar), R(0), //
B(JumpIfNotHole), U8(5), //
B(Mov), R(1), R(0), //
B(Ldar), R(1), //
B(LdaSmi8), U8(20), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Ldar), R(1), //
B(LdaUndefined), //
B(Return) //
},
0},
};
// clang-format on
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
FLAG_legacy_const = old_legacy_const_flag;
}
TEST(ConstVariableContextSlot) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
int closure = Register::function_closure().index();
int context = Register::current_context().index();
// TODO(mythria): Add tests for initialization of this via super calls.
// TODO(mythria): Add tests that walk the context chain.
// clang-format off
ExpectedSnippet<InstanceType> snippets[] = {
{"const x = 10; function f1() {return x;}",
2 * kPointerSize,
1,
24,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(1), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
B(CreateClosure), U8(0), U8(0), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(StaContextSlot), R(context), U8(4), //
B(LdaUndefined), //
B(Return) //
},
1,
{InstanceType::SHARED_FUNCTION_INFO_TYPE}},
{"const x = 10; function f1() {return x;} return x;",
3 * kPointerSize,
1,
37,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(1), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
B(CreateClosure), U8(0), U8(0), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(StaContextSlot), R(context), U8(4), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1), //
B(Return) //
},
2,
{InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"const x = (x = 20); function f1() {return x;}",
4 * kPointerSize,
1,
50,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
/* */ U8(1), //
B(PushContext), R(1), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
B(CreateClosure), U8(0), U8(0), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(20), //
B(Star), R(2), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(3), U8(1), //
B(CallRuntime), U16(Runtime::kThrowConstAssignError), R(0), //
U8(0), //
B(Ldar), R(2), //
B(StaContextSlot), R(context), U8(4), //
B(StaContextSlot), R(context), U8(4), //
B(LdaUndefined), //
B(Return) //
},
2,
{InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"const x = 10; x = 20; function f1() {return x;}",
4 * kPointerSize,
1,
52,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
/* */ U8(1), //
B(PushContext), R(1), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
B(CreateClosure), U8(0), U8(0), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(StaContextSlot), R(context), U8(4), //
B(LdaSmi8), U8(20), //
B(Star), R(2), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(3), U8(1), //
B(CallRuntime), U16(Runtime::kThrowConstAssignError), R(0), //
U8(0), //
B(Ldar), R(2), //
B(StaContextSlot), R(context), U8(4), //
B(LdaUndefined), //
B(Return) //
},
2,
{InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
};
// clang-format on
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(LetVariableContextSlot) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
int closure = Register::function_closure().index();
int context = Register::current_context().index();
// clang-format off
ExpectedSnippet<InstanceType> snippets[] = {
{"let x = 10; function f1() {return x;}",
2 * kPointerSize,
1,
24,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
/* */ U8(1), //
B(PushContext), R(1), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
B(CreateClosure), U8(0), U8(0), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(StaContextSlot), R(context), U8(4), //
B(LdaUndefined), //
B(Return) //
},
1,
{InstanceType::SHARED_FUNCTION_INFO_TYPE}},
{"let x = 10; function f1() {return x;} return x;",
3 * kPointerSize,
1,
37,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
/* */ U8(1), //
B(PushContext), R(1), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
B(CreateClosure), U8(0), U8(0), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(StaContextSlot), R(context), U8(4), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1), //
B(Return) //
},
2,
{InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"let x = (x = 20); function f1() {return x;}",
4 * kPointerSize,
1,
45,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
/* */ U8(1), //
B(PushContext), R(1), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
B(CreateClosure), U8(0), U8(0), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(20), //
B(Star), R(2), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(3), U8(1), //
B(Ldar), R(2), //
B(StaContextSlot), R(context), U8(4), //
B(StaContextSlot), R(context), U8(4), //
B(LdaUndefined), //
B(Return) //
},
2,
{InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"let x = 10; x = 20; function f1() {return x;}",
4 * kPointerSize,
1,
47,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
/* */ U8(1), //
B(PushContext), R(1), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
B(CreateClosure), U8(0), U8(0), //
B(Star), R(0), //
B(StackCheck), //
B(LdaSmi8), U8(10), //
B(StaContextSlot), R(context), U8(4), //
B(LdaSmi8), U8(20), //
B(Star), R(2), //
B(LdaContextSlot), R(context), U8(4), //
B(JumpIfNotHole), U8(11), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(3), U8(1), //
B(Ldar), R(2), //
B(StaContextSlot), R(context), U8(4), //
B(LdaUndefined), //
B(Return) //
},
2,
{InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
};
// clang-format on
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(DoExpression) { TEST(DoExpression) {
bool old_flag = FLAG_harmony_do_expressions; bool old_flag = FLAG_harmony_do_expressions;
FLAG_harmony_do_expressions = true; FLAG_harmony_do_expressions = true;
......
...@@ -96,6 +96,18 @@ class InterpreterTester { ...@@ -96,6 +96,18 @@ class InterpreterTester {
return InterpreterCallable<A...>(isolate_, GetBytecodeFunction<A...>()); return InterpreterCallable<A...>(isolate_, GetBytecodeFunction<A...>());
} }
Local<Message> CheckThrowsReturnMessage() {
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate_));
auto callable = GetCallable<>();
MaybeHandle<Object> no_result = callable();
CHECK(isolate_->has_pending_exception());
CHECK(try_catch.HasCaught());
CHECK(no_result.is_null());
isolate_->OptionalRescheduleException(true);
CHECK(!try_catch.Message().IsEmpty());
return try_catch.Message();
}
static Handle<Object> NewObject(const char* script) { static Handle<Object> NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script)); return v8::Utils::OpenHandle(*CompileRun(script));
} }
...@@ -3853,6 +3865,244 @@ TEST(InterpreterClassLiterals) { ...@@ -3853,6 +3865,244 @@ TEST(InterpreterClassLiterals) {
} }
} }
TEST(InterpreterConstDeclaration) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> const_decl[] = {
{"const x = 3; return x;", handle(Smi::FromInt(3), isolate)},
{"let x = 10; x = x + 20; return x;", handle(Smi::FromInt(30), isolate)},
{"let x = 10; x = 20; return x;", handle(Smi::FromInt(20), isolate)},
{"let x; x = 20; return x;", handle(Smi::FromInt(20), isolate)},
{"let x; return x;", factory->undefined_value()},
{"var x = 10; { let x = 30; } return x;",
handle(Smi::FromInt(10), isolate)},
{"let x = 10; { let x = 20; } return x;",
handle(Smi::FromInt(10), isolate)},
{"var x = 10; eval('let x = 20;'); return x;",
handle(Smi::FromInt(10), isolate)},
{"var x = 10; eval('const x = 20;'); return x;",
handle(Smi::FromInt(10), isolate)},
{"var x = 10; { const x = 20; } return x;",
handle(Smi::FromInt(10), isolate)},
{"var x = 10; { const x = 20; return x;} return -1;",
handle(Smi::FromInt(20), isolate)},
{"var a = 10;\n"
"for (var i = 0; i < 10; ++i) {\n"
" const x = i;\n" // const declarations are block scoped.
" a = a + x;\n"
"}\n"
"return a;\n",
handle(Smi::FromInt(55), isolate)},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string source(InterpreterTester::SourceForBody(const_decl[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string strict_body =
"'use strict'; " + std::string(const_decl[i].first);
std::string source(InterpreterTester::SourceForBody(strict_body.c_str()));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
}
TEST(InterpreterConstDeclarationLookupSlots) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> const_decl[] = {
{"const x = 3; function f1() {return x;}; return x;",
handle(Smi::FromInt(3), isolate)},
{"let x = 10; x = x + 20; function f1() {return x;}; return x;",
handle(Smi::FromInt(30), isolate)},
{"let x; x = 20; function f1() {return x;}; return x;",
handle(Smi::FromInt(20), isolate)},
{"let x; function f1() {return x;}; return x;",
factory->undefined_value()},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string source(InterpreterTester::SourceForBody(const_decl[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string strict_body =
"'use strict'; " + std::string(const_decl[i].first);
std::string source(InterpreterTester::SourceForBody(strict_body.c_str()));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
}
TEST(InterpreterConstInLookupContextChain) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
const char* prologue =
"function OuterMost() {\n"
" const outerConst = 10;\n"
" let outerLet = 20;\n"
" function Outer() {\n"
" function Inner() {\n"
" this.innerFunc = function() { ";
const char* epilogue =
" }\n"
" }\n"
" this.getInnerFunc ="
" function() {return new Inner().innerFunc;}\n"
" }\n"
" this.getOuterFunc ="
" function() {return new Outer().getInnerFunc();}"
"}\n"
"var f = new OuterMost().getOuterFunc();\n"
"f();\n";
std::pair<const char*, Handle<Object>> const_decl[] = {
{"return outerConst;", handle(Smi::FromInt(10), isolate)},
{"return outerLet;", handle(Smi::FromInt(20), isolate)},
{"outerLet = 30; return outerLet;", handle(Smi::FromInt(30), isolate)},
{"var outerLet = 40; return outerLet;",
handle(Smi::FromInt(40), isolate)},
{"var outerConst = 50; return outerConst;",
handle(Smi::FromInt(50), isolate)},
{"try { outerConst = 30 } catch(e) { return -1; }",
handle(Smi::FromInt(-1), isolate)}};
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string script = std::string(prologue) +
std::string(const_decl[i].first) +
std::string(epilogue);
InterpreterTester tester(handles.main_isolate(), script.c_str(), "*");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
// Tests for Legacy constant.
bool old_flag_legacy_const = FLAG_legacy_const;
FLAG_legacy_const = true;
std::pair<const char*, Handle<Object>> legacy_const_decl[] = {
{"return outerConst = 23;", handle(Smi::FromInt(23), isolate)},
{"outerConst = 30; return outerConst;",
handle(Smi::FromInt(10), isolate)},
};
for (size_t i = 0; i < arraysize(legacy_const_decl); i++) {
std::string script = std::string(prologue) +
std::string(legacy_const_decl[i].first) +
std::string(epilogue);
InterpreterTester tester(handles.main_isolate(), script.c_str(), "*");
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*legacy_const_decl[i].second));
}
FLAG_legacy_const = old_flag_legacy_const;
}
TEST(InterpreterIllegalConstDeclaration) {
HandleAndZoneScope handles;
std::pair<const char*, const char*> const_decl[] = {
{"const x = x = 10 + 3; return x;",
"Uncaught ReferenceError: x is not defined"},
{"const x = 10; x = 20; return x;",
"Uncaught TypeError: Assignment to constant variable."},
{"const x = 10; { x = 20; } return x;",
"Uncaught TypeError: Assignment to constant variable."},
{"const x = 10; eval('x = 20;'); return x;",
"Uncaught TypeError: Assignment to constant variable."},
{"let x = x + 10; return x;",
"Uncaught ReferenceError: x is not defined"},
{"'use strict'; (function f1() { f1 = 123; })() ",
"Uncaught TypeError: Assignment to constant variable."},
};
// Tests for sloppy mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string source(InterpreterTester::SourceForBody(const_decl[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
v8::Local<v8::String> message = tester.CheckThrowsReturnMessage()->Get();
v8::Local<v8::String> expected_string = v8_str(const_decl[i].second);
CHECK(
message->Equals(CcTest::isolate()->GetCurrentContext(), expected_string)
.FromJust());
}
// Tests for strict mode.
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string strict_body =
"'use strict'; " + std::string(const_decl[i].first);
std::string source(InterpreterTester::SourceForBody(strict_body.c_str()));
InterpreterTester tester(handles.main_isolate(), source.c_str());
v8::Local<v8::String> message = tester.CheckThrowsReturnMessage()->Get();
v8::Local<v8::String> expected_string = v8_str(const_decl[i].second);
CHECK(
message->Equals(CcTest::isolate()->GetCurrentContext(), expected_string)
.FromJust());
}
}
TEST(InterpreterLegacyConstDeclaration) {
bool old_flag_legacy_const = FLAG_legacy_const;
FLAG_legacy_const = true;
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> const_decl[] = {
{"const x = (x = 10) + 3; return x;", handle(Smi::FromInt(13), isolate)},
{"const x = 10; x = 20; return x;", handle(Smi::FromInt(10), isolate)},
{"var a = 10;\n"
"for (var i = 0; i < 10; ++i) {\n"
" const x = i;\n" // Legacy constants are not block scoped.
" a = a + x;\n"
"}\n"
"return a;\n",
handle(Smi::FromInt(10), isolate)},
{"const x = 20; eval('x = 10;'); return x;",
handle(Smi::FromInt(20), isolate)},
};
for (size_t i = 0; i < arraysize(const_decl); i++) {
std::string source(InterpreterTester::SourceForBody(const_decl[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*const_decl[i].second));
}
FLAG_legacy_const = old_flag_legacy_const;
}
} // namespace interpreter } // namespace interpreter
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -747,7 +747,6 @@ ...@@ -747,7 +747,6 @@
}], # 'arch == ppc and simulator_run == True' }], # 'arch == ppc and simulator_run == True'
['ignition == True', { ['ignition == True', {
'const*': [SKIP],
'debug-*': [SKIP], 'debug-*': [SKIP],
'es6/*': [SKIP], 'es6/*': [SKIP],
'es7/*': [SKIP], 'es7/*': [SKIP],
...@@ -771,20 +770,14 @@ ...@@ -771,20 +770,14 @@
'compiler/deopt-tonumber-compare': [SKIP], 'compiler/deopt-tonumber-compare': [SKIP],
'compiler/expression-trees': [SKIP], 'compiler/expression-trees': [SKIP],
'compiler/regress-446647': [SKIP], 'compiler/regress-446647': [SKIP],
'compiler/regress-447567': [SKIP],
'compiler/regress-96989': [SKIP],
'compiler/regress-const': [SKIP],
'compiler/regress-funarguments': [SKIP], 'compiler/regress-funarguments': [SKIP],
'compiler/regress-stacktrace-methods': [SKIP], 'compiler/regress-stacktrace-methods': [SKIP],
'compiler/strict-recompile': [SKIP], 'compiler/strict-recompile': [SKIP],
'cyclic-array-to-string': [SKIP], 'cyclic-array-to-string': [SKIP],
'd8-worker-sharedarraybuffer': [SKIP], 'd8-worker-sharedarraybuffer': [SKIP],
'declare-locally': [SKIP],
'deserialize-optimize-inner': [SKIP], 'deserialize-optimize-inner': [SKIP],
'eval-enclosing-function-name': [SKIP],
'eval-stack-trace': [SKIP], 'eval-stack-trace': [SKIP],
'field-type-tracking': [SKIP], 'field-type-tracking': [SKIP],
'global-const-var-conflicts': [SKIP],
'global-hash': [SKIP], 'global-hash': [SKIP],
'messages': [SKIP], 'messages': [SKIP],
'property-load-across-eval': [SKIP], 'property-load-across-eval': [SKIP],
...@@ -796,7 +789,6 @@ ...@@ -796,7 +789,6 @@
'regress/regress-109195': [SKIP], 'regress/regress-109195': [SKIP],
'regress/regress-1114040': [SKIP], 'regress/regress-1114040': [SKIP],
'regress/regress-1170187': [SKIP], 'regress/regress-1170187': [SKIP],
'regress/regress-1178598': [SKIP],
'regress/regress-119609': [SKIP], 'regress/regress-119609': [SKIP],
'regress/regress-1199637': [SKIP], 'regress/regress-1199637': [SKIP],
'regress/regress-1200351': [SKIP], 'regress/regress-1200351': [SKIP],
...@@ -812,7 +804,6 @@ ...@@ -812,7 +804,6 @@
'regress/regress-1639': [SKIP], 'regress/regress-1639': [SKIP],
'regress/regress-1790': [SKIP], 'regress/regress-1790': [SKIP],
'regress/regress-1853': [SKIP], 'regress/regress-1853': [SKIP],
'regress/regress-186': [SKIP],
'regress/regress-1980': [SKIP], 'regress/regress-1980': [SKIP],
'regress/regress-2318': [SKIP], 'regress/regress-2318': [SKIP],
'regress/regress-2618': [SKIP], 'regress/regress-2618': [SKIP],
...@@ -822,7 +813,6 @@ ...@@ -822,7 +813,6 @@
'regress/regress-2825': [SKIP], 'regress/regress-2825': [SKIP],
'regress/regress-353551': [SKIP], 'regress/regress-353551': [SKIP],
'regress/regress-354357': [SKIP], 'regress/regress-354357': [SKIP],
'regress/regress-3926': [SKIP],
'regress/regress-3960': [SKIP], 'regress/regress-3960': [SKIP],
'regress/regress-3969': [SKIP], 'regress/regress-3969': [SKIP],
'regress/regress-3985': [SKIP], 'regress/regress-3985': [SKIP],
...@@ -837,7 +827,6 @@ ...@@ -837,7 +827,6 @@
'regress/regress-4309-3': [SKIP], 'regress/regress-4309-3': [SKIP],
'regress/regress-4320': [SKIP], 'regress/regress-4320': [SKIP],
'regress/regress-4374': [SKIP], 'regress/regress-4374': [SKIP],
'regress/regress-4388': [SKIP],
'regress/regress-446389': [SKIP], 'regress/regress-446389': [SKIP],
'regress/regress-447756': [SKIP], 'regress/regress-447756': [SKIP],
'regress/regress-4509-Class-constructor-typeerror-realm' : [SKIP], 'regress/regress-4509-Class-constructor-typeerror-realm' : [SKIP],
...@@ -854,7 +843,6 @@ ...@@ -854,7 +843,6 @@
'regress/regress-544991': [SKIP], 'regress/regress-544991': [SKIP],
'regress/regress-572589': [SKIP], 'regress/regress-572589': [SKIP],
'regress/regress-799761': [SKIP], 'regress/regress-799761': [SKIP],
'regress/regress-88591': [SKIP],
'regress/regress-94873': [SKIP], 'regress/regress-94873': [SKIP],
'regress/regress-97116b': [SKIP], 'regress/regress-97116b': [SKIP],
'regress/regress-97116': [SKIP], 'regress/regress-97116': [SKIP],
......
...@@ -564,7 +564,6 @@ ...@@ -564,7 +564,6 @@
'language/expressions/object/method-definition/generator*': [SKIP], 'language/expressions/object/method-definition/generator*': [SKIP],
'language/expressions/yield/*': [SKIP], 'language/expressions/yield/*': [SKIP],
'language/statements/class/*': [SKIP], 'language/statements/class/*': [SKIP],
'language/statements/const/*': [SKIP],
'language/statements/generators/*': [SKIP], 'language/statements/generators/*': [SKIP],
'built-ins/Array/prototype/concat/Array.prototype.concat_non-array': [SKIP], 'built-ins/Array/prototype/concat/Array.prototype.concat_non-array': [SKIP],
...@@ -602,15 +601,12 @@ ...@@ -602,15 +601,12 @@
'language/computed-property-names/object/method/super': [SKIP], 'language/computed-property-names/object/method/super': [SKIP],
'language/default-parameters/class-definitions': [SKIP], 'language/default-parameters/class-definitions': [SKIP],
'language/default-parameters/generators': [SKIP], 'language/default-parameters/generators': [SKIP],
'language/default-parameters/param-ref-uninitialized': [SKIP],
'language/expressions/object/method-definition/name-prop-name-yield-expr': [SKIP], 'language/expressions/object/method-definition/name-prop-name-yield-expr': [SKIP],
'language/expressions/object/method-definition/name-super-prop-param': [SKIP], 'language/expressions/object/method-definition/name-super-prop-param': [SKIP],
'language/expressions/object/method-definition/name-super-prop-body': [SKIP], 'language/expressions/object/method-definition/name-super-prop-body': [SKIP],
'language/expressions/tagged-template/call-expression-context-no-strict': [SKIP], 'language/expressions/tagged-template/call-expression-context-no-strict': [SKIP],
'language/expressions/tagged-template/call-expression-context-strict': [SKIP], 'language/expressions/tagged-template/call-expression-context-strict': [SKIP],
'language/expressions/template-literal/evaluation-order': [SKIP], 'language/expressions/template-literal/evaluation-order': [SKIP],
'language/statements/for-in/const-bound-names-fordecl-tdz-for-in': [SKIP],
'language/statements/for-in/let-bound-names-fordecl-tdz-for-in': [SKIP],
'language/statements/for-of/body-dstr-assign': [SKIP], 'language/statements/for-of/body-dstr-assign': [SKIP],
'language/statements/for-of/break': [SKIP], 'language/statements/for-of/break': [SKIP],
'language/statements/for-of/break-from-catch': [SKIP], 'language/statements/for-of/break-from-catch': [SKIP],
...@@ -620,7 +616,6 @@ ...@@ -620,7 +616,6 @@
'language/statements/for-of/break-label-from-catch': [SKIP], 'language/statements/for-of/break-label-from-catch': [SKIP],
'language/statements/for-of/break-label-from-finally': [SKIP], 'language/statements/for-of/break-label-from-finally': [SKIP],
'language/statements/for-of/break-label-from-try': [SKIP], 'language/statements/for-of/break-label-from-try': [SKIP],
'language/statements/for-of/const-bound-names-fordecl-tdz-for-of': [SKIP],
'language/statements/for-of/continue': [SKIP], 'language/statements/for-of/continue': [SKIP],
'language/statements/for-of/continue-from-catch': [SKIP], 'language/statements/for-of/continue-from-catch': [SKIP],
'language/statements/for-of/continue-from-finally': [SKIP], 'language/statements/for-of/continue-from-finally': [SKIP],
...@@ -631,7 +626,6 @@ ...@@ -631,7 +626,6 @@
'language/statements/for-of/continue-label-from-try': [SKIP], 'language/statements/for-of/continue-label-from-try': [SKIP],
'language/statements/for-of/generator': [SKIP], 'language/statements/for-of/generator': [SKIP],
'language/statements/for-of/generator-next-error': [SKIP], 'language/statements/for-of/generator-next-error': [SKIP],
'language/statements/for-of/let-bound-names-fordecl-tdz-for-of': [SKIP],
'language/statements/for-of/nested': [SKIP], 'language/statements/for-of/nested': [SKIP],
'language/statements/for-of/return': [SKIP], 'language/statements/for-of/return': [SKIP],
'language/statements/for-of/return-from-catch': [SKIP], 'language/statements/for-of/return-from-catch': [SKIP],
...@@ -654,18 +648,6 @@ ...@@ -654,18 +648,6 @@
'language/object-literal/setter': [SKIP], 'language/object-literal/setter': [SKIP],
'language/rest-parameters/with-new-target': [SKIP], 'language/rest-parameters/with-new-target': [SKIP],
'language/statements/do-while/S12.6.1_A4_T5': [SKIP], 'language/statements/do-while/S12.6.1_A4_T5': [SKIP],
'language/statements/let/block-local-closure-get-before-initialization': [SKIP],
'language/statements/let/block-local-closure-set-before-initialization': [SKIP],
'language/statements/let/block-local-use-before-initialization-in-declaration-statement': [SKIP],
'language/statements/let/block-local-use-before-initialization-in-prior-statement': [SKIP],
'language/statements/let/function-local-closure-get-before-initialization': [SKIP],
'language/statements/let/function-local-closure-set-before-initialization': [SKIP],
'language/statements/let/function-local-use-before-initialization-in-declaration-statement': [SKIP],
'language/statements/let/function-local-use-before-initialization-in-prior-statement': [SKIP],
'language/statements/let/global-closure-get-before-initialization': [SKIP],
'language/statements/let/global-closure-set-before-initialization': [SKIP],
'language/statements/let/global-use-before-initialization-in-declaration-statement': [SKIP],
'language/statements/let/global-use-before-initialization-in-prior-statement': [SKIP],
'language/statements/while/S12.6.2_A4_T5': [SKIP], 'language/statements/while/S12.6.2_A4_T5': [SKIP],
}], # ignition == True }], # ignition == True
......
...@@ -165,7 +165,11 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -165,7 +165,11 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
BytecodeLabel start; BytecodeLabel start;
builder.Bind(&start); builder.Bind(&start);
// Short jumps with Imm8 operands // Short jumps with Imm8 operands
builder.Jump(&start).JumpIfNull(&start).JumpIfUndefined(&start); builder.Jump(&start)
.JumpIfNull(&start)
.JumpIfUndefined(&start)
.JumpIfHole(&start)
.JumpIfNotHole(&start);
// Perform an operation that returns boolean value to // Perform an operation that returns boolean value to
// generate JumpIfTrue/False // generate JumpIfTrue/False
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK) builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment