Commit f93ac896 authored by caitp's avatar caitp Committed by Commit bot

[builtins] support exception handling in TFJ builtins

BUG=v8:5264, v8:5268
R=mstarzinger@chromium.org, bmeurer@chromium.org, epertoso@chromium.org

Review-Url: https://codereview.chromium.org/2247353005
Cr-Commit-Position: refs/heads/master@{#38792}
parent 10e7ccfb
......@@ -312,6 +312,26 @@ void CodeAssembler::BranchIf(Node* condition, Label* if_true, Label* if_false) {
Goto(if_false);
}
void CodeAssembler::GotoIfException(Node* node, Label* if_exception,
Variable* exception_var) {
Label success(this), exception(this, Label::kDeferred);
success.MergeVariables();
exception.MergeVariables();
DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
raw_assembler_->Continuations(node, success.label_, exception.label_);
Bind(&exception);
const Operator* op = raw_assembler_->common()->IfException();
Node* exception_value = raw_assembler_->AddNode(op, node, node);
if (exception_var != nullptr) {
exception_var->Bind(exception_value);
}
Goto(if_exception);
Bind(&success);
}
Node* CodeAssembler::CallN(CallDescriptor* descriptor, Node* code_target,
Node** args) {
CallPrologue();
......
......@@ -406,6 +406,10 @@ class CodeAssembler {
Node* CallJS(Callable const& callable, Node* context, Node* function,
Node* receiver, Node* arg1, Node* arg2, size_t result_size = 1);
// Exception handling support.
void GotoIfException(Node* node, Label* if_exception,
Variable* exception_var = nullptr);
// Branching helpers.
void BranchIf(Node* condition, Label* if_true, Label* if_false);
......
......@@ -85,6 +85,14 @@ void RawMachineAssembler::Branch(Node* condition, RawMachineLabel* true_val,
current_block_ = nullptr;
}
void RawMachineAssembler::Continuations(Node* call, RawMachineLabel* if_success,
RawMachineLabel* if_exception) {
DCHECK_NOT_NULL(schedule_);
DCHECK_NOT_NULL(current_block_);
schedule()->AddCall(CurrentBlock(), call, Use(if_success), Use(if_exception));
current_block_ = nullptr;
}
void RawMachineAssembler::Switch(Node* index, RawMachineLabel* default_label,
const int32_t* case_values,
RawMachineLabel** case_labels,
......
......@@ -764,6 +764,11 @@ class RawMachineAssembler {
void DebugBreak();
void Comment(const char* msg);
// Add success / exception successor blocks and ends the current block ending
// in a potentially throwing call node.
void Continuations(Node* call, RawMachineLabel* if_success,
RawMachineLabel* if_exception);
// Variables.
Node* Phi(MachineRepresentation rep, Node* n1, Node* n2) {
return AddNode(common()->Phi(rep, 2), n1, n2, graph()->start());
......
......@@ -1226,7 +1226,11 @@ Object* Isolate::UnwindAndFindHandler() {
// Gather information from the frame.
code = frame->LookupCode();
if (code->marked_for_deoptimization()) {
// TODO(bmeurer): Turbofanned BUILTIN frames appear as OPTIMIZED, but
// do not have a code kind of OPTIMIZED_FUNCTION.
if (code->kind() == Code::OPTIMIZED_FUNCTION &&
code->marked_for_deoptimization()) {
// If the target code is lazy deoptimized, we jump to the original
// return address, but we make a note that we are throwing, so that
// the deoptimizer can do the right thing.
......
......@@ -1340,5 +1340,138 @@ TEST(TryProbeStubCache) {
CHECK(queried_existing && queried_non_existing);
}
TEST(GotoIfException) {
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
CodeStubAssemblerTester m(isolate, kNumParams);
Node* context = m.HeapConstant(Handle<Context>(isolate->native_context()));
Node* to_string_tag =
m.HeapConstant(isolate->factory()->to_string_tag_symbol());
Variable exception(&m, MachineRepresentation::kTagged);
Label exception_handler(&m);
Callable to_string = CodeFactory::ToString(isolate);
Node* string = m.CallStub(to_string, context, to_string_tag);
m.GotoIfException(string, &exception_handler, &exception);
m.Return(string);
m.Bind(&exception_handler);
m.Return(exception.value());
Handle<Code> code = m.GenerateCode();
CHECK(!code.is_null());
// Emulate TFJ builtin
code->set_flags(Code::ComputeFlags(Code::BUILTIN));
FunctionTester ft(code, kNumParams);
Handle<Object> result = ft.Call().ToHandleChecked();
// Should be a TypeError
CHECK(result->IsJSObject());
Handle<Object> constructor =
Object::GetPropertyOrElement(result,
isolate->factory()->constructor_string())
.ToHandleChecked();
CHECK(constructor->SameValue(*isolate->type_error_function()));
}
TEST(GotoIfExceptionMultiple) {
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 4; // receiver, first, second, third
CodeStubAssemblerTester m(isolate, kNumParams);
Node* context = m.HeapConstant(Handle<Context>(isolate->native_context()));
Node* first_value = m.Parameter(0);
Node* second_value = m.Parameter(1);
Node* third_value = m.Parameter(2);
Label exception_handler1(&m);
Label exception_handler2(&m);
Label exception_handler3(&m);
Variable return_value(&m, MachineRepresentation::kWord32);
Variable error(&m, MachineRepresentation::kTagged);
return_value.Bind(m.Int32Constant(0));
// try { return ToString(param1) } catch (e) { ... }
Callable to_string = CodeFactory::ToString(isolate);
Node* string = m.CallStub(to_string, context, first_value);
m.GotoIfException(string, &exception_handler1, &error);
m.Return(string);
// try { ToString(param2); return 7 } catch (e) { ... }
m.Bind(&exception_handler1);
return_value.Bind(m.Int32Constant(7));
error.Bind(m.UndefinedConstant());
string = m.CallStub(to_string, context, second_value);
m.GotoIfException(string, &exception_handler2, &error);
m.Return(m.SmiFromWord32(return_value.value()));
// try { ToString(param3); return 7 & ~2; } catch (e) { return e; }
m.Bind(&exception_handler2);
// Return returnValue & ~2
error.Bind(m.UndefinedConstant());
string = m.CallStub(to_string, context, third_value);
m.GotoIfException(string, &exception_handler3, &error);
m.Return(m.SmiFromWord32(
m.Word32And(return_value.value(),
m.Word32Xor(m.Int32Constant(2), m.Int32Constant(-1)))));
m.Bind(&exception_handler3);
m.Return(error.value());
Handle<Code> code = m.GenerateCode();
CHECK(!code.is_null());
// Emulate TFJ builtin
code->set_flags(Code::ComputeFlags(Code::BUILTIN));
FunctionTester ft(code, kNumParams);
Handle<Object> result;
// First handler does not throw, returns result of first value
result = ft.Call(isolate->factory()->undefined_value(),
isolate->factory()->to_string_tag_symbol())
.ToHandleChecked();
CHECK(String::cast(*result)->IsOneByteEqualTo(OneByteVector("undefined")));
// First handler returns a number
result = ft.Call(isolate->factory()->to_string_tag_symbol(),
isolate->factory()->undefined_value())
.ToHandleChecked();
CHECK_EQ(7, Smi::cast(*result)->value());
// First handler throws, second handler returns a number
result = ft.Call(isolate->factory()->to_string_tag_symbol(),
isolate->factory()->to_primitive_symbol())
.ToHandleChecked();
CHECK_EQ(7 & ~2, Smi::cast(*result)->value());
// First handler throws, second handler throws, third handler returns thrown
// value.
result = ft.Call(isolate->factory()->to_string_tag_symbol(),
isolate->factory()->to_primitive_symbol(),
isolate->factory()->unscopables_symbol())
.ToHandleChecked();
// Should be a TypeError
CHECK(result->IsJSObject());
Handle<Object> constructor =
Object::GetPropertyOrElement(result,
isolate->factory()->constructor_string())
.ToHandleChecked();
CHECK(constructor->SameValue(*isolate->type_error_function()));
}
} // namespace internal
} // namespace v8
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