OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/flow_graph_builder.h" | 5 #include "vm/flow_graph_builder.h" |
6 | 6 |
7 #include "lib/invocation_mirror.h" | 7 #include "lib/invocation_mirror.h" |
8 #include "vm/ast_printer.h" | 8 #include "vm/ast_printer.h" |
9 #include "vm/bit_vector.h" | 9 #include "vm/bit_vector.h" |
10 #include "vm/class_finalizer.h" | 10 #include "vm/class_finalizer.h" |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
253 guarded_fields_(new(I) ZoneGrowableArray<const Field*>()), | 253 guarded_fields_(new(I) ZoneGrowableArray<const Field*>()), |
254 last_used_block_id_(0), // 0 is used for the graph entry. | 254 last_used_block_id_(0), // 0 is used for the graph entry. |
255 try_index_(CatchClauseNode::kInvalidTryIndex), | 255 try_index_(CatchClauseNode::kInvalidTryIndex), |
256 catch_try_index_(CatchClauseNode::kInvalidTryIndex), | 256 catch_try_index_(CatchClauseNode::kInvalidTryIndex), |
257 loop_depth_(0), | 257 loop_depth_(0), |
258 graph_entry_(NULL), | 258 graph_entry_(NULL), |
259 temp_count_(0), | 259 temp_count_(0), |
260 args_pushed_(0), | 260 args_pushed_(0), |
261 nesting_stack_(NULL), | 261 nesting_stack_(NULL), |
262 osr_id_(osr_id), | 262 osr_id_(osr_id), |
263 is_optimizing_(is_optimizing) { } | 263 is_optimizing_(is_optimizing), |
| 264 jump_cnt_(0), |
| 265 await_joins_(new(I) ZoneGrowableArray<JoinEntryInstr*>()), |
| 266 await_levels_(new(I) ZoneGrowableArray<intptr_t>()) { } |
264 | 267 |
265 | 268 |
266 void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { | 269 void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { |
267 graph_entry_->AddCatchEntry(entry); | 270 graph_entry_->AddCatchEntry(entry); |
268 } | 271 } |
269 | 272 |
270 | 273 |
271 void InlineExitCollector::PrepareGraphs(FlowGraph* callee_graph) { | 274 void InlineExitCollector::PrepareGraphs(FlowGraph* callee_graph) { |
272 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); | 275 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); |
273 ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id()); | 276 ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id()); |
(...skipping 757 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1031 if (!is_implicit_dynamic_getter && !function.IsConstructor()) { | 1034 if (!is_implicit_dynamic_getter && !function.IsConstructor()) { |
1032 const AbstractType& dst_type = | 1035 const AbstractType& dst_type = |
1033 AbstractType::ZoneHandle(I, function.result_type()); | 1036 AbstractType::ZoneHandle(I, function.result_type()); |
1034 return_value = BuildAssignableValue(node->value()->token_pos(), | 1037 return_value = BuildAssignableValue(node->value()->token_pos(), |
1035 return_value, | 1038 return_value, |
1036 dst_type, | 1039 dst_type, |
1037 Symbols::FunctionResult()); | 1040 Symbols::FunctionResult()); |
1038 } | 1041 } |
1039 } | 1042 } |
1040 | 1043 |
1041 intptr_t current_context_level = owner()->context_level(); | |
1042 ASSERT(current_context_level >= 0); | |
1043 if (owner()->parsed_function()->saved_entry_context_var() != NULL) { | |
1044 // CTX on entry was saved, but not linked as context parent. | |
1045 BuildRestoreContext(*owner()->parsed_function()->saved_entry_context_var()); | |
1046 } else { | |
1047 UnchainContexts(current_context_level); | |
1048 } | |
1049 | |
1050 // Async functions contain two types of return statements: | 1044 // Async functions contain two types of return statements: |
1051 // 1) Returns that should complete the completer once all finally blocks have | 1045 // 1) Returns that should complete the completer once all finally blocks have |
1052 // been inlined (call: :async_completer.complete(return_value)). These | 1046 // been inlined (call: :async_completer.complete(return_value)). These |
1053 // returns end up returning null in the end. | 1047 // returns end up returning null in the end. |
1054 // 2) "Continuation" returns that should not complete the completer but return | 1048 // 2) "Continuation" returns that should not complete the completer but return |
1055 // the value. | 1049 // the value. |
1056 // | 1050 // |
1057 // We distinguish those kinds of nodes via is_regular_return(). | 1051 // We distinguish those kinds of nodes via is_regular_return(). |
1058 // | 1052 // |
1059 if (function.is_async_closure() && node->is_regular_return()) { | 1053 if (function.is_async_closure() && (node->type() == ReturnNode::kRegular)) { |
1060 // Temporary store the computed return value. | 1054 // Temporary store the computed return value. |
1061 Do(BuildStoreExprTemp(return_value)); | 1055 Do(BuildStoreExprTemp(return_value)); |
1062 | 1056 |
1063 LocalVariable* rcv_var = node->scope()->LookupVariable( | 1057 LocalVariable* rcv_var = node->scope()->LookupVariable( |
1064 Symbols::AsyncCompleter(), false); | 1058 Symbols::AsyncCompleter(), false); |
1065 ASSERT(rcv_var != NULL && rcv_var->is_captured()); | 1059 ASSERT(rcv_var != NULL && rcv_var->is_captured()); |
1066 ZoneGrowableArray<PushArgumentInstr*>* arguments = | 1060 ZoneGrowableArray<PushArgumentInstr*>* arguments = |
1067 new(I) ZoneGrowableArray<PushArgumentInstr*>(2); | 1061 new(I) ZoneGrowableArray<PushArgumentInstr*>(2); |
1068 Value* rcv_value = Bind(BuildLoadLocal(*rcv_var)); | 1062 Value* rcv_value = Bind(BuildLoadLocal(*rcv_var)); |
1069 arguments->Add(PushArgument(rcv_value)); | 1063 arguments->Add(PushArgument(rcv_value)); |
1070 Value* returned_value = Bind(BuildLoadExprTemp()); | 1064 Value* returned_value = Bind(BuildLoadExprTemp()); |
1071 arguments->Add(PushArgument(returned_value)); | 1065 arguments->Add(PushArgument(returned_value)); |
1072 InstanceCallInstr* call = new(I) InstanceCallInstr( | 1066 InstanceCallInstr* call = new(I) InstanceCallInstr( |
1073 Scanner::kNoSourcePos, | 1067 Scanner::kNoSourcePos, |
1074 Symbols::CompleterComplete(), | 1068 Symbols::CompleterComplete(), |
1075 Token::kILLEGAL, | 1069 Token::kILLEGAL, |
1076 arguments, | 1070 arguments, |
1077 Object::null_array(), | 1071 Object::null_array(), |
1078 1, | 1072 1, |
1079 owner()->ic_data_array()); | 1073 owner()->ic_data_array()); |
1080 Do(call); | 1074 Do(call); |
1081 | 1075 |
1082 // Rebind the return value for the actual return call to be null. | 1076 // Rebind the return value for the actual return call to be null. |
1083 return_value = BuildNullValue(); | 1077 return_value = BuildNullValue(); |
1084 } | 1078 } |
1085 | 1079 |
| 1080 intptr_t current_context_level = owner()->context_level(); |
| 1081 ASSERT(current_context_level >= 0); |
| 1082 if (owner()->parsed_function()->saved_entry_context_var() != NULL) { |
| 1083 // CTX on entry was saved, but not linked as context parent. |
| 1084 BuildRestoreContext(*owner()->parsed_function()->saved_entry_context_var()); |
| 1085 } else { |
| 1086 UnchainContexts(current_context_level); |
| 1087 } |
| 1088 |
| 1089 |
1086 AddReturnExit(node->token_pos(), return_value); | 1090 AddReturnExit(node->token_pos(), return_value); |
1087 } | 1091 } |
1088 | 1092 |
1089 | 1093 |
1090 // <Expression> ::= Literal { literal: Instance } | 1094 // <Expression> ::= Literal { literal: Instance } |
1091 void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) { | 1095 void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) { |
1092 ReturnDefinition(new(I) ConstantInstr(node->literal())); | 1096 ReturnDefinition(new(I) ConstantInstr(node->literal())); |
1093 } | 1097 } |
1094 | 1098 |
1095 | 1099 |
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1437 } | 1441 } |
1438 return new(I) AssertAssignableInstr(token_pos, | 1442 return new(I) AssertAssignableInstr(token_pos, |
1439 value, | 1443 value, |
1440 instantiator, | 1444 instantiator, |
1441 instantiator_type_arguments, | 1445 instantiator_type_arguments, |
1442 dst_type, | 1446 dst_type, |
1443 dst_name); | 1447 dst_name); |
1444 } | 1448 } |
1445 | 1449 |
1446 | 1450 |
| 1451 void EffectGraphVisitor::BuildAwaitJump( |
| 1452 const LocalVariable& continuation_result, |
| 1453 const LocalVariable& old_ctx, |
| 1454 const intptr_t old_ctx_level, |
| 1455 JoinEntryInstr* target) { |
| 1456 // Building a jump consists of the following actions: |
| 1457 // * Record the current continuation result in a temporary. |
| 1458 // * Restore the old context. |
| 1459 // * Overwrite the old context's continuation result with the temporary. |
| 1460 // * Append a Goto to the target's join. |
| 1461 ASSERT(continuation_result.is_captured()); |
| 1462 ASSERT(old_ctx.is_captured()); |
| 1463 // Before resoring the continuation context we need to temporary save the |
| 1464 // current continuation result. |
| 1465 Value* continuation_result_value = Bind(BuildLoadLocal(continuation_result)); |
| 1466 Do(BuildStoreExprTemp(continuation_result_value)); |
| 1467 |
| 1468 // Restore the saved continuation context. |
| 1469 BuildRestoreContext(old_ctx); |
| 1470 |
| 1471 // Pass over the continuation result. |
| 1472 Value* saved_continuation_result = Bind(BuildLoadExprTemp()); |
| 1473 // FlowGraphBuilder is at top context level, but the await target has possibly |
| 1474 // been recorded in a nested context (old_ctx_level). We need to unroll |
| 1475 // manually here. |
| 1476 LocalVariable* tmp_var = EnterTempLocalScope(saved_continuation_result); |
| 1477 intptr_t delta = old_ctx_level - continuation_result.owner()->context_level(); |
| 1478 ASSERT(delta >= 0); |
| 1479 Value* context = Bind(new(I) CurrentContextInstr()); |
| 1480 while (delta-- > 0) { |
| 1481 context = Bind(new(I) LoadFieldInstr( |
| 1482 context, Context::parent_offset(), Type::ZoneHandle(I, Type::null()), |
| 1483 Scanner::kNoSourcePos)); |
| 1484 } |
| 1485 Value* tmp_val = Bind(new(I) LoadLocalInstr(*tmp_var)); |
| 1486 StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr( |
| 1487 Context::variable_offset(continuation_result.index()), |
| 1488 context, |
| 1489 tmp_val, |
| 1490 kEmitStoreBarrier, |
| 1491 Scanner::kNoSourcePos); |
| 1492 Do(store); |
| 1493 Do(ExitTempLocalScope(tmp_var)); |
| 1494 |
| 1495 // Goto saved join. |
| 1496 Goto(target); |
| 1497 } |
| 1498 |
| 1499 |
1447 // Used for type casts and to test assignments. | 1500 // Used for type casts and to test assignments. |
1448 Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, | 1501 Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, |
1449 Value* value, | 1502 Value* value, |
1450 const AbstractType& dst_type, | 1503 const AbstractType& dst_type, |
1451 const String& dst_name) { | 1504 const String& dst_name) { |
1452 if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { | 1505 if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { |
1453 return value; | 1506 return value; |
1454 } | 1507 } |
1455 return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); | 1508 return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); |
1456 } | 1509 } |
(...skipping 656 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2113 UNREACHABLE(); | 2166 UNREACHABLE(); |
2114 } | 2167 } |
2115 | 2168 |
2116 | 2169 |
2117 void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { | 2170 void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { |
2118 // Await nodes are temporary during parsing. | 2171 // Await nodes are temporary during parsing. |
2119 UNREACHABLE(); | 2172 UNREACHABLE(); |
2120 } | 2173 } |
2121 | 2174 |
2122 | 2175 |
| 2176 void EffectGraphVisitor::VisitAwaitMarkerNode(AwaitMarkerNode* node) { |
| 2177 if (node->type() == AwaitMarkerNode::kNewContinuationState) { |
| 2178 // We need to create a new await state which involves: |
| 2179 // * Increase the jump counter. Sanity check against the list of targets. |
| 2180 // * Save the current context for resuming. |
| 2181 ASSERT(node->scope() != NULL); |
| 2182 LocalVariable* jump_var = node->scope()->LookupVariable( |
| 2183 Symbols::AwaitJumpVar(), false); |
| 2184 LocalVariable* ctx_var = node->scope()->LookupVariable( |
| 2185 Symbols::AwaitContextVar(), false); |
| 2186 ASSERT(jump_var != NULL && jump_var->is_captured()); |
| 2187 ASSERT(ctx_var != NULL && ctx_var->is_captured()); |
| 2188 const intptr_t jump_cnt = owner()->next_await_counter(); |
| 2189 ASSERT(jump_cnt >= 0); |
| 2190 // Sanity check that we always add a JoinEntryInstr before adding a new |
| 2191 // state. |
| 2192 ASSERT(jump_cnt == owner()->await_joins()->length()); |
| 2193 // Store the counter in :await_jump_var. |
| 2194 StoreLocalNode* store_jump_cnt = new(I) StoreLocalNode( |
| 2195 Scanner::kNoSourcePos, |
| 2196 jump_var, |
| 2197 new(I) LiteralNode( |
| 2198 Scanner::kNoSourcePos, Smi::ZoneHandle(I, Smi::New(jump_cnt)))); |
| 2199 EffectGraphVisitor for_effect(owner()); |
| 2200 store_jump_cnt->Visit(&for_effect); |
| 2201 Append(for_effect); |
| 2202 // Save the current context for resuming. |
| 2203 BuildSaveContext(*ctx_var); |
| 2204 owner()->await_levels()->Add(owner()->context_level()); |
| 2205 return; |
| 2206 } else if (node->type() == AwaitMarkerNode::kTargetForContinuation) { |
| 2207 // We need to create a new await target which involves: |
| 2208 // * Append a join that is also added to the list that will later result in |
| 2209 // a preamble. |
| 2210 JoinEntryInstr* const join = new(I) JoinEntryInstr( |
| 2211 owner()->AllocateBlockId(), owner()->try_index()); |
| 2212 owner()->await_joins()->Add(join); |
| 2213 Goto(join); |
| 2214 exit_ = join; |
| 2215 return; |
| 2216 } |
| 2217 UNREACHABLE(); |
| 2218 } |
| 2219 |
| 2220 |
2123 intptr_t EffectGraphVisitor::GetCurrentTempLocalIndex() const { | 2221 intptr_t EffectGraphVisitor::GetCurrentTempLocalIndex() const { |
2124 return kFirstLocalSlotFromFp | 2222 return kFirstLocalSlotFromFp |
2125 - owner()->num_stack_locals() | 2223 - owner()->num_stack_locals() |
2126 - owner()->num_copied_params() | 2224 - owner()->num_copied_params() |
2127 - owner()->args_pushed() | 2225 - owner()->args_pushed() |
2128 - owner()->temp_count() + 1; | 2226 - owner()->temp_count() + 1; |
2129 } | 2227 } |
2130 | 2228 |
2131 | 2229 |
2132 LocalVariable* EffectGraphVisitor::EnterTempLocalScope(Value* value) { | 2230 LocalVariable* EffectGraphVisitor::EnterTempLocalScope(Value* value) { |
(...skipping 1486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3619 parameter_value = Bind(assert_assignable); | 3717 parameter_value = Bind(assert_assignable); |
3620 // Store the type checked argument back to its corresponding local | 3718 // Store the type checked argument back to its corresponding local |
3621 // variable so that ssa renaming detects the dependency and makes use | 3719 // variable so that ssa renaming detects the dependency and makes use |
3622 // of the checked type in type propagation. | 3720 // of the checked type in type propagation. |
3623 Do(BuildStoreLocal(parameter, parameter_value)); | 3721 Do(BuildStoreLocal(parameter, parameter_value)); |
3624 } | 3722 } |
3625 pos++; | 3723 pos++; |
3626 } | 3724 } |
3627 } | 3725 } |
3628 | 3726 |
| 3727 // Continuation part: |
| 3728 // If this node sequence is the body of an async closure leave room for a |
| 3729 // preamble. The preamble is generated after visiting the body. |
| 3730 GotoInstr* preamble_start = NULL; |
| 3731 if ((node == owner()->parsed_function()->node_sequence()) && |
| 3732 (owner()->parsed_function()->function().is_async_closure())) { |
| 3733 JoinEntryInstr* preamble_end = new(I) JoinEntryInstr( |
| 3734 owner()->AllocateBlockId(), owner()->try_index()); |
| 3735 ASSERT(exit() != NULL); |
| 3736 exit()->Goto(preamble_end); |
| 3737 ASSERT(exit()->next()->IsGoto()); |
| 3738 preamble_start = exit()->next()->AsGoto(); |
| 3739 ASSERT(preamble_start->IsGoto()); |
| 3740 exit_ = preamble_end; |
| 3741 } |
| 3742 |
3629 intptr_t i = 0; | 3743 intptr_t i = 0; |
3630 while (is_open() && (i < node->length())) { | 3744 while (is_open() && (i < node->length())) { |
3631 EffectGraphVisitor for_effect(owner()); | 3745 EffectGraphVisitor for_effect(owner()); |
3632 node->NodeAt(i++)->Visit(&for_effect); | 3746 node->NodeAt(i++)->Visit(&for_effect); |
3633 Append(for_effect); | 3747 Append(for_effect); |
3634 if (!is_open()) { | 3748 if (!is_open()) { |
3635 // E.g., because of a JumpNode. | 3749 // E.g., because of a JumpNode. |
3636 break; | 3750 break; |
3637 } | 3751 } |
3638 } | 3752 } |
3639 | 3753 |
| 3754 // Continuation part: |
| 3755 // After generating the CFG for the body we can create the preamble because we |
| 3756 // know exactly how many continuation states we need. |
| 3757 if ((node == owner()->parsed_function()->node_sequence()) && |
| 3758 (owner()->parsed_function()->function().is_async_closure())) { |
| 3759 ASSERT(preamble_start != NULL); |
| 3760 // We are at the top level. Fetch the corresponding scope. |
| 3761 LocalScope* top_scope = node->scope(); |
| 3762 LocalVariable* jump_var = top_scope->LookupVariable( |
| 3763 Symbols::AwaitJumpVar(), false); |
| 3764 LocalVariable* ctx_var = top_scope->LookupVariable( |
| 3765 Symbols::AwaitContextVar(), false); |
| 3766 ASSERT(jump_var != NULL && jump_var->is_captured()); |
| 3767 ASSERT(ctx_var != NULL && ctx_var->is_captured()); |
| 3768 |
| 3769 Instruction* saved_entry = entry_; |
| 3770 Instruction* saved_exit = exit_; |
| 3771 entry_ = NULL; |
| 3772 exit_ = NULL; |
| 3773 |
| 3774 LoadLocalNode* load_jump_cnt = new(I) LoadLocalNode( |
| 3775 Scanner::kNoSourcePos, jump_var); |
| 3776 ComparisonNode* check_jump_cnt; |
| 3777 const intptr_t num_await_states = owner()->await_joins()->length(); |
| 3778 for (intptr_t i = 0; i < num_await_states; i++) { |
| 3779 check_jump_cnt = new(I) ComparisonNode( |
| 3780 Scanner::kNoSourcePos, |
| 3781 Token::kEQ, |
| 3782 load_jump_cnt, |
| 3783 new(I) LiteralNode( |
| 3784 Scanner::kNoSourcePos, Smi::ZoneHandle(I, Smi::New(i)))); |
| 3785 TestGraphVisitor for_test(owner(), Scanner::kNoSourcePos); |
| 3786 check_jump_cnt->Visit(&for_test); |
| 3787 EffectGraphVisitor for_true(owner()); |
| 3788 EffectGraphVisitor for_false(owner()); |
| 3789 |
| 3790 LocalVariable* continuation_result_var = |
| 3791 top_scope->LookupVariable(Symbols::AsyncOperationParam(), false); |
| 3792 ASSERT(continuation_result_var != NULL); |
| 3793 for_true.BuildAwaitJump(*continuation_result_var, |
| 3794 *ctx_var, |
| 3795 (*owner()->await_levels())[i], |
| 3796 (*owner()->await_joins())[i]); |
| 3797 Join(for_test, for_true, for_false); |
| 3798 |
| 3799 if (i == 0) { |
| 3800 // Manually link up the preamble start. |
| 3801 preamble_start->previous()->set_next(for_test.entry()); |
| 3802 for_test.entry()->set_previous(preamble_start->previous()); |
| 3803 } |
| 3804 if (i == (num_await_states - 1)) { |
| 3805 // Link up preamble end. |
| 3806 if (exit_ == NULL) { |
| 3807 exit_ = preamble_start; |
| 3808 } else { |
| 3809 exit_->LinkTo(preamble_start); |
| 3810 } |
| 3811 } |
| 3812 } |
| 3813 entry_ = saved_entry; |
| 3814 exit_ = saved_exit; |
| 3815 } |
| 3816 |
3640 if (is_open()) { | 3817 if (is_open()) { |
3641 if (MustSaveRestoreContext(node)) { | 3818 if (MustSaveRestoreContext(node)) { |
3642 BuildRestoreContext( | 3819 BuildRestoreContext( |
3643 *owner()->parsed_function()->saved_entry_context_var()); | 3820 *owner()->parsed_function()->saved_entry_context_var()); |
3644 } else if (num_context_variables > 0) { | 3821 } else if (num_context_variables > 0) { |
3645 UnchainContexts(1); | 3822 UnchainContexts(1); |
3646 } | 3823 } |
3647 } | 3824 } |
3648 | 3825 |
3649 // If this node sequence is labeled, a break out of the sequence will have | 3826 // If this node sequence is labeled, a break out of the sequence will have |
(...skipping 355 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4005 Report::MessageF(Report::kBailout, | 4182 Report::MessageF(Report::kBailout, |
4006 Script::Handle(function.script()), | 4183 Script::Handle(function.script()), |
4007 function.token_pos(), | 4184 function.token_pos(), |
4008 "FlowGraphBuilder Bailout: %s %s", | 4185 "FlowGraphBuilder Bailout: %s %s", |
4009 String::Handle(function.name()).ToCString(), | 4186 String::Handle(function.name()).ToCString(), |
4010 reason); | 4187 reason); |
4011 UNREACHABLE(); | 4188 UNREACHABLE(); |
4012 } | 4189 } |
4013 | 4190 |
4014 } // namespace dart | 4191 } // namespace dart |
OLD | NEW |