Commit 4d62978d authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for Throw.

Adds support for throwing exceptions. Adds the bytecode Throw.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#31366}
parent a5d4608e
...@@ -246,6 +246,7 @@ namespace internal { ...@@ -246,6 +246,7 @@ namespace internal {
V(kUnsupportedPhiUseOfArguments, "Unsupported phi use of arguments") \ V(kUnsupportedPhiUseOfArguments, "Unsupported phi use of arguments") \
V(kUnsupportedPhiUseOfConstVariable, \ V(kUnsupportedPhiUseOfConstVariable, \
"Unsupported phi use of const variable") \ "Unsupported phi use of const variable") \
V(kUnexpectedReturnFromThrow, "Unexpectedly returned from a throw") \
V(kUnsupportedTaggedImmediate, "Unsupported tagged immediate") \ V(kUnsupportedTaggedImmediate, "Unsupported tagged immediate") \
V(kVariableResolvedToWithContext, "Variable resolved to with context") \ V(kVariableResolvedToWithContext, "Variable resolved to with context") \
V(kWeShouldNotHaveAnEmptyLexicalContext, \ V(kWeShouldNotHaveAnEmptyLexicalContext, \
......
...@@ -381,6 +381,12 @@ void BytecodeGraphBuilder::VisitNew( ...@@ -381,6 +381,12 @@ void BytecodeGraphBuilder::VisitNew(
} }
void BytecodeGraphBuilder::VisitThrow(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::BuildBinaryOp( void BytecodeGraphBuilder::BuildBinaryOp(
const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) { const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) {
Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0));
......
...@@ -557,15 +557,20 @@ void InterpreterAssembler::DispatchTo(Node* new_bytecode_offset) { ...@@ -557,15 +557,20 @@ void InterpreterAssembler::DispatchTo(Node* new_bytecode_offset) {
} }
void InterpreterAssembler::Abort(BailoutReason bailout_reason) {
Node* abort_id = SmiTag(Int32Constant(bailout_reason));
CallRuntime(Runtime::kAbort, abort_id);
Return();
}
void InterpreterAssembler::AbortIfWordNotEqual(Node* lhs, Node* rhs, void InterpreterAssembler::AbortIfWordNotEqual(Node* lhs, Node* rhs,
BailoutReason bailout_reason) { BailoutReason bailout_reason) {
RawMachineAssembler::Label match, no_match; RawMachineAssembler::Label match, no_match;
Node* condition = raw_assembler_->WordEqual(lhs, rhs); Node* condition = raw_assembler_->WordEqual(lhs, rhs);
raw_assembler_->Branch(condition, &match, &no_match); raw_assembler_->Branch(condition, &match, &no_match);
raw_assembler_->Bind(&no_match); raw_assembler_->Bind(&no_match);
Node* abort_id = SmiTag(Int32Constant(bailout_reason)); Abort(bailout_reason);
CallRuntime(Runtime::kAbort, abort_id);
Return();
raw_assembler_->Bind(&match); raw_assembler_->Bind(&match);
} }
......
...@@ -144,6 +144,9 @@ class InterpreterAssembler { ...@@ -144,6 +144,9 @@ class InterpreterAssembler {
// Dispatch to the bytecode. // Dispatch to the bytecode.
void Dispatch(); void Dispatch();
// Abort with the given bailout reason.
void Abort(BailoutReason bailout_reason);
protected: protected:
// Close the graph. // Close the graph.
void End(); void End();
......
...@@ -15,7 +15,7 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone) ...@@ -15,7 +15,7 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
bytecode_generated_(false), bytecode_generated_(false),
last_block_end_(0), last_block_end_(0),
last_bytecode_start_(~0), last_bytecode_start_(~0),
return_seen_in_block_(false), exit_seen_in_block_(false),
constants_map_(isolate->heap(), zone), constants_map_(isolate->heap(), zone),
constants_(zone), constants_(zone),
parameter_count_(-1), parameter_count_(-1),
...@@ -576,9 +576,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfToBooleanFalse( ...@@ -576,9 +576,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfToBooleanFalse(
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() {
Output(Bytecode::kThrow);
exit_seen_in_block_ = true;
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() { BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
Output(Bytecode::kReturn); Output(Bytecode::kReturn);
return_seen_in_block_ = true; exit_seen_in_block_ = true;
return *this; return *this;
} }
...@@ -588,13 +595,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::EnterBlock() { return *this; } ...@@ -588,13 +595,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::EnterBlock() { return *this; }
BytecodeArrayBuilder& BytecodeArrayBuilder::LeaveBlock() { BytecodeArrayBuilder& BytecodeArrayBuilder::LeaveBlock() {
last_block_end_ = bytecodes()->size(); last_block_end_ = bytecodes()->size();
return_seen_in_block_ = false; exit_seen_in_block_ = false;
return *this; return *this;
} }
void BytecodeArrayBuilder::EnsureReturn() { void BytecodeArrayBuilder::EnsureReturn() {
if (!return_seen_in_block_) { if (!exit_seen_in_block_) {
LoadUndefined(); LoadUndefined();
Return(); Return();
} }
......
...@@ -156,6 +156,8 @@ class BytecodeArrayBuilder { ...@@ -156,6 +156,8 @@ class BytecodeArrayBuilder {
// than explicitly using them. // than explicitly using them.
BytecodeArrayBuilder& JumpIfToBooleanTrue(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfToBooleanTrue(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfToBooleanFalse(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfToBooleanFalse(BytecodeLabel* label);
BytecodeArrayBuilder& Throw();
BytecodeArrayBuilder& Return(); BytecodeArrayBuilder& Return();
BytecodeArrayBuilder& EnterBlock(); BytecodeArrayBuilder& EnterBlock();
...@@ -215,7 +217,7 @@ class BytecodeArrayBuilder { ...@@ -215,7 +217,7 @@ class BytecodeArrayBuilder {
bool bytecode_generated_; bool bytecode_generated_;
size_t last_block_end_; size_t last_block_end_;
size_t last_bytecode_start_; size_t last_bytecode_start_;
bool return_seen_in_block_; bool exit_seen_in_block_;
IdentityMap<size_t> constants_map_; IdentityMap<size_t> constants_map_;
ZoneVector<Handle<Object>> constants_; ZoneVector<Handle<Object>> constants_;
......
...@@ -962,7 +962,11 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { ...@@ -962,7 +962,11 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); } void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitThrow(Throw* expr) { UNIMPLEMENTED(); } void BytecodeGenerator::VisitThrow(Throw* expr) {
TemporaryRegisterScope temporary_register_scope(builder());
Visit(expr->exception());
builder()->Throw();
}
void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) { void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) {
......
...@@ -132,6 +132,7 @@ namespace interpreter { ...@@ -132,6 +132,7 @@ namespace interpreter {
V(JumpIfToBooleanTrueConstant, OperandType::kIdx8) \ V(JumpIfToBooleanTrueConstant, OperandType::kIdx8) \
V(JumpIfToBooleanFalse, OperandType::kImm8) \ V(JumpIfToBooleanFalse, OperandType::kImm8) \
V(JumpIfToBooleanFalseConstant, OperandType::kIdx8) \ V(JumpIfToBooleanFalseConstant, OperandType::kIdx8) \
V(Throw, OperandType::kNone) \
V(Return, OperandType::kNone) V(Return, OperandType::kNone)
......
...@@ -908,6 +908,17 @@ void Interpreter::DoCreateClosure(compiler::InterpreterAssembler* assembler) { ...@@ -908,6 +908,17 @@ void Interpreter::DoCreateClosure(compiler::InterpreterAssembler* assembler) {
} }
// Throw
//
// Throws the exception in the accumulator.
void Interpreter::DoThrow(compiler::InterpreterAssembler* assembler) {
Node* exception = __ GetAccumulator();
__ CallRuntime(Runtime::kThrow, exception);
// We shouldn't ever return from a throw.
__ Abort(kUnexpectedReturnFromThrow);
}
// Return // Return
// //
// Return the value in the accumulator. // Return the value in the accumulator.
......
...@@ -2757,6 +2757,56 @@ TEST(TryFinally) { ...@@ -2757,6 +2757,56 @@ TEST(TryFinally) {
} }
TEST(Throw) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
// TODO(rmcilroy): modify tests when we have real try catch support.
ExpectedSnippet<const char*> snippets[] = {
{"throw 1;",
0,
1,
3,
{
B(LdaSmi8), U8(1), //
B(Throw), //
},
0},
{"throw 'Error';",
0,
1,
3,
{
B(LdaConstant), U8(0), //
B(Throw), //
},
1,
{"Error"}},
{"if ('test') { throw 'Error'; };",
0,
1,
10,
{
B(LdaConstant), U8(0), //
B(ToBoolean), //
B(JumpIfFalse), U8(5), //
B(LdaConstant), U8(1), //
B(Throw), //
B(LdaUndefined), //
B(Return), //
},
2,
{"test", "Error"}},
};
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(CallNew) { TEST(CallNew) {
InitializedHandleScope handle_scope; InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper; BytecodeGeneratorHelper helper;
......
...@@ -2028,3 +2028,37 @@ TEST(InterpreterTryFinally) { ...@@ -2028,3 +2028,37 @@ TEST(InterpreterTryFinally) {
Handle<Object> return_val = callable().ToHandleChecked(); Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(4)); CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(4));
} }
TEST(InterpreterThrow) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
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::make_pair("throw undefined;\n",
factory->undefined_value()),
std::make_pair("throw 1;\n",
Handle<Object>(Smi::FromInt(1), isolate)),
std::make_pair("throw 'Error';\n",
factory->NewStringFromStaticChars("Error")),
std::make_pair("var a = true; if (a) { throw 'Error'; }\n",
factory->NewStringFromStaticChars("Error")),
std::make_pair("var a = false; if (a) { throw 'Error'; }\n",
factory->undefined_value()),
std::make_pair("throw 'Error1'; throw 'Error2'\n",
factory->NewStringFromStaticChars("Error1")),
};
const char* try_wrapper =
"(function() { try { f(); } catch(e) { return e; }})()";
for (size_t i = 0; i < arraysize(throws); i++) {
std::string source(InterpreterTester::SourceForBody(throws[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
tester.GetCallable<>();
Handle<Object> thrown_obj = v8::Utils::OpenHandle(*CompileRun(try_wrapper));
CHECK(thrown_obj->SameValue(*throws[i].second));
}
}
...@@ -135,6 +135,11 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -135,6 +135,11 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.JumpIfFalse(&start) .JumpIfFalse(&start)
.JumpIfToBooleanTrue(&start) .JumpIfToBooleanTrue(&start)
.JumpIfToBooleanFalse(&start); .JumpIfToBooleanFalse(&start);
builder.EnterBlock()
.Throw()
.LeaveBlock();
builder.Return(); builder.Return();
// Generate BytecodeArray. // Generate BytecodeArray.
......
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