Index: runtime/vm/flow_graph_builder.cc |
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc |
index aa73c5506071b784ed91c8e3ca20b13ce4ac4206..141d2b094272a8b4f8f5abef811565ce20172e78 100644 |
--- a/runtime/vm/flow_graph_builder.cc |
+++ b/runtime/vm/flow_graph_builder.cc |
@@ -113,6 +113,7 @@ class NestedStatement : public ValueObject { |
JoinEntryInstr* break_target() const { return break_target_; } |
virtual intptr_t ContextLevel() const; |
+ virtual void AdjustContextLevel(intptr_t context_level); |
virtual JoinEntryInstr* BreakTargetFor(SourceLabel* label); |
virtual JoinEntryInstr* ContinueTargetFor(SourceLabel* label); |
@@ -152,6 +153,13 @@ intptr_t NestedStatement::ContextLevel() const { |
} |
+void NestedStatement::AdjustContextLevel(intptr_t context_level) { |
+ // There must be a NestedContextAdjustment on the nesting stack. |
+ ASSERT(outer() != NULL); |
+ outer()->AdjustContextLevel(context_level); |
+} |
+ |
+ |
intptr_t FlowGraphBuilder::context_level() const { |
return (nesting_stack() == NULL) ? 0 : nesting_stack()->ContextLevel(); |
} |
@@ -162,7 +170,7 @@ JoinEntryInstr* NestedStatement::BreakTargetFor(SourceLabel* label) { |
if (break_target_ == NULL) { |
break_target_ = |
new(owner()->zone()) JoinEntryInstr(owner()->AllocateBlockId(), |
- owner()->try_index()); |
+ owner()->try_index()); |
} |
return break_target_; |
} |
@@ -193,6 +201,24 @@ intptr_t NestedBlock::ContextLevel() const { |
} |
+// A nested statement reflecting a context level adjustment. |
+class NestedContextAdjustment : public NestedStatement { |
+ public: |
+ NestedContextAdjustment(FlowGraphBuilder* owner, intptr_t context_level) |
+ : NestedStatement(owner, NULL), context_level_(context_level) { } |
+ |
+ virtual intptr_t ContextLevel() const { return context_level_; } |
+ |
+ virtual void AdjustContextLevel(intptr_t context_level) { |
+ ASSERT(context_level <= context_level_); |
+ context_level_ = context_level; |
+ } |
+ |
+ private: |
+ intptr_t context_level_; |
+}; |
+ |
+ |
// A nested statement that can be the target of a continue as well as a |
// break. |
class NestedLoop : public NestedStatement { |
@@ -1055,6 +1081,8 @@ void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { |
Append(for_value); |
Value* return_value = for_value.value(); |
+ NestedContextAdjustment context_adjustment(owner(), owner()->context_level()); |
+ |
if (node->inlined_finally_list_length() > 0) { |
LocalVariable* temp = owner()->parsed_function().finally_return_temp_var(); |
ASSERT(temp != NULL); |
@@ -2295,6 +2323,8 @@ void EffectGraphVisitor::VisitForNode(ForNode* node) { |
void EffectGraphVisitor::VisitJumpNode(JumpNode* node) { |
+ NestedContextAdjustment context_adjustment(owner(), owner()->context_level()); |
+ |
for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) { |
EffectGraphVisitor for_effect(owner()); |
node->InlinedFinallyNodeAt(i)->Visit(&for_effect); |
@@ -2306,27 +2336,7 @@ void EffectGraphVisitor::VisitJumpNode(JumpNode* node) { |
// contains the destination label. |
SourceLabel* label = node->label(); |
ASSERT(label->owner() != NULL); |
- int target_context_level = 0; |
- LocalScope* target_scope = label->owner(); |
- if (target_scope->num_context_variables() > 0) { |
- // The scope of the target label allocates a context, therefore its outer |
- // scope is at a lower context level. |
- target_context_level = target_scope->context_level() - 1; |
- } else { |
- // The scope of the target label does not allocate a context, so its outer |
- // scope is at the same context level. Find it. |
- while ((target_scope != NULL) && |
- (target_scope->num_context_variables() == 0)) { |
- target_scope = target_scope->parent(); |
- } |
- if (target_scope != NULL) { |
- target_context_level = target_scope->context_level(); |
- } |
- } |
- ASSERT(target_context_level >= 0); |
- intptr_t current_context_level = owner()->context_level(); |
- ASSERT(current_context_level >= target_context_level); |
- UnchainContexts(current_context_level - target_context_level); |
+ AdjustContextLevel(label->owner()); |
JoinEntryInstr* jump_target = NULL; |
NestedStatement* current = owner()->nesting_stack(); |
@@ -3867,6 +3877,33 @@ void EffectGraphVisitor::UnchainContexts(intptr_t n) { |
} |
+void EffectGraphVisitor::AdjustContextLevel(LocalScope* target_scope) { |
+ ASSERT(target_scope != NULL); |
+ intptr_t target_context_level = 0; |
+ if (target_scope->num_context_variables() > 0) { |
+ // The scope of the target label allocates a context, therefore its outer |
+ // scope is at a lower context level. |
+ target_context_level = target_scope->context_level() - 1; |
+ } else { |
+ // The scope of the target label does not allocate a context, so its outer |
+ // scope is at the same context level. Find it. |
+ while ((target_scope != NULL) && |
+ (target_scope->num_context_variables() == 0)) { |
+ target_scope = target_scope->parent(); |
+ } |
+ if (target_scope != NULL) { |
+ target_context_level = target_scope->context_level(); |
+ } |
+ } |
+ ASSERT(target_context_level >= 0); |
+ intptr_t current_context_level = owner()->context_level(); |
+ ASSERT(current_context_level >= target_context_level); |
+ UnchainContexts(current_context_level - target_context_level); |
+ // Record adjusted context level. |
+ owner()->nesting_stack()->AdjustContextLevel(target_context_level); |
+} |
+ |
+ |
// <Statement> ::= Sequence { scope: LocalScope |
// nodes: <Statement>* |
// label: SourceLabel } |
@@ -4406,12 +4443,15 @@ void EffectGraphVisitor::VisitInlinedFinallyNode(InlinedFinallyNode* node) { |
} |
// Note: do not restore the saved_try_context here since the inlined |
- // code is running at he context level of the return or jump instruction |
- // that follows the inlined code. See issue 22822. |
+ // code is not reached via an exception handler, therefore the context is |
+ // always properly set on entry. In other words, the inlined finally clause is |
+ // never the target of a long jump that would find an uninitialized current |
+ // context variable. |
JoinEntryInstr* finally_entry = |
new(Z) JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
EffectGraphVisitor for_finally_block(owner()); |
+ for_finally_block.AdjustContextLevel(node->finally_block()->scope()); |
node->finally_block()->Visit(&for_finally_block); |
if (try_index >= 0) { |