Commit ab3b3bec authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Unwind and jump to the catch handler in the deoptimizer.

The idea here is to perform the handler lookup in the deoptimizer, and then take the information from the handler table to build the catch handler frame in the deoptimizer. Specifically, we use the pc offset, context location and stack height (in full-code) to tweak the output frame.

Sadly, this still requires nasty voodoo for the liveness analyzer so that it keeps variables alive if they are used in the catch handler.

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

Cr-Commit-Position: refs/heads/master@{#33936}
parent 0e79d8c7
......@@ -431,10 +431,13 @@ class AstGraphBuilder::FrameStateBeforeAndAfter {
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node, 0)->opcode());
bool node_has_exception = NodeProperties::IsExceptionalCall(node);
Node* frame_state_after =
id_after == BailoutId::None()
? builder_->jsgraph()->EmptyFrameState()
: builder_->environment()->Checkpoint(id_after, combine);
: builder_->environment()->Checkpoint(id_after, combine,
node_has_exception);
NodeProperties::ReplaceFrameStateInput(node, 0, frame_state_after);
}
......@@ -869,9 +872,9 @@ void AstGraphBuilder::Environment::UpdateStateValuesWithCache(
env_values, static_cast<size_t>(count));
}
Node* AstGraphBuilder::Environment::Checkpoint(
BailoutId ast_id, OutputFrameStateCombine combine) {
Node* AstGraphBuilder::Environment::Checkpoint(BailoutId ast_id,
OutputFrameStateCombine combine,
bool owner_has_exception) {
if (!builder()->info()->is_deoptimization_enabled()) {
return builder()->jsgraph()->EmptyFrameState();
}
......@@ -891,7 +894,15 @@ Node* AstGraphBuilder::Environment::Checkpoint(
DCHECK(IsLivenessBlockConsistent());
if (liveness_block() != nullptr) {
liveness_block()->Checkpoint(result);
// If the owning node has an exception, register the checkpoint to the
// predecessor so that the checkpoint is used for both the normal and the
// exceptional paths. Yes, this is a terrible hack and we might want
// to use an explicit frame state for the exceptional path.
if (owner_has_exception) {
liveness_block()->GetPredecessor()->Checkpoint(result);
} else {
liveness_block()->Checkpoint(result);
}
}
return result;
}
......@@ -4042,8 +4053,10 @@ void AstGraphBuilder::PrepareFrameState(Node* node, BailoutId ast_id,
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node, 0)->opcode());
bool node_has_exception = NodeProperties::IsExceptionalCall(node);
NodeProperties::ReplaceFrameStateInput(
node, 0, environment()->Checkpoint(ast_id, combine));
node, 0,
environment()->Checkpoint(ast_id, combine, node_has_exception));
}
}
......
......@@ -518,7 +518,8 @@ class AstGraphBuilder::Environment : public ZoneObject {
// Preserve a checkpoint of the environment for the IR graph. Any
// further mutation of the environment will not affect checkpoints.
Node* Checkpoint(BailoutId ast_id, OutputFrameStateCombine combine =
OutputFrameStateCombine::Ignore());
OutputFrameStateCombine::Ignore(),
bool node_has_exception = false);
// Control dependency tracked by this environment.
Node* GetControlDependency() { return control_dependency_; }
......
......@@ -85,6 +85,10 @@ class LivenessAnalyzerBlock {
void Bind(int var) { entries_.push_back(Entry(Entry::kBind, var)); }
void Checkpoint(Node* node) { entries_.push_back(Entry(node)); }
void AddPredecessor(LivenessAnalyzerBlock* b) { predecessors_.push_back(b); }
LivenessAnalyzerBlock* GetPredecessor() {
DCHECK(predecessors_.size() == 1);
return predecessors_[0];
}
private:
class Entry {
......
......@@ -123,6 +123,7 @@ bool NodeProperties::IsControlEdge(Edge edge) {
// static
bool NodeProperties::IsExceptionalCall(Node* node) {
if (node->op()->HasProperty(Operator::kNoThrow)) return false;
for (Edge const edge : node->use_edges()) {
if (!NodeProperties::IsControlEdge(edge)) continue;
if (edge.from()->opcode() == IrOpcode::kIfException) return true;
......
......@@ -502,7 +502,6 @@ const char* Deoptimizer::MessageFor(BailoutType type) {
return NULL;
}
Deoptimizer::Deoptimizer(Isolate* isolate, JSFunction* function,
BailoutType type, unsigned bailout_id, Address from,
int fp_to_sp_delta, Code* optimized_code)
......@@ -513,11 +512,19 @@ Deoptimizer::Deoptimizer(Isolate* isolate, JSFunction* function,
from_(from),
fp_to_sp_delta_(fp_to_sp_delta),
has_alignment_padding_(0),
deoptimizing_throw_(false),
catch_handler_data_(-1),
catch_handler_pc_offset_(-1),
input_(nullptr),
output_count_(0),
jsframe_count_(0),
output_(nullptr),
trace_scope_(nullptr) {
if (isolate->deoptimizer_lazy_throw()) {
isolate->set_deoptimizer_lazy_throw(false);
deoptimizing_throw_ = true;
}
// For COMPILED_STUBs called from builtins, the function pointer is a SMI
// indicating an internal frame.
if (function->IsSmi()) {
......@@ -691,6 +698,41 @@ int Deoptimizer::GetDeoptimizedCodeCount(Isolate* isolate) {
return length;
}
namespace {
int LookupCatchHandler(TranslatedFrame* translated_frame, int* data_out) {
switch (translated_frame->kind()) {
case TranslatedFrame::kFunction: {
BailoutId node_id = translated_frame->node_id();
JSFunction* function =
JSFunction::cast(translated_frame->begin()->GetRawValue());
Code* non_optimized_code = function->shared()->code();
FixedArray* raw_data = non_optimized_code->deoptimization_data();
DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
unsigned pc_and_state =
Deoptimizer::GetOutputInfo(data, node_id, function->shared());
unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state);
HandlerTable* table =
HandlerTable::cast(non_optimized_code->handler_table());
HandlerTable::CatchPrediction prediction;
return table->LookupRange(pc_offset, data_out, &prediction);
}
case TranslatedFrame::kInterpretedFunction: {
int bytecode_offset = translated_frame->node_id().ToInt();
JSFunction* function =
JSFunction::cast(translated_frame->begin()->GetRawValue());
BytecodeArray* bytecode = function->shared()->bytecode_array();
HandlerTable* table = HandlerTable::cast(bytecode->handler_table());
HandlerTable::CatchPrediction prediction;
return table->LookupRange(bytecode_offset, data_out, &prediction);
}
default:
break;
}
return -1;
}
} // namespace
// We rely on this function not causing a GC. It is called from generated code
// without having a real stack frame in place.
......@@ -731,6 +773,22 @@ void Deoptimizer::DoComputeOutputFrames() {
// Do the input frame to output frame(s) translation.
size_t count = translated_state_.frames().size();
// If we are supposed to go to the catch handler, find the catching frame
// for the catch and make sure we only deoptimize upto that frame.
if (deoptimizing_throw_) {
size_t catch_handler_frame_index = count;
for (size_t i = count; i-- > 0;) {
catch_handler_pc_offset_ = LookupCatchHandler(
&(translated_state_.frames()[i]), &catch_handler_data_);
if (catch_handler_pc_offset_ >= 0) {
catch_handler_frame_index = i;
break;
}
}
CHECK_LT(catch_handler_frame_index, count);
count = catch_handler_frame_index + 1;
}
DCHECK(output_ == NULL);
output_ = new FrameDescription*[count];
for (size_t i = 0; i < count; ++i) {
......@@ -749,11 +807,12 @@ void Deoptimizer::DoComputeOutputFrames() {
int frame_index = static_cast<int>(i);
switch (translated_state_.frames()[i].kind()) {
case TranslatedFrame::kFunction:
DoComputeJSFrame(frame_index);
DoComputeJSFrame(frame_index, deoptimizing_throw_ && i == count - 1);
jsframe_count_++;
break;
case TranslatedFrame::kInterpretedFunction:
DoComputeInterpretedFrame(frame_index);
DoComputeInterpretedFrame(frame_index,
deoptimizing_throw_ && i == count - 1);
jsframe_count_++;
break;
case TranslatedFrame::kArgumentsAdaptor:
......@@ -798,19 +857,28 @@ void Deoptimizer::DoComputeOutputFrames() {
}
}
void Deoptimizer::DoComputeJSFrame(int frame_index) {
void Deoptimizer::DoComputeJSFrame(int frame_index, bool goto_catch_handler) {
TranslatedFrame* translated_frame =
&(translated_state_.frames()[frame_index]);
SharedFunctionInfo* shared = translated_frame->raw_shared_info();
TranslatedFrame::iterator value_iterator = translated_frame->begin();
bool is_bottommost = (0 == frame_index);
bool is_topmost = (output_count_ - 1 == frame_index);
int input_index = 0;
BailoutId node_id = translated_frame->node_id();
unsigned height =
translated_frame->height() - 1; // Do not count the context.
unsigned height_in_bytes = height * kPointerSize;
if (goto_catch_handler) {
// Take the stack height from the handler table.
height = catch_handler_data_;
// We also make space for the exception itself.
height_in_bytes = (height + 1) * kPointerSize;
DCHECK(is_topmost);
}
JSFunction* function = JSFunction::cast(value_iterator->GetRawValue());
value_iterator++;
input_index++;
......@@ -820,6 +888,8 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
PrintF(trace_scope_->file(), "%s", name.get());
PrintF(trace_scope_->file(),
" => node=%d, height=%d\n", node_id.ToInt(), height_in_bytes);
PrintF(trace_scope_->file(), " => node=%d, height=%d%s\n", node_id.ToInt(),
height_in_bytes, goto_catch_handler ? " (throw)" : "");
}
// The 'fixed' part of the frame consists of the incoming parameters and
......@@ -833,8 +903,6 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
new(output_frame_size) FrameDescription(output_frame_size, function);
output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
bool is_bottommost = (0 == frame_index);
bool is_topmost = (output_count_ - 1 == frame_index);
CHECK(frame_index >= 0 && frame_index < output_count_);
CHECK_NULL(output_[frame_index]);
output_[frame_index] = output_frame;
......@@ -935,8 +1003,20 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
Register context_reg = JavaScriptFrame::context_register();
output_offset -= kPointerSize;
input_offset -= kPointerSize;
TranslatedFrame::iterator context_pos = value_iterator;
int context_input_index = input_index;
// When deoptimizing into a catch block, we need to take the context
// from just above the top of the operand stack (we push the context
// at the entry of the try block).
if (goto_catch_handler) {
for (unsigned i = 0; i < height + 1; ++i) {
context_pos++;
context_input_index++;
}
}
// Read the context from the translations.
Object* context = value_iterator->GetRawValue();
Object* context = context_pos->GetRawValue();
if (context == isolate_->heap()->undefined_value()) {
// If the context was optimized away, just use the context from
// the activation. This should only apply to Crankshaft code.
......@@ -949,13 +1029,13 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
value = reinterpret_cast<intptr_t>(context);
output_frame->SetContext(value);
if (is_topmost) output_frame->SetRegister(context_reg.code(), value);
WriteValueToOutput(context, input_index, frame_index, output_offset,
WriteValueToOutput(context, context_input_index, frame_index, output_offset,
"context ");
if (context == isolate_->heap()->arguments_marker()) {
Address output_address =
reinterpret_cast<Address>(output_[frame_index]->GetTop()) +
output_offset;
values_to_materialize_.push_back({output_address, value_iterator});
values_to_materialize_.push_back({output_address, context_pos});
}
value_iterator++;
input_index++;
......@@ -975,19 +1055,19 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
WriteTranslatedValueToOutput(&value_iterator, &input_index, frame_index,
output_offset);
}
if (goto_catch_handler) {
// Write out the exception for the catch handler.
output_offset -= kPointerSize;
Object* exception_obj = reinterpret_cast<Object*>(
input_->GetRegister(FullCodeGenerator::result_register().code()));
WriteValueToOutput(exception_obj, input_index, frame_index, output_offset,
"exception ");
input_index++;
}
CHECK_EQ(0u, output_offset);
// Compute this frame's PC, state, and continuation.
Code* non_optimized_code = shared->code();
FixedArray* raw_data = non_optimized_code->deoptimization_data();
DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
Address start = non_optimized_code->instruction_start();
unsigned pc_and_state = GetOutputInfo(data, node_id, shared);
unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state);
intptr_t pc_value = reinterpret_cast<intptr_t>(start + pc_offset);
output_frame->SetPc(pc_value);
// Update constant pool.
Code* non_optimized_code = shared->code();
if (FLAG_enable_embedded_constant_pool) {
intptr_t constant_pool_value =
reinterpret_cast<intptr_t>(non_optimized_code->constant_pool());
......@@ -999,8 +1079,22 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
}
}
// Compute this frame's PC, state, and continuation.
FixedArray* raw_data = non_optimized_code->deoptimization_data();
DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
Address start = non_optimized_code->instruction_start();
unsigned pc_and_state = GetOutputInfo(data, node_id, function->shared());
unsigned pc_offset = goto_catch_handler
? catch_handler_pc_offset_
: FullCodeGenerator::PcField::decode(pc_and_state);
intptr_t pc_value = reinterpret_cast<intptr_t>(start + pc_offset);
output_frame->SetPc(pc_value);
// If we are going to the catch handler, then the exception lives in
// the accumulator.
FullCodeGenerator::State state =
FullCodeGenerator::StateField::decode(pc_and_state);
goto_catch_handler ? FullCodeGenerator::TOS_REG
: FullCodeGenerator::StateField::decode(pc_and_state);
output_frame->SetState(Smi::FromInt(state));
// Set the continuation for the topmost frame.
......@@ -1019,8 +1113,8 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
}
}
void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
void Deoptimizer::DoComputeInterpretedFrame(int frame_index,
bool goto_catch_handler) {
TranslatedFrame* translated_frame =
&(translated_state_.frames()[frame_index]);
SharedFunctionInfo* shared = translated_frame->raw_shared_info();
......@@ -1028,7 +1122,7 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
TranslatedFrame::iterator value_iterator = translated_frame->begin();
int input_index = 0;
BailoutId bytecode_offset = translated_frame->node_id();
int bytecode_offset = translated_frame->node_id().ToInt();
unsigned height = translated_frame->height();
unsigned height_in_bytes = height * kPointerSize;
JSFunction* function = JSFunction::cast(value_iterator->GetRawValue());
......@@ -1038,8 +1132,12 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
PrintF(trace_scope_->file(), " translating interpreted frame ");
base::SmartArrayPointer<char> name = shared->DebugName()->ToCString();
PrintF(trace_scope_->file(), "%s", name.get());
PrintF(trace_scope_->file(), " => bytecode_offset=%d, height=%d\n",
bytecode_offset.ToInt(), height_in_bytes);
PrintF(trace_scope_->file(), " => bytecode_offset=%d, height=%d%s\n",
bytecode_offset, height_in_bytes,
goto_catch_handler ? " (throw)" : "");
}
if (goto_catch_handler) {
bytecode_offset = catch_handler_pc_offset_;
}
// The 'fixed' part of the frame consists of the incoming parameters and
......@@ -1151,14 +1249,27 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
Register context_reg = InterpretedFrame::context_register();
output_offset -= kPointerSize;
input_offset -= kPointerSize;
// When deoptimizing into a catch block, we need to take the context
// from a register that was specified in the handler table.
TranslatedFrame::iterator context_pos = value_iterator;
int context_input_index = input_index;
if (goto_catch_handler) {
// Skip to the translated value of the register specified
// in the handler table.
for (int i = 0; i < catch_handler_data_ + 1; ++i) {
context_pos++;
context_input_index++;
}
}
// Read the context from the translations.
Object* context = value_iterator->GetRawValue();
Object* context = context_pos->GetRawValue();
// The context should not be a placeholder for a materialized object.
CHECK(context != isolate_->heap()->arguments_marker());
value = reinterpret_cast<intptr_t>(context);
output_frame->SetContext(value);
if (is_topmost) output_frame->SetRegister(context_reg.code(), value);
WriteValueToOutput(context, input_index, frame_index, output_offset,
WriteValueToOutput(context, context_input_index, frame_index, output_offset,
"context ");
value_iterator++;
input_index++;
......@@ -1191,7 +1302,7 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
output_offset -= kPointerSize;
input_offset -= kPointerSize;
int raw_bytecode_offset =
BytecodeArray::kHeaderSize - kHeapObjectTag + bytecode_offset.ToInt();
BytecodeArray::kHeaderSize - kHeapObjectTag + bytecode_offset;
Smi* smi_bytecode_offset = Smi::FromInt(raw_bytecode_offset);
WriteValueToOutput(smi_bytecode_offset, 0, frame_index, output_offset,
"bytecode offset ");
......@@ -1204,10 +1315,15 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
}
CHECK_EQ(0u, output_offset);
// Set the accumulator register.
output_frame->SetRegister(
kInterpreterAccumulatorRegister.code(),
reinterpret_cast<intptr_t>(value_iterator->GetRawValue()));
// Set the accumulator register. If we are lazy deopting to a catch handler,
// we set the accumulator to the exception (which lives in the result
// register).
intptr_t accumulator_value =
goto_catch_handler
? input_->GetRegister(FullCodeGenerator::result_register().code())
: reinterpret_cast<intptr_t>(value_iterator->GetRawValue());
output_frame->SetRegister(kInterpreterAccumulatorRegister.code(),
accumulator_value);
value_iterator++;
Builtins* builtins = isolate_->builtins();
......
......@@ -597,8 +597,8 @@ class Deoptimizer : public Malloced {
void DeleteFrameDescriptions();
void DoComputeOutputFrames();
void DoComputeJSFrame(int frame_index);
void DoComputeInterpretedFrame(int frame_index);
void DoComputeJSFrame(int frame_index, bool goto_catch_handler);
void DoComputeInterpretedFrame(int frame_index, bool goto_catch_handler);
void DoComputeArgumentsAdaptorFrame(int frame_index);
void DoComputeConstructStubFrame(int frame_index);
void DoComputeAccessorStubFrame(int frame_index, bool is_setter_stub_frame);
......@@ -671,6 +671,9 @@ class Deoptimizer : public Malloced {
Address from_;
int fp_to_sp_delta_;
int has_alignment_padding_;
bool deoptimizing_throw_;
int catch_handler_data_;
int catch_handler_pc_offset_;
// Input frame description.
FrameDescription* input_;
......
......@@ -96,6 +96,8 @@ class FullCodeGenerator: public AstVisitor {
#error Unsupported target architecture.
#endif
static Register result_register();
private:
class Breakable;
class Iteration;
......@@ -741,7 +743,6 @@ class FullCodeGenerator: public AstVisitor {
FunctionLiteral* literal() const { return info_->literal(); }
Scope* scope() { return scope_; }
static Register result_register();
static Register context_register();
// Set fields in the stack frame. Offsets are the frame pointer relative
......
......@@ -1112,6 +1112,13 @@ Object* Isolate::UnwindAndFindHandler() {
// Gather information from the frame.
code = frame->LookupCode();
if (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.
offset = static_cast<int>(frame->pc() - code->entry());
set_deoptimizer_lazy_throw(true);
}
handler_sp = return_sp;
handler_fp = frame->fp();
break;
......@@ -1755,7 +1762,6 @@ void Isolate::ThreadDataTable::RemoveAllThreads(Isolate* isolate) {
#define TRACE_ISOLATE(tag)
#endif
Isolate::Isolate(bool enable_serializer)
: embedder_data_(),
entry_stack_(NULL),
......@@ -1771,6 +1777,7 @@ Isolate::Isolate(bool enable_serializer)
stub_cache_(NULL),
code_aging_helper_(NULL),
deoptimizer_data_(NULL),
deoptimizer_lazy_throw_(false),
materialized_object_store_(NULL),
capture_stack_trace_for_uncaught_exceptions_(false),
stack_trace_for_uncaught_exceptions_frame_limit_(0),
......
......@@ -820,6 +820,10 @@ class Isolate {
StubCache* stub_cache() { return stub_cache_; }
CodeAgingHelper* code_aging_helper() { return code_aging_helper_; }
DeoptimizerData* deoptimizer_data() { return deoptimizer_data_; }
bool deoptimizer_lazy_throw() const { return deoptimizer_lazy_throw_; }
void set_deoptimizer_lazy_throw(bool value) {
deoptimizer_lazy_throw_ = value;
}
ThreadLocalTop* thread_local_top() { return &thread_local_top_; }
MaterializedObjectStore* materialized_object_store() {
return materialized_object_store_;
......@@ -1218,6 +1222,7 @@ class Isolate {
StubCache* stub_cache_;
CodeAgingHelper* code_aging_helper_;
DeoptimizerData* deoptimizer_data_;
bool deoptimizer_lazy_throw_;
MaterializedObjectStore* materialized_object_store_;
ThreadLocalTop thread_local_top_;
bool capture_stack_trace_for_uncaught_exceptions_;
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
(function LazyDeoptFromTryBlock() {
function g(dummy) {
%DeoptimizeFunction(f);
throw 42;
}
function f() {
var a = 1;
try {
var dummy = 2; // perturb the stack height.
g(dummy);
} catch (e) {
return e + a;
}
}
assertEquals(43, f());
assertEquals(43, f());
%NeverOptimizeFunction(g);
%OptimizeFunctionOnNextCall(f);
assertEquals(43, f());
})();
(function LazyDeoptDoublyNestedTryBlock() {
function g(dummy) {
%DeoptimizeFunction(f);
throw 42;
}
function f() {
var b;
try {
var a = 1;
try {
var dummy = 2; // perturb the stack height.
g(dummy);
} catch (e) {
b = e + a;
}
} catch (e) {
return 0;
}
return b;
}
assertEquals(43, f());
assertEquals(43, f());
%NeverOptimizeFunction(g);
%OptimizeFunctionOnNextCall(f);
assertEquals(43, f());
})();
(function LazyDeoptInlinedTry() {
function g(dummy) {
%DeoptimizeFunction(f);
%DeoptimizeFunction(h);
throw 42;
}
function h() {
var a = 1;
try {
var dummy = 2; // perturb the stack height.
g(dummy);
} catch (e) {
b = e + a;
}
return b;
}
function f() {
var c = 1;
return h() + 1;
}
assertEquals(44, f());
assertEquals(44, f());
%NeverOptimizeFunction(g);
%OptimizeFunctionOnNextCall(f);
assertEquals(44, f());
})();
(function LazyDeoptInlinedIntoTry() {
function g(c) {
%DeoptimizeFunction(f);
%DeoptimizeFunction(h);
throw c;
}
function h(c) {
return g(c);
}
function f() {
var a = 1;
try {
var c = 42; // perturb the stack height.
h(c);
} catch (e) {
a += e;
}
return a;
}
assertEquals(43, f());
assertEquals(43, f());
%NeverOptimizeFunction(g);
%OptimizeFunctionOnNextCall(f);
assertEquals(43, f());
})();
(function LazyDeoptTryBlockContextCatch() {
var global = 0;
function g() {
%DeoptimizeFunction(f);
throw "boom!";
}
function f(a) {
var x = a + 23
try {
let y = a + 42;
function capture() { return x + y }
g();
} catch(e) {
global = x;
}
return x;
}
assertEquals(23, f(0));
assertEquals(24, f(1));
%OptimizeFunctionOnNextCall(f);
assertEquals(25, f(2));
assertEquals(25, global);
})();
(function LazyDeoptTryBlockFinally() {
var global = 0;
function g() {
%DeoptimizeFunction(f);
throw "boom!";
}
function f(a) {
var x = a + 23
try {
let y = a + 42;
function capture() { return x + y }
g();
} finally {
global = x;
}
return x;
}
assertThrows(function() { f(0) });
assertThrows(function() { f(1) });
%OptimizeFunctionOnNextCall(f);
assertThrowsEquals(function() { f(2) }, "boom!");
assertEquals(25, global);
})();
(function LazyDeoptTryCatchContextCatch() {
var global = 0;
function g() {
%DeoptimizeFunction(f);
throw 5;
}
function f(a) {
var x = a + 23
try {
try {
throw 1;
} catch(e2) {
function capture() { return x + y }
g();
}
} catch(e) {
global = x + e;
}
return x;
}
assertEquals(23, f(0));
assertEquals(24, f(1));
%OptimizeFunctionOnNextCall(f);
assertEquals(25, f(2));
assertEquals(30, global);
})();
(function LazyDeoptTryWithContextCatch() {
var global = 0;
function g() {
%DeoptimizeFunction(f);
throw 5;
}
function f(a) {
var x = a + 23
try {
with ({ y : a + 42 }) {
function capture() { return x + y }
g();
}
} catch(e) {
global = x + e;
}
return x;
}
assertEquals(23, f(0));
assertEquals(24, f(1));
%OptimizeFunctionOnNextCall(f);
assertEquals(25, f(2));
assertEquals(30, global);
})();
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