Chromium Code Reviews| 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->is_regular_return()) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 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 // FGB is at top context level, but the await target has possibly been | |
|
srdjan
2014/08/20 17:50:06
ditto for FGB
Michael Lippautz (Google)
2014/08/20 20:56:06
Done.
| |
| 1474 // recorded in a nested context (old_ctx_level). We need to unroll manually | |
| 1475 // 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 int32_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( | |
|
Florian Schneider
2014/08/21 11:09:29
Why do you create an AST node first, only to visit
Michael Lippautz (Google)
2014/08/21 16:39:14
Yes and no :)
I inlined the IL instructions, but
| |
| 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) { | |
|
Florian Schneider
2014/08/21 11:09:29
No need for "else if" if you return inside the if-
Michael Lippautz (Google)
2014/08/21 16:39:14
Done.
| |
| 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() && | |
|
srdjan
2014/08/20 17:50:06
Add parentheses.
Michael Lippautz (Google)
2014/08/20 20:56:06
Done.
| |
| 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() && | |
|
srdjan
2014/08/20 17:50:06
ditto
Michael Lippautz (Google)
2014/08/20 20:56:06
Done.
| |
| 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 int32_t num_await_states = owner()->await_joins()->length(); | |
| 3778 for (int32_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 // Link up the preamble start. | |
| 3801 preamble_start->previous()->set_next(for_test.entry()); | |
| 3802 } | |
| 3803 if (i == (num_await_states - 1)) { | |
| 3804 // Link up preamble end. | |
| 3805 if (exit_ == NULL) { | |
| 3806 exit_ = preamble_start; | |
| 3807 } else { | |
| 3808 exit_->LinkTo(preamble_start); | |
| 3809 } | |
| 3810 } | |
| 3811 } | |
| 3812 entry_ = saved_entry; | |
| 3813 exit_ = saved_exit; | |
| 3814 } | |
| 3815 | |
| 3640 if (is_open()) { | 3816 if (is_open()) { |
| 3641 if (MustSaveRestoreContext(node)) { | 3817 if (MustSaveRestoreContext(node)) { |
| 3642 BuildRestoreContext( | 3818 BuildRestoreContext( |
| 3643 *owner()->parsed_function()->saved_entry_context_var()); | 3819 *owner()->parsed_function()->saved_entry_context_var()); |
| 3644 } else if (num_context_variables > 0) { | 3820 } else if (num_context_variables > 0) { |
| 3645 UnchainContexts(1); | 3821 UnchainContexts(1); |
| 3646 } | 3822 } |
| 3647 } | 3823 } |
| 3648 | 3824 |
| 3649 // If this node sequence is labeled, a break out of the sequence will have | 3825 // 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, | 4181 Report::MessageF(Report::kBailout, |
| 4006 Script::Handle(function.script()), | 4182 Script::Handle(function.script()), |
| 4007 function.token_pos(), | 4183 function.token_pos(), |
| 4008 "FlowGraphBuilder Bailout: %s %s", | 4184 "FlowGraphBuilder Bailout: %s %s", |
| 4009 String::Handle(function.name()).ToCString(), | 4185 String::Handle(function.name()).ToCString(), |
| 4010 reason); | 4186 reason); |
| 4011 UNREACHABLE(); | 4187 UNREACHABLE(); |
| 4012 } | 4188 } |
| 4013 | 4189 |
| 4014 } // namespace dart | 4190 } // namespace dart |
| OLD | NEW |