[Interpreter] Add switch support.

Adds support for switch statments to the interpreter.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#31687}
This commit is contained in:
rmcilroy 2015-10-30 05:55:05 -07:00 committed by Commit bot
parent 18b37c7f59
commit 40f8605ff9
7 changed files with 558 additions and 55 deletions

View File

@ -288,6 +288,8 @@ class BytecodeLabel final {
public:
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
INLINE(bool is_bound() const) { return bound_; }
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
@ -301,7 +303,6 @@ class BytecodeLabel final {
offset_ = offset;
}
INLINE(size_t offset() const) { return offset_; }
INLINE(bool is_bound() const) { return bound_; }
INLINE(bool is_forward_target() const) {
return offset() != kInvalidOffset && !is_bound();
}

View File

@ -136,6 +136,36 @@ class BytecodeGenerator::ControlScopeForIteration
};
// Scoped class for enabling 'break' in switch statements.
class BytecodeGenerator::ControlScopeForSwitch
: public BytecodeGenerator::ControlScope {
public:
ControlScopeForSwitch(BytecodeGenerator* generator,
SwitchStatement* statement,
SwitchBuilder* switch_builder)
: ControlScope(generator),
statement_(statement),
switch_builder_(switch_builder) {}
protected:
virtual bool Execute(Command command, Statement* statement) {
if (statement != statement_) return false;
switch (command) {
case CMD_BREAK:
switch_builder_->Break();
return true;
case CMD_CONTINUE:
break;
}
return false;
}
private:
Statement* statement_;
SwitchBuilder* switch_builder_;
};
void BytecodeGenerator::ControlScope::PerformCommand(Command command,
Statement* statement) {
ControlScope* current = this;
@ -539,11 +569,57 @@ void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) {
void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
UNIMPLEMENTED();
ZoneList<CaseClause*>* clauses = stmt->cases();
SwitchBuilder switch_builder(builder(), clauses->length());
ControlScopeForSwitch scope(this, stmt, &switch_builder);
int default_index = -1;
// Keep the switch value in a register until a case matches.
Register tag = VisitForRegisterValue(stmt->tag());
// Iterate over all cases and create nodes for label comparison.
BytecodeLabel done_label;
for (int i = 0; i < clauses->length(); i++) {
CaseClause* clause = clauses->at(i);
// The default is not a test, remember index.
if (clause->is_default()) {
default_index = i;
continue;
}
// Perform label comparison as if via '===' with tag.
VisitForAccumulatorValue(clause->label());
builder()->CompareOperation(Token::Value::EQ_STRICT, tag,
language_mode_strength());
switch_builder.Case(i);
}
if (default_index >= 0) {
// Emit default jump if there is a default case.
switch_builder.DefaultAt(default_index);
} else {
// Otherwise if we have reached here none of the cases matched, so jump to
// done.
builder()->Jump(&done_label);
}
// Iterate over all cases and create the case bodies.
for (int i = 0; i < clauses->length(); i++) {
CaseClause* clause = clauses->at(i);
switch_builder.SetCaseTarget(i);
VisitStatements(clause->statements());
}
builder()->Bind(&done_label);
switch_builder.SetBreakTarget(done_label);
}
void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCaseClause(CaseClause* clause) {
// Handled entirely in VisitSwitchStatement.
UNREACHABLE();
}
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {

View File

@ -31,6 +31,7 @@ class BytecodeGenerator : public AstVisitor {
class ContextScope;
class ControlScope;
class ControlScopeForIteration;
class ControlScopeForSwitch;
class ExpressionResultScope;
class EffectResultScope;
class AccumulatorResultScope;

View File

@ -9,48 +9,57 @@ namespace internal {
namespace interpreter {
LoopBuilder::~LoopBuilder() {
DCHECK(continue_sites_.empty());
BreakableControlFlowBuilder::~BreakableControlFlowBuilder() {
DCHECK(break_sites_.empty());
}
void LoopBuilder::SetContinueTarget(const BytecodeLabel& target) {
BindLabels(target, &continue_sites_);
}
void LoopBuilder::SetBreakTarget(const BytecodeLabel& target) {
void BreakableControlFlowBuilder::SetBreakTarget(const BytecodeLabel& target) {
BindLabels(target, &break_sites_);
}
void LoopBuilder::EmitJump(ZoneVector<BytecodeLabel>* sites) {
void BreakableControlFlowBuilder::EmitJump(ZoneVector<BytecodeLabel>* sites) {
sites->push_back(BytecodeLabel());
builder()->Jump(&sites->back());
}
void LoopBuilder::EmitJumpIfTrue(ZoneVector<BytecodeLabel>* sites) {
void BreakableControlFlowBuilder::EmitJumpIfTrue(
ZoneVector<BytecodeLabel>* sites) {
sites->push_back(BytecodeLabel());
builder()->JumpIfTrue(&sites->back());
}
void LoopBuilder::EmitJumpIfUndefined(ZoneVector<BytecodeLabel>* sites) {
void BreakableControlFlowBuilder::EmitJumpIfUndefined(
ZoneVector<BytecodeLabel>* sites) {
sites->push_back(BytecodeLabel());
builder()->JumpIfUndefined(&sites->back());
}
void LoopBuilder::EmitJumpIfNull(ZoneVector<BytecodeLabel>* sites) {
void BreakableControlFlowBuilder::EmitJumpIfNull(
ZoneVector<BytecodeLabel>* sites) {
sites->push_back(BytecodeLabel());
builder()->JumpIfNull(&sites->back());
}
void LoopBuilder::BindLabels(const BytecodeLabel& target,
ZoneVector<BytecodeLabel>* sites) {
void BreakableControlFlowBuilder::EmitJump(ZoneVector<BytecodeLabel>* sites,
int index) {
builder()->Jump(&sites->at(index));
}
void BreakableControlFlowBuilder::EmitJumpIfTrue(
ZoneVector<BytecodeLabel>* sites, int index) {
builder()->JumpIfTrue(&sites->at(index));
}
void BreakableControlFlowBuilder::BindLabels(const BytecodeLabel& target,
ZoneVector<BytecodeLabel>* sites) {
for (size_t i = 0; i < sites->size(); i++) {
BytecodeLabel& site = sites->at(i);
builder()->Bind(target, &site);
@ -58,6 +67,29 @@ void LoopBuilder::BindLabels(const BytecodeLabel& target,
sites->clear();
}
LoopBuilder::~LoopBuilder() { DCHECK(continue_sites_.empty()); }
void LoopBuilder::SetContinueTarget(const BytecodeLabel& target) {
BindLabels(target, &continue_sites_);
}
SwitchBuilder::~SwitchBuilder() {
#ifdef DEBUG
for (auto site : case_sites_) {
DCHECK(site.is_bound());
}
#endif
}
void SwitchBuilder::SetCaseTarget(int index) {
BytecodeLabel& site = case_sites_.at(index);
builder()->Bind(&site);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -28,46 +28,95 @@ class ControlFlowBuilder BASE_EMBEDDED {
DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder);
};
// A class to help with co-ordinating break and continue statements with
// their loop.
// TODO(oth): add support for TF branch/merge info.
class LoopBuilder : public ControlFlowBuilder {
class BreakableControlFlowBuilder : public ControlFlowBuilder {
public:
explicit LoopBuilder(BytecodeArrayBuilder* builder)
explicit BreakableControlFlowBuilder(BytecodeArrayBuilder* builder)
: ControlFlowBuilder(builder),
continue_sites_(builder->zone()),
break_sites_(builder->zone()) {}
~LoopBuilder();
virtual ~BreakableControlFlowBuilder();
// These methods should be called by the LoopBuilder owner before
// destruction to update sites that emit jumps for break/continue.
void SetContinueTarget(const BytecodeLabel& continue_target);
// This method should be called by the control flow owner before
// destruction to update sites that emit jumps for break.
void SetBreakTarget(const BytecodeLabel& break_target);
// These methods are called when visiting break and continue
// statements in the AST. Inserts a jump to a unbound label that is
// patched when the corresponding SetContinueTarget/SetBreakTarget
// is called.
// This method is called when visiting break statements in the AST.
// Inserts a jump to a unbound label that is patched when the corresponding
// SetBreakTarget is called.
void Break() { EmitJump(&break_sites_); }
void BreakIfTrue() { EmitJumpIfTrue(&break_sites_); }
void BreakIfUndefined() { EmitJumpIfUndefined(&break_sites_); }
void BreakIfNull() { EmitJumpIfNull(&break_sites_); }
protected:
void EmitJump(ZoneVector<BytecodeLabel>* labels);
void EmitJump(ZoneVector<BytecodeLabel>* labels, int index);
void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels);
void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels, int index);
void EmitJumpIfUndefined(ZoneVector<BytecodeLabel>* labels);
void EmitJumpIfNull(ZoneVector<BytecodeLabel>* labels);
void BindLabels(const BytecodeLabel& target, ZoneVector<BytecodeLabel>* site);
private:
// Unbound labels that identify jumps for break statements in the code.
ZoneVector<BytecodeLabel> break_sites_;
};
// A class to help with co-ordinating break and continue statements with
// their loop.
// TODO(oth): add support for TF branch/merge info.
class LoopBuilder final : public BreakableControlFlowBuilder {
public:
explicit LoopBuilder(BytecodeArrayBuilder* builder)
: BreakableControlFlowBuilder(builder),
continue_sites_(builder->zone()) {}
~LoopBuilder();
// This methods should be called by the LoopBuilder owner before
// destruction to update sites that emit jumps for continue.
void SetContinueTarget(const BytecodeLabel& continue_target);
// This method is called when visiting continue statements in the AST.
// Inserts a jump to a unbound label that is patched when the corresponding
// SetContinueTarget is called.
void Continue() { EmitJump(&continue_sites_); }
void ContinueIfTrue() { EmitJumpIfTrue(&continue_sites_); }
void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_sites_); }
void ContinueIfNull() { EmitJumpIfNull(&continue_sites_); }
private:
void BindLabels(const BytecodeLabel& target, ZoneVector<BytecodeLabel>* site);
void EmitJump(ZoneVector<BytecodeLabel>* labels);
void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels);
void EmitJumpIfUndefined(ZoneVector<BytecodeLabel>* labels);
void EmitJumpIfNull(ZoneVector<BytecodeLabel>* labels);
// Unbound labels that identify jumps for continue/break statements
// in the code.
// Unbound labels that identify jumps for continue statements in the code.
ZoneVector<BytecodeLabel> continue_sites_;
ZoneVector<BytecodeLabel> break_sites_;
};
// A class to help with co-ordinating break statements with their switch.
// TODO(oth): add support for TF branch/merge info.
class SwitchBuilder final : public BreakableControlFlowBuilder {
public:
explicit SwitchBuilder(BytecodeArrayBuilder* builder, int number_of_cases)
: BreakableControlFlowBuilder(builder),
case_sites_(builder->zone()) {
case_sites_.resize(number_of_cases);
}
~SwitchBuilder();
// This method should be called by the SwitchBuilder owner when the case
// statement with |index| is emitted to update the case jump site.
void SetCaseTarget(int index);
// This method is called when visiting case comparison operation for |index|.
// Inserts a JumpIfTrue to a unbound label that is patched when the
// corresponding SetCaseTarget is called.
void Case(int index) { EmitJumpIfTrue(&case_sites_, index); }
// This method is called when all cases comparisons have been emitted if there
// is a default case statement. Inserts a Jump to a unbound label that is
// patched when the corresponding SetCaseTarget is called.
void DefaultAt(int index) { EmitJump(&case_sites_, index); }
private:
// Unbound labels that identify jumps for case statements in the code.
ZoneVector<BytecodeLabel> case_sites_;
};
} // namespace interpreter

View File

@ -4341,6 +4341,270 @@ TEST(Conditional) {
}
}
TEST(Switch) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<int> snippets[] = {
{"var a = 1;\n"
"switch(a) {\n"
" case 1: return 2;\n"
" case 2: return 3;\n"
"}\n",
2 * kPointerSize,
1,
30,
{
B(LdaSmi8), U8(1), //
B(Star), R(1), // The tag variable is allocated as a
B(Ldar), R(1), // local by the parser, hence this
B(Star), R(0), // strange shuffling.
B(LdaSmi8), U8(1), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(LdaSmi8), U8(2), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(7), //
B(Jump), U8(8), //
B(LdaSmi8), U8(2), //
B(Return), //
B(LdaSmi8), U8(3), //
B(Return), //
B(LdaUndefined), //
B(Return), //
}},
{"var a = 1;\n"
"switch(a) {\n"
" case 1: a = 2; break;\n"
" case 2: a = 3; break;\n"
"}\n",
2 * kPointerSize,
1,
36,
{
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(1), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(LdaSmi8), U8(2), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(Jump), U8(14), //
B(LdaSmi8), U8(2), //
B(Star), R(1), //
B(Jump), U8(8), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(Jump), U8(2), //
B(LdaUndefined), //
B(Return), //
}},
{"var a = 1;\n"
"switch(a) {\n"
" case 1: a = 2; // fall-through\n"
" case 2: a = 3; break;\n"
"}\n",
2 * kPointerSize,
1,
34,
{
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(1), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(LdaSmi8), U8(2), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(8), //
B(Jump), U8(12), //
B(LdaSmi8), U8(2), //
B(Star), R(1), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(Jump), U8(2), //
B(LdaUndefined), //
B(Return), //
}},
{"var a = 1;\n"
"switch(a) {\n"
" case 2: break;\n"
" case 3: break;\n"
" default: a = 1; break;\n"
"}\n",
2 * kPointerSize,
1,
34,
{
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(1), //
B(Star), R(0), //
B(LdaSmi8), U8(2), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(LdaSmi8), U8(3), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(6), //
B(Jump), U8(6), //
B(Jump), U8(10), //
B(Jump), U8(8), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Jump), U8(2), //
B(LdaUndefined), //
B(Return), //
}},
{"var a = 1;\n"
"switch(typeof(a)) {\n"
" case 2: a = 1; break;\n"
" case 3: a = 2; break;\n"
" default: a = 3; break;\n"
"}\n",
2 * kPointerSize,
1,
43,
{
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(1), //
B(TypeOf), //
B(Star), R(0), //
B(LdaSmi8), U8(2), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(LdaSmi8), U8(3), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(Jump), U8(14), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Jump), U8(14), //
B(LdaSmi8), U8(2), //
B(Star), R(1), //
B(Jump), U8(8), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(Jump), U8(2), //
B(LdaUndefined), //
B(Return), //
}},
{"var a = 1;\n"
"switch(a) {\n"
" case typeof(a): a = 1; break;\n"
" default: a = 2; break;\n"
"}\n",
2 * kPointerSize,
1,
31,
{
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(1), //
B(Star), R(0), //
B(Ldar), R(1), //
B(TypeOf), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(4), //
B(Jump), U8(8), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Jump), U8(8), //
B(LdaSmi8), U8(2), //
B(Star), R(1), //
B(Jump), U8(2), //
B(LdaUndefined), //
B(Return), //
}},
{"var a = 1;\n"
"switch(a) {\n"
" case 1:\n" REPEAT_64(SPACE, " a = 2;")
"break;\n"
" case 2: a = 3; break;"
"}\n",
2 * kPointerSize,
1,
288,
{
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(1), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(LdaSmi8), U8(2), //
B(TestEqualStrict), R(0), //
B(JumpIfTrueConstant), U8(0), //
B(JumpConstant), U8(1), //
REPEAT_64(COMMA, //
B(LdaSmi8), U8(2), //
B(Star), R(1)), //
B(Jump), U8(8), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(Jump), U8(2), //
B(LdaUndefined), //
B(Return), //
},
2,
{262, 266}},
{"var a = 1;\n"
"switch(a) {\n"
" case 1: \n"
" switch(a + 1) {\n"
" case 2 : a = 1; break;\n"
" default : a = 2; break;\n"
" } // fall-through\n"
" case 2: a = 3;\n"
"}\n",
3 * kPointerSize,
1,
54,
{
B(LdaSmi8), U8(1), //
B(Star), R(2), //
B(Ldar), R(2), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(10), //
B(LdaSmi8), U8(2), //
B(TestEqualStrict), R(0), //
B(JumpIfTrue), U8(30), //
B(Jump), U8(32), //
B(LdaSmi8), U8(1), //
B(Add), R(2), //
B(Star), R(1), //
B(LdaSmi8), U8(2), //
B(TestEqualStrict), R(1), //
B(JumpIfTrue), U8(4), //
B(Jump), U8(8), //
B(LdaSmi8), U8(1), //
B(Star), R(2), //
B(Jump), U8(8), //
B(LdaSmi8), U8(2), //
B(Star), R(2), //
B(Jump), U8(2), //
B(LdaSmi8), U8(3), //
B(Star), R(2), //
B(LdaUndefined), //
B(Return), //
}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -1718,7 +1718,7 @@ TEST(InterpreterRegExpLiterals) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[5] = {
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("return /abd/.exec('cccabbdd');\n",
factory->null_value()),
std::make_pair("return /ab+d/.exec('cccabbdd')[0];\n",
@ -1747,7 +1747,7 @@ TEST(InterpreterArrayLiterals) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[6] = {
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("return [][0];\n",
factory->undefined_value()),
std::make_pair("return [1, 3, 2][1];\n",
@ -1778,7 +1778,7 @@ TEST(InterpreterObjectLiterals) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[14] = {
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("return { }.name;",
factory->undefined_value()),
std::make_pair("return { name: 'string', val: 9.2 }.name;",
@ -1889,7 +1889,7 @@ TEST(InterpreterContextVariables) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> context_vars[5] = {
std::pair<const char*, Handle<Object>> context_vars[] = {
std::make_pair("var a; (function() { a = 1; })(); return a;",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var a = 10; (function() { a; })(); return a;",
@ -1921,7 +1921,7 @@ TEST(InterpreterContextParameters) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> context_params[3] = {
std::pair<const char*, Handle<Object>> context_params[] = {
std::make_pair("return (function() { return arg1; })();",
handle(Smi::FromInt(1), isolate)),
std::make_pair("(function() { arg1 = 4; })(); return arg1;",
@ -1950,7 +1950,7 @@ TEST(InterpreterOuterContextVariables) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> context_vars[2] = {
std::pair<const char*, Handle<Object>> context_vars[] = {
std::make_pair("return outerVar * innerArg;",
handle(Smi::FromInt(200), isolate)),
std::make_pair("outerVar = innerArg; return outerVar",
@ -1984,7 +1984,7 @@ TEST(InterpreterComma) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[6] = {
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("var a; return 0, a;\n", factory->undefined_value()),
std::make_pair("return 'a', 2.2, 3;\n",
handle(Smi::FromInt(3), isolate)),
@ -2012,7 +2012,7 @@ TEST(InterpreterLogicalOr) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[5] = {
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("var a, b; return a || b;\n", factory->undefined_value()),
std::make_pair("var a, b = 10; return a || b;\n",
handle(Smi::FromInt(10), isolate)),
@ -2038,7 +2038,7 @@ TEST(InterpreterLogicalAnd) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> literals[7] = {
std::pair<const char*, Handle<Object>> literals[] = {
std::make_pair("var a, b = 10; return a && b;\n",
factory->undefined_value()),
std::make_pair("var a = 0, b = 10; return a && b / a;\n",
@ -2099,7 +2099,7 @@ TEST(InterpreterThrow) {
i::Factory* factory = isolate->factory();
// TODO(rmcilroy): modify tests when we have real try catch support.
std::pair<const char*, Handle<Object>> throws[6] = {
std::pair<const char*, Handle<Object>> throws[] = {
std::make_pair("throw undefined;\n",
factory->undefined_value()),
std::make_pair("throw 1;\n",
@ -2132,7 +2132,7 @@ TEST(InterpreterCountOperators) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> count_ops[16] = {
std::pair<const char*, Handle<Object>> count_ops[] = {
std::make_pair("var a = 1; return ++a;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = 1; return a++;",
@ -2182,7 +2182,7 @@ TEST(InterpreterGlobalCountOperators) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> count_ops[6] = {
std::pair<const char*, Handle<Object>> count_ops[] = {
std::make_pair("var global = 100;function f(){ return ++global; }",
handle(Smi::FromInt(101), isolate)),
std::make_pair("var global = 100; function f(){ return --global; }",
@ -2212,7 +2212,7 @@ TEST(InterpreterCompoundExpressions) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> compound_expr[5] = {
std::pair<const char*, Handle<Object>> compound_expr[] = {
std::make_pair("var a = 1; a += 2; return a;",
Handle<Object>(Smi::FromInt(3), isolate)),
std::make_pair("var a = 10; a /= 2; return a;",
@ -2266,7 +2266,7 @@ TEST(InterpreterCreateArguments) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, int> create_args[9] = {
std::pair<const char*, int> create_args[] = {
std::make_pair("function f() { return arguments[0]; }", 0),
std::make_pair("function f(a) { return arguments[0]; }", 0),
std::make_pair("function f() { return arguments[2]; }", 2),
@ -2330,7 +2330,7 @@ TEST(InterpreterConditional) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> conditional[8] = {
std::pair<const char*, Handle<Object>> conditional[] = {
std::make_pair("return true ? 2 : 3;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("return false ? 2 : 3;",
@ -2712,6 +2712,86 @@ TEST(InterpreterForIn) {
}
}
TEST(InterpreterSwitch) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> switch_ops[] = {
std::make_pair("var a = 1;\n"
"switch(a) {\n"
" case 1: return 2;\n"
" case 2: return 3;\n"
"}\n",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = 1;\n"
"switch(a) {\n"
" case 2: a = 2; break;\n"
" case 1: a = 3; break;\n"
"}\n"
"return a;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 1;\n"
"switch(a) {\n"
" case 1: a = 2; // fall-through\n"
" case 2: a = 3; break;\n"
"}\n"
"return a;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 100;\n"
"switch(a) {\n"
" case 1: return 100;\n"
" case 2: return 200;\n"
"}\n"
"return undefined;",
factory->undefined_value()),
std::make_pair("var a = 100;\n"
"switch(a) {\n"
" case 1: return 100;\n"
" case 2: return 200;\n"
" default: return 300;\n"
"}\n"
"return undefined;",
handle(Smi::FromInt(300), isolate)),
std::make_pair("var a = 100;\n"
"switch(typeof(a)) {\n"
" case 'string': return 1;\n"
" case 'number': return 2;\n"
" default: return 3;\n"
"}\n",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = 100;\n"
"switch(a) {\n"
" case a += 20: return 1;\n"
" case a -= 10: return 2;\n"
" case a -= 10: return 3;\n"
" default: return 3;\n"
"}\n",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 1;\n"
"switch(a) {\n"
" case 1: \n"
" switch(a + 1) {\n"
" case 2 : a += 1; break;\n"
" default : a += 2; break;\n"
" } // fall-through\n"
" case 2: a += 3;\n"
"}\n"
"return a;",
handle(Smi::FromInt(5), isolate)),
};
for (size_t i = 0; i < arraysize(switch_ops); i++) {
std::string source(InterpreterTester::SourceForBody(switch_ops[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(*switch_ops[i].second));
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8