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 {
V(kUnsupportedPhiUseOfArguments, "Unsupported phi use of arguments") \
V(kUnsupportedPhiUseOfConstVariable, \
"Unsupported phi use of const variable") \
V(kUnexpectedReturnFromThrow, "Unexpectedly returned from a throw") \
V(kUnsupportedTaggedImmediate, "Unsupported tagged immediate") \
V(kVariableResolvedToWithContext, "Variable resolved to with context") \
V(kWeShouldNotHaveAnEmptyLexicalContext, \
......
......@@ -381,6 +381,12 @@ void BytecodeGraphBuilder::VisitNew(
}
void BytecodeGraphBuilder::VisitThrow(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::BuildBinaryOp(
const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) {
Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0));
......
......@@ -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,
BailoutReason bailout_reason) {
RawMachineAssembler::Label match, no_match;
Node* condition = raw_assembler_->WordEqual(lhs, rhs);
raw_assembler_->Branch(condition, &match, &no_match);
raw_assembler_->Bind(&no_match);
Node* abort_id = SmiTag(Int32Constant(bailout_reason));
CallRuntime(Runtime::kAbort, abort_id);
Return();
Abort(bailout_reason);
raw_assembler_->Bind(&match);
}
......
......@@ -144,6 +144,9 @@ class InterpreterAssembler {
// Dispatch to the bytecode.
void Dispatch();
// Abort with the given bailout reason.
void Abort(BailoutReason bailout_reason);
protected:
// Close the graph.
void End();
......
......@@ -15,7 +15,7 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
bytecode_generated_(false),
last_block_end_(0),
last_bytecode_start_(~0),
return_seen_in_block_(false),
exit_seen_in_block_(false),
constants_map_(isolate->heap(), zone),
constants_(zone),
parameter_count_(-1),
......@@ -576,9 +576,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfToBooleanFalse(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() {
Output(Bytecode::kThrow);
exit_seen_in_block_ = true;
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
Output(Bytecode::kReturn);
return_seen_in_block_ = true;
exit_seen_in_block_ = true;
return *this;
}
......@@ -588,13 +595,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::EnterBlock() { return *this; }
BytecodeArrayBuilder& BytecodeArrayBuilder::LeaveBlock() {
last_block_end_ = bytecodes()->size();
return_seen_in_block_ = false;
exit_seen_in_block_ = false;
return *this;
}
void BytecodeArrayBuilder::EnsureReturn() {
if (!return_seen_in_block_) {
if (!exit_seen_in_block_) {
LoadUndefined();
Return();
}
......
......@@ -156,6 +156,8 @@ class BytecodeArrayBuilder {
// than explicitly using them.
BytecodeArrayBuilder& JumpIfToBooleanTrue(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfToBooleanFalse(BytecodeLabel* label);
BytecodeArrayBuilder& Throw();
BytecodeArrayBuilder& Return();
BytecodeArrayBuilder& EnterBlock();
......@@ -215,7 +217,7 @@ class BytecodeArrayBuilder {
bool bytecode_generated_;
size_t last_block_end_;
size_t last_bytecode_start_;
bool return_seen_in_block_;
bool exit_seen_in_block_;
IdentityMap<size_t> constants_map_;
ZoneVector<Handle<Object>> constants_;
......
......@@ -962,7 +962,11 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
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) {
......
......@@ -132,6 +132,7 @@ namespace interpreter {
V(JumpIfToBooleanTrueConstant, OperandType::kIdx8) \
V(JumpIfToBooleanFalse, OperandType::kImm8) \
V(JumpIfToBooleanFalseConstant, OperandType::kIdx8) \
V(Throw, OperandType::kNone) \
V(Return, OperandType::kNone)
......
......@@ -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 the value in the accumulator.
......
......@@ -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) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
......
......@@ -2028,3 +2028,37 @@ TEST(InterpreterTryFinally) {
Handle<Object> return_val = callable().ToHandleChecked();
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) {
.JumpIfFalse(&start)
.JumpIfToBooleanTrue(&start)
.JumpIfToBooleanFalse(&start);
builder.EnterBlock()
.Throw()
.LeaveBlock();
builder.Return();
// 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