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() {
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() {
FrameStateBeforeAndAfter states(this);
Node* node = NewNode(javascript()->StackCheck());
......
......@@ -661,9 +661,8 @@ Node* InterpreterAssembler::Advance(Node* 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;
Node* condition = raw_assembler_->WordEqual(lhs, rhs);
raw_assembler_->Branch(condition, &match, &no_match);
raw_assembler_->Bind(&match);
DispatchTo(Advance(delta));
......@@ -671,6 +670,14 @@ void InterpreterAssembler::JumpIfWordEqual(Node* lhs, Node* rhs, Node* delta) {
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() {
DispatchTo(Advance(interpreter::Bytecodes::Size(bytecode_)));
......
......@@ -147,10 +147,19 @@ class InterpreterAssembler {
// Jump relative to the current bytecode by |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
// word values |lhs| and |rhs| are equal.
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.
void StackCheck();
......
......@@ -807,6 +807,8 @@ Bytecode BytecodeArrayBuilder::GetJumpWithToBoolean(Bytecode jump_bytecode) {
case Bytecode::kJump:
case Bytecode::kJumpIfNull:
case Bytecode::kJumpIfUndefined:
case Bytecode::kJumpIfHole:
case Bytecode::kJumpIfNotHole:
return jump_bytecode;
case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfToBooleanTrue;
......@@ -972,6 +974,15 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck() {
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() {
Output(Bytecode::kThrow);
exit_seen_in_block_ = true;
......
......@@ -231,6 +231,8 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
BytecodeArrayBuilder& JumpIfFalse(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfHole(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfNotHole(BytecodeLabel* label);
BytecodeArrayBuilder& StackCheck();
......
......@@ -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);
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);
}
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);
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.
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());
}
......
......@@ -76,7 +76,13 @@ class BytecodeGenerator final : public AstVisitor {
MUST_USE_RESULT Register
VisitVariableLoadForRegisterValue(Variable* variable, FeedbackVectorSlot slot,
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 VisitRestArgumentsArray(Variable* rest);
......
......@@ -196,7 +196,9 @@ bool Bytecodes::IsConditionalJumpImmediate(Bytecode bytecode) {
bytecode == Bytecode::kJumpIfToBooleanTrue ||
bytecode == Bytecode::kJumpIfToBooleanFalse ||
bytecode == Bytecode::kJumpIfNull ||
bytecode == Bytecode::kJumpIfUndefined;
bytecode == Bytecode::kJumpIfUndefined ||
bytecode == Bytecode::kJumpIfHole ||
bytecode == Bytecode::kJumpIfNotHole;
}
......
......@@ -252,6 +252,9 @@ namespace interpreter {
V(JumpIfUndefined, OperandType::kImm8) \
V(JumpIfUndefinedConstant, OperandType::kIdx8) \
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 */ \
V(ForInPrepare, OperandType::kRegOutTriple8) \
......
......@@ -1605,8 +1605,7 @@ void Interpreter::DoJumpIfNullConstantWide(
DoJumpIfNullConstant(assembler);
}
// jumpifundefined <imm8>
// JumpIfUndefined <imm8>
//
// Jump by number of bytes represented by an immediate operand if the object
// referenced by the accumulator is the undefined constant.
......@@ -1644,6 +1643,27 @@ void Interpreter::DoJumpIfUndefinedConstantWide(
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,
compiler::InterpreterAssembler* assembler) {
......
......@@ -575,14 +575,6 @@
'test-api/InterceptorShouldThrowOnError': [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
'test-api/SetFunctionEntryHook': [FAIL],
......@@ -640,7 +632,6 @@
'test-heap/CompilationCacheCachingBehavior': [FAIL],
'test-heap/CellsInOptimizedCodeAreWeak': [FAIL],
'test-run-inlining/InlineTwice': [FAIL],
'test-decls/Regress425510': [FAIL],
# TODO(rmcilroy,4680): Fail on some bot configurations.
'test-heap/CanonicalSharedFunctionInfo': [PASS, FAIL],
......
......@@ -2649,7 +2649,6 @@ TEST(JumpWithConstantsAndWideConstants) {
TEST(BytecodeGraphBuilderDoExpressions) {
bool old_flag = FLAG_harmony_do_expressions;
FLAG_harmony_do_expressions = true;
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
......@@ -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) {
FLAG_expose_debug_as = "debug";
HandleAndZoneScope scope;
......
......@@ -29,7 +29,6 @@ class BytecodeGeneratorHelper {
i::FLAG_ignition_filter = StrDup(kFunctionName);
i::FLAG_always_opt = false;
i::FLAG_allow_natives_syntax = true;
i::FLAG_legacy_const = true;
CcTest::i_isolate()->interpreter()->Initialize();
}
......@@ -2539,7 +2538,7 @@ TEST(BreakableBlocks) {
"}\n",
5 * kPointerSize,
1,
40,
51,
{
B(StackCheck), //
B(LdaConstant), U8(0), //
......@@ -2555,15 +2554,19 @@ TEST(BreakableBlocks) {
B(LdaSmi8), U8(10), //
B(StaContextSlot), R(context), U8(4), //
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(Jump), U8(2), //
B(PopContext), R(2), //
B(LdaUndefined), //
B(Return), //
},
2,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
3,
{InstanceType::FIXED_ARRAY_TYPE, InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"let x = 1;\n"
"outer: {\n"
" inner: {\n"
......@@ -2576,10 +2579,10 @@ TEST(BreakableBlocks) {
"x = 4;",
6 * kPointerSize,
1,
73,
131,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
/* */ U8(1), //
U8(1), //
B(PushContext), R(2), //
B(LdaTheHole), //
B(StaContextSlot), R(context), U8(4), //
......@@ -2599,22 +2602,46 @@ TEST(BreakableBlocks) {
B(LdaSmi8), U8(2), //
B(StaContextSlot), R(context), U8(4), //
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(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(PopContext), R(3), //
B(Jump), U8(9), //
B(Jump), U8(27), //
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(PopContext), R(3), //
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(LdaUndefined), //
B(Return), //
},
2,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
5,
{InstanceType::FIXED_ARRAY_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
......@@ -3289,15 +3316,15 @@ TEST(BasicLoops) {
" z++;\n"
" }\n"
"}\n",
6 * kPointerSize,
7 * kPointerSize,
1,
67,
118,
{
B(StackCheck), //
B(LdaZero), //
B(Star), R(1), //
B(Ldar), R(1), //
B(JumpIfToBooleanFalse), U8(59), //
B(JumpIfToBooleanFalse), U8(110), //
B(StackCheck), //
B(LdaConstant), U8(0), //
B(Star), R(4), //
......@@ -3312,24 +3339,44 @@ TEST(BasicLoops) {
B(LdaSmi8), U8(1), //
B(StaContextSlot), R(context), U8(4), //
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(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(PopContext), R(3), //
B(Jump), U8(-45), //
B(Jump), U8(-67), //
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(Star), R(4), //
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(PopContext), R(3), //
B(Jump), U8(-59), //
B(Jump), U8(-110), //
B(LdaUndefined), //
B(Return), //
},
2,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
4,
{InstanceType::FIXED_ARRAY_TYPE, InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
};
// clang-format on
......@@ -6105,6 +6152,9 @@ TEST(CreateRestParameter) {
}
TEST(IllegalRedeclaration) {
bool old_legacy_const_flag = FLAG_legacy_const;
FLAG_legacy_const = true;
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
......@@ -6136,6 +6186,8 @@ TEST(IllegalRedeclaration) {
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
FLAG_legacy_const = old_legacy_const_flag;
}
......@@ -7094,28 +7146,39 @@ TEST(ThisFunction) {
// clang-format off
ExpectedSnippet<int> snippets[] = {
{"var f;\n f = function f() { }",
1 * kPointerSize,
2 * kPointerSize,
1,
10,
19,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
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(Return), //
}},
{"var f;\n f = function f() { return f; }",
1 * kPointerSize,
2 * kPointerSize,
1,
9,
23,
{
B(LdaTheHole), //
B(Star), R(0), //
B(StackCheck), //
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), //
}},
};
......@@ -7136,31 +7199,42 @@ TEST(NewTarget) {
int new_target = Register::new_target().index();
// clang-format off
ExpectedSnippet<int> snippets[] = {
ExpectedSnippet<InstanceType> snippets[] = {
{"return new.target;",
1 * kPointerSize,
2 * kPointerSize,
1,
8,
19,
{
B(Ldar), R(new_target), //
B(Star), R(0), //
B(StackCheck), //
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), //
}},
},
1,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"new.target;",
1 * kPointerSize,
2 * kPointerSize,
1,
9,
20,
{
B(Ldar), R(new_target), //
B(Star), R(0), //
B(StackCheck), //
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(Return), //
}},
};
},
1,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}};
// clang-format on
for (size_t i = 0; i < arraysize(snippets); i++) {
......@@ -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) {
bool old_flag = FLAG_harmony_do_expressions;
FLAG_harmony_do_expressions = true;
......
......@@ -96,6 +96,18 @@ class InterpreterTester {
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) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
......@@ -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 internal
} // namespace v8
......@@ -747,7 +747,6 @@
}], # 'arch == ppc and simulator_run == True'
['ignition == True', {
'const*': [SKIP],
'debug-*': [SKIP],
'es6/*': [SKIP],
'es7/*': [SKIP],
......@@ -771,20 +770,14 @@
'compiler/deopt-tonumber-compare': [SKIP],
'compiler/expression-trees': [SKIP],
'compiler/regress-446647': [SKIP],
'compiler/regress-447567': [SKIP],
'compiler/regress-96989': [SKIP],
'compiler/regress-const': [SKIP],
'compiler/regress-funarguments': [SKIP],
'compiler/regress-stacktrace-methods': [SKIP],
'compiler/strict-recompile': [SKIP],
'cyclic-array-to-string': [SKIP],
'd8-worker-sharedarraybuffer': [SKIP],
'declare-locally': [SKIP],
'deserialize-optimize-inner': [SKIP],
'eval-enclosing-function-name': [SKIP],
'eval-stack-trace': [SKIP],
'field-type-tracking': [SKIP],
'global-const-var-conflicts': [SKIP],
'global-hash': [SKIP],
'messages': [SKIP],
'property-load-across-eval': [SKIP],
......@@ -796,7 +789,6 @@
'regress/regress-109195': [SKIP],
'regress/regress-1114040': [SKIP],
'regress/regress-1170187': [SKIP],
'regress/regress-1178598': [SKIP],
'regress/regress-119609': [SKIP],
'regress/regress-1199637': [SKIP],
'regress/regress-1200351': [SKIP],
......@@ -812,7 +804,6 @@
'regress/regress-1639': [SKIP],
'regress/regress-1790': [SKIP],
'regress/regress-1853': [SKIP],
'regress/regress-186': [SKIP],
'regress/regress-1980': [SKIP],
'regress/regress-2318': [SKIP],
'regress/regress-2618': [SKIP],
......@@ -822,7 +813,6 @@
'regress/regress-2825': [SKIP],
'regress/regress-353551': [SKIP],
'regress/regress-354357': [SKIP],
'regress/regress-3926': [SKIP],
'regress/regress-3960': [SKIP],
'regress/regress-3969': [SKIP],
'regress/regress-3985': [SKIP],
......@@ -837,7 +827,6 @@
'regress/regress-4309-3': [SKIP],
'regress/regress-4320': [SKIP],
'regress/regress-4374': [SKIP],
'regress/regress-4388': [SKIP],
'regress/regress-446389': [SKIP],
'regress/regress-447756': [SKIP],
'regress/regress-4509-Class-constructor-typeerror-realm' : [SKIP],
......@@ -854,7 +843,6 @@
'regress/regress-544991': [SKIP],
'regress/regress-572589': [SKIP],
'regress/regress-799761': [SKIP],
'regress/regress-88591': [SKIP],
'regress/regress-94873': [SKIP],
'regress/regress-97116b': [SKIP],
'regress/regress-97116': [SKIP],
......
......@@ -564,7 +564,6 @@
'language/expressions/object/method-definition/generator*': [SKIP],
'language/expressions/yield/*': [SKIP],
'language/statements/class/*': [SKIP],
'language/statements/const/*': [SKIP],
'language/statements/generators/*': [SKIP],
'built-ins/Array/prototype/concat/Array.prototype.concat_non-array': [SKIP],
......@@ -602,15 +601,12 @@
'language/computed-property-names/object/method/super': [SKIP],
'language/default-parameters/class-definitions': [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-super-prop-param': [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-strict': [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/break': [SKIP],
'language/statements/for-of/break-from-catch': [SKIP],
......@@ -620,7 +616,6 @@
'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-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-from-catch': [SKIP],
'language/statements/for-of/continue-from-finally': [SKIP],
......@@ -631,7 +626,6 @@
'language/statements/for-of/continue-label-from-try': [SKIP],
'language/statements/for-of/generator': [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/return': [SKIP],
'language/statements/for-of/return-from-catch': [SKIP],
......@@ -654,18 +648,6 @@
'language/object-literal/setter': [SKIP],
'language/rest-parameters/with-new-target': [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],
}], # ignition == True
......
......@@ -165,7 +165,11 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
BytecodeLabel start;
builder.Bind(&start);
// 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
// generate JumpIfTrue/False
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