| 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/compiler.h" | 5 #include "vm/compiler.h" |
| 6 | 6 |
| 7 #include "vm/assembler.h" | 7 #include "vm/assembler.h" |
| 8 | 8 |
| 9 #include "vm/ast_printer.h" | 9 #include "vm/ast_printer.h" |
| 10 #include "vm/block_scheduler.h" | 10 #include "vm/block_scheduler.h" |
| (...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 395 // We may reattempt compilation if the function needs to be assembled using | 395 // We may reattempt compilation if the function needs to be assembled using |
| 396 // far branches on ARM and MIPS. In the else branch of the setjmp call, | 396 // far branches on ARM and MIPS. In the else branch of the setjmp call, |
| 397 // done is set to false, and use_far_branches is set to true if there is a | 397 // done is set to false, and use_far_branches is set to true if there is a |
| 398 // longjmp from the ARM or MIPS assemblers. In all other paths through this | 398 // longjmp from the ARM or MIPS assemblers. In all other paths through this |
| 399 // while loop, done is set to true. use_far_branches is always false on ia32 | 399 // while loop, done is set to true. use_far_branches is always false on ia32 |
| 400 // and x64. | 400 // and x64. |
| 401 bool done = false; | 401 bool done = false; |
| 402 // volatile because the variable may be clobbered by a longjmp. | 402 // volatile because the variable may be clobbered by a longjmp. |
| 403 volatile bool use_far_branches = false; | 403 volatile bool use_far_branches = false; |
| 404 while (!done) { | 404 while (!done) { |
| 405 const intptr_t prev_deopt_id = isolate->deopt_id(); | 405 const intptr_t prev_deopt_id = thread->deopt_id(); |
| 406 isolate->set_deopt_id(0); | 406 thread->set_deopt_id(0); |
| 407 LongJumpScope jump; | 407 LongJumpScope jump; |
| 408 if (setjmp(*jump.Set()) == 0) { | 408 if (setjmp(*jump.Set()) == 0) { |
| 409 FlowGraph* flow_graph = NULL; | 409 FlowGraph* flow_graph = NULL; |
| 410 | 410 |
| 411 // Class hierarchy analysis is registered with the isolate in the | 411 // Class hierarchy analysis is registered with the isolate in the |
| 412 // constructor and unregisters itself upon destruction. | 412 // constructor and unregisters itself upon destruction. |
| 413 CHA cha(thread); | 413 CHA cha(thread); |
| 414 | 414 |
| 415 // TimerScope needs an isolate to be properly terminated in case of a | 415 // TimerScope needs an isolate to be properly terminated in case of a |
| 416 // LongJump. | 416 // LongJump. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 439 *ic_data_array, | 439 *ic_data_array, |
| 440 osr_id); | 440 osr_id); |
| 441 } | 441 } |
| 442 | 442 |
| 443 const bool print_flow_graph = | 443 const bool print_flow_graph = |
| 444 (FLAG_print_flow_graph || | 444 (FLAG_print_flow_graph || |
| 445 (optimized && FLAG_print_flow_graph_optimized)) && | 445 (optimized && FLAG_print_flow_graph_optimized)) && |
| 446 FlowGraphPrinter::ShouldPrint(function); | 446 FlowGraphPrinter::ShouldPrint(function); |
| 447 | 447 |
| 448 if (print_flow_graph) { | 448 if (print_flow_graph) { |
| 449 if (osr_id == Isolate::kNoDeoptId) { | 449 if (osr_id == Thread::kNoDeoptId) { |
| 450 FlowGraphPrinter::PrintGraph("Before Optimizations", flow_graph); | 450 FlowGraphPrinter::PrintGraph("Before Optimizations", flow_graph); |
| 451 } else { | 451 } else { |
| 452 FlowGraphPrinter::PrintGraph("For OSR", flow_graph); | 452 FlowGraphPrinter::PrintGraph("For OSR", flow_graph); |
| 453 } | 453 } |
| 454 } | 454 } |
| 455 | 455 |
| 456 BlockScheduler block_scheduler(flow_graph); | 456 BlockScheduler block_scheduler(flow_graph); |
| 457 const bool reorder_blocks = | 457 const bool reorder_blocks = |
| 458 FlowGraph::ShouldReorderBlocks(function, optimized); | 458 FlowGraph::ShouldReorderBlocks(function, optimized); |
| 459 if (reorder_blocks) { | 459 if (reorder_blocks) { |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 750 graph_compiler.FinalizePcDescriptors(code); | 750 graph_compiler.FinalizePcDescriptors(code); |
| 751 code.set_deopt_info_array(deopt_info_array); | 751 code.set_deopt_info_array(deopt_info_array); |
| 752 | 752 |
| 753 graph_compiler.FinalizeStackmaps(code); | 753 graph_compiler.FinalizeStackmaps(code); |
| 754 graph_compiler.FinalizeVarDescriptors(code); | 754 graph_compiler.FinalizeVarDescriptors(code); |
| 755 graph_compiler.FinalizeExceptionHandlers(code); | 755 graph_compiler.FinalizeExceptionHandlers(code); |
| 756 graph_compiler.FinalizeStaticCallTargetsTable(code); | 756 graph_compiler.FinalizeStaticCallTargetsTable(code); |
| 757 | 757 |
| 758 if (optimized) { | 758 if (optimized) { |
| 759 // We may not have previous code if 'always_optimize' is set. | 759 // We may not have previous code if 'always_optimize' is set. |
| 760 if ((osr_id == Isolate::kNoDeoptId) && function.HasCode()) { | 760 if ((osr_id == Thread::kNoDeoptId) && function.HasCode()) { |
| 761 Code::Handle(function.CurrentCode()).DisableDartCode(); | 761 Code::Handle(function.CurrentCode()).DisableDartCode(); |
| 762 } | 762 } |
| 763 function.AttachCode(code); | 763 function.AttachCode(code); |
| 764 | 764 |
| 765 // Register code with the classes it depends on because of CHA. | 765 // Register code with the classes it depends on because of CHA. |
| 766 for (intptr_t i = 0; | 766 for (intptr_t i = 0; |
| 767 i < thread->cha()->leaf_classes().length(); | 767 i < thread->cha()->leaf_classes().length(); |
| 768 ++i) { | 768 ++i) { |
| 769 thread->cha()->leaf_classes()[i]->RegisterCHACode(code); | 769 thread->cha()->leaf_classes()[i]->RegisterCHACode(code); |
| 770 } | 770 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 788 if (parsed_function->HasDeferredPrefixes()) { | 788 if (parsed_function->HasDeferredPrefixes()) { |
| 789 ASSERT(!FLAG_load_deferred_eagerly); | 789 ASSERT(!FLAG_load_deferred_eagerly); |
| 790 ZoneGrowableArray<const LibraryPrefix*>* prefixes = | 790 ZoneGrowableArray<const LibraryPrefix*>* prefixes = |
| 791 parsed_function->deferred_prefixes(); | 791 parsed_function->deferred_prefixes(); |
| 792 for (intptr_t i = 0; i < prefixes->length(); i++) { | 792 for (intptr_t i = 0; i < prefixes->length(); i++) { |
| 793 (*prefixes)[i]->RegisterDependentCode(code); | 793 (*prefixes)[i]->RegisterDependentCode(code); |
| 794 } | 794 } |
| 795 } | 795 } |
| 796 } | 796 } |
| 797 // Mark that this isolate now has compiled code. | 797 // Mark that this isolate now has compiled code. |
| 798 isolate->set_has_compiled(true); | 798 isolate->set_has_compiled_code(true); |
| 799 // Exit the loop and the function with the correct result value. | 799 // Exit the loop and the function with the correct result value. |
| 800 is_compiled = true; | 800 is_compiled = true; |
| 801 done = true; | 801 done = true; |
| 802 } else { | 802 } else { |
| 803 // We bailed out or we encountered an error. | 803 // We bailed out or we encountered an error. |
| 804 const Error& error = Error::Handle( | 804 const Error& error = Error::Handle( |
| 805 isolate->object_store()->sticky_error()); | 805 isolate->object_store()->sticky_error()); |
| 806 | 806 |
| 807 if (error.raw() == Object::branch_offset_error().raw()) { | 807 if (error.raw() == Object::branch_offset_error().raw()) { |
| 808 // Compilation failed due to an out of range branch offset in the | 808 // Compilation failed due to an out of range branch offset in the |
| (...skipping 13 matching lines...) Expand all Loading... |
| 822 } | 822 } |
| 823 | 823 |
| 824 // Clear the error if it was not a real error, but just a bailout. | 824 // Clear the error if it was not a real error, but just a bailout. |
| 825 if (error.IsLanguageError() && | 825 if (error.IsLanguageError() && |
| 826 (LanguageError::Cast(error).kind() == Report::kBailout)) { | 826 (LanguageError::Cast(error).kind() == Report::kBailout)) { |
| 827 isolate->object_store()->clear_sticky_error(); | 827 isolate->object_store()->clear_sticky_error(); |
| 828 } | 828 } |
| 829 is_compiled = false; | 829 is_compiled = false; |
| 830 } | 830 } |
| 831 // Reset global isolate state. | 831 // Reset global isolate state. |
| 832 isolate->set_deopt_id(prev_deopt_id); | 832 thread->set_deopt_id(prev_deopt_id); |
| 833 } | 833 } |
| 834 return is_compiled; | 834 return is_compiled; |
| 835 } | 835 } |
| 836 | 836 |
| 837 | 837 |
| 838 static void DisassembleCode(const Function& function, bool optimized) { | 838 static void DisassembleCode(const Function& function, bool optimized) { |
| 839 const char* function_fullname = function.ToFullyQualifiedCString(); | 839 const char* function_fullname = function.ToFullyQualifiedCString(); |
| 840 THR_Print("Code for %sfunction '%s' {\n", | 840 THR_Print("Code for %sfunction '%s' {\n", |
| 841 optimized ? "optimized " : "", | 841 optimized ? "optimized " : "", |
| 842 function_fullname); | 842 function_fullname); |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1011 Isolate* const isolate = thread->isolate(); | 1011 Isolate* const isolate = thread->isolate(); |
| 1012 StackZone stack_zone(thread); | 1012 StackZone stack_zone(thread); |
| 1013 Zone* const zone = stack_zone.GetZone(); | 1013 Zone* const zone = stack_zone.GetZone(); |
| 1014 Timer per_compile_timer(FLAG_trace_compiler, "Compilation time"); | 1014 Timer per_compile_timer(FLAG_trace_compiler, "Compilation time"); |
| 1015 per_compile_timer.Start(); | 1015 per_compile_timer.Start(); |
| 1016 | 1016 |
| 1017 ParsedFunction* parsed_function = new(zone) ParsedFunction( | 1017 ParsedFunction* parsed_function = new(zone) ParsedFunction( |
| 1018 thread, Function::ZoneHandle(zone, function.raw())); | 1018 thread, Function::ZoneHandle(zone, function.raw())); |
| 1019 if (FLAG_trace_compiler) { | 1019 if (FLAG_trace_compiler) { |
| 1020 THR_Print("Compiling %s%sfunction: '%s' @ token %" Pd ", size %" Pd "\n", | 1020 THR_Print("Compiling %s%sfunction: '%s' @ token %" Pd ", size %" Pd "\n", |
| 1021 (osr_id == Isolate::kNoDeoptId ? "" : "osr "), | 1021 (osr_id == Thread::kNoDeoptId ? "" : "osr "), |
| 1022 (optimized ? "optimized " : ""), | 1022 (optimized ? "optimized " : ""), |
| 1023 function.ToFullyQualifiedCString(), | 1023 function.ToFullyQualifiedCString(), |
| 1024 function.token_pos(), | 1024 function.token_pos(), |
| 1025 (function.end_token_pos() - function.token_pos())); | 1025 (function.end_token_pos() - function.token_pos())); |
| 1026 } | 1026 } |
| 1027 INC_STAT(thread, num_functions_compiled, 1); | 1027 INC_STAT(thread, num_functions_compiled, 1); |
| 1028 if (optimized) { | 1028 if (optimized) { |
| 1029 INC_STAT(thread, num_functions_optimized, 1); | 1029 INC_STAT(thread, num_functions_optimized, 1); |
| 1030 } | 1030 } |
| 1031 { | 1031 { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1113 Function::KindToCString(function.kind())); | 1113 Function::KindToCString(function.kind())); |
| 1114 } | 1114 } |
| 1115 | 1115 |
| 1116 CompilationPipeline* pipeline = | 1116 CompilationPipeline* pipeline = |
| 1117 CompilationPipeline::New(thread->zone(), function); | 1117 CompilationPipeline::New(thread->zone(), function); |
| 1118 | 1118 |
| 1119 const bool optimized = | 1119 const bool optimized = |
| 1120 Compiler::always_optimize() && function.IsOptimizable(); | 1120 Compiler::always_optimize() && function.IsOptimizable(); |
| 1121 | 1121 |
| 1122 return CompileFunctionHelper(pipeline, function, optimized, | 1122 return CompileFunctionHelper(pipeline, function, optimized, |
| 1123 Isolate::kNoDeoptId); | 1123 Thread::kNoDeoptId); |
| 1124 } | 1124 } |
| 1125 | 1125 |
| 1126 | 1126 |
| 1127 RawError* Compiler::EnsureUnoptimizedCode(Thread* thread, | 1127 RawError* Compiler::EnsureUnoptimizedCode(Thread* thread, |
| 1128 const Function& function) { | 1128 const Function& function) { |
| 1129 if (function.unoptimized_code() != Object::null()) { | 1129 if (function.unoptimized_code() != Object::null()) { |
| 1130 return Error::null(); | 1130 return Error::null(); |
| 1131 } | 1131 } |
| 1132 Code& original_code = Code::ZoneHandle(thread->zone()); | 1132 Code& original_code = Code::ZoneHandle(thread->zone()); |
| 1133 if (function.HasCode()) { | 1133 if (function.HasCode()) { |
| 1134 original_code = function.CurrentCode(); | 1134 original_code = function.CurrentCode(); |
| 1135 } | 1135 } |
| 1136 CompilationPipeline* pipeline = | 1136 CompilationPipeline* pipeline = |
| 1137 CompilationPipeline::New(thread->zone(), function); | 1137 CompilationPipeline::New(thread->zone(), function); |
| 1138 const Error& error = Error::Handle( | 1138 const Error& error = Error::Handle( |
| 1139 CompileFunctionHelper(pipeline, function, false, Isolate::kNoDeoptId)); | 1139 CompileFunctionHelper(pipeline, function, false, Thread::kNoDeoptId)); |
| 1140 if (!error.IsNull()) { | 1140 if (!error.IsNull()) { |
| 1141 return error.raw(); | 1141 return error.raw(); |
| 1142 } | 1142 } |
| 1143 // Since CompileFunctionHelper replaces the current code, re-attach the | 1143 // Since CompileFunctionHelper replaces the current code, re-attach the |
| 1144 // the original code if the function was already compiled. | 1144 // the original code if the function was already compiled. |
| 1145 if (!original_code.IsNull() && | 1145 if (!original_code.IsNull() && |
| 1146 (original_code.raw() != function.CurrentCode())) { | 1146 (original_code.raw() != function.CurrentCode())) { |
| 1147 function.AttachCode(original_code); | 1147 function.AttachCode(original_code); |
| 1148 } | 1148 } |
| 1149 ASSERT(function.unoptimized_code() != Object::null()); | 1149 ASSERT(function.unoptimized_code() != Object::null()); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 1170 // This is only used from unit tests. | 1170 // This is only used from unit tests. |
| 1171 RawError* Compiler::CompileParsedFunction( | 1171 RawError* Compiler::CompileParsedFunction( |
| 1172 ParsedFunction* parsed_function) { | 1172 ParsedFunction* parsed_function) { |
| 1173 LongJumpScope jump; | 1173 LongJumpScope jump; |
| 1174 if (setjmp(*jump.Set()) == 0) { | 1174 if (setjmp(*jump.Set()) == 0) { |
| 1175 // Non-optimized code generator. | 1175 // Non-optimized code generator. |
| 1176 DartCompilationPipeline pipeline; | 1176 DartCompilationPipeline pipeline; |
| 1177 CompileParsedFunctionHelper(&pipeline, | 1177 CompileParsedFunctionHelper(&pipeline, |
| 1178 parsed_function, | 1178 parsed_function, |
| 1179 false, | 1179 false, |
| 1180 Isolate::kNoDeoptId); | 1180 Thread::kNoDeoptId); |
| 1181 if (FLAG_disassemble) { | 1181 if (FLAG_disassemble) { |
| 1182 DisassembleCode(parsed_function->function(), false); | 1182 DisassembleCode(parsed_function->function(), false); |
| 1183 } | 1183 } |
| 1184 return Error::null(); | 1184 return Error::null(); |
| 1185 } else { | 1185 } else { |
| 1186 Isolate* const isolate = Isolate::Current(); | 1186 Isolate* const isolate = Isolate::Current(); |
| 1187 Error& error = Error::Handle(); | 1187 Error& error = Error::Handle(); |
| 1188 // We got an error during compilation. | 1188 // We got an error during compilation. |
| 1189 error = isolate->object_store()->sticky_error(); | 1189 error = isolate->object_store()->sticky_error(); |
| 1190 isolate->object_store()->clear_sticky_error(); | 1190 isolate->object_store()->clear_sticky_error(); |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1276 StackZone zone(thread); | 1276 StackZone zone(thread); |
| 1277 | 1277 |
| 1278 ParsedFunction* parsed_function = Parser::ParseStaticFieldInitializer(field); | 1278 ParsedFunction* parsed_function = Parser::ParseStaticFieldInitializer(field); |
| 1279 | 1279 |
| 1280 parsed_function->AllocateVariables(); | 1280 parsed_function->AllocateVariables(); |
| 1281 // Non-optimized code generator. | 1281 // Non-optimized code generator. |
| 1282 DartCompilationPipeline pipeline; | 1282 DartCompilationPipeline pipeline; |
| 1283 CompileParsedFunctionHelper(&pipeline, | 1283 CompileParsedFunctionHelper(&pipeline, |
| 1284 parsed_function, | 1284 parsed_function, |
| 1285 false, // optimized | 1285 false, // optimized |
| 1286 Isolate::kNoDeoptId); | 1286 Thread::kNoDeoptId); |
| 1287 | 1287 |
| 1288 const Function& initializer = parsed_function->function(); | 1288 const Function& initializer = parsed_function->function(); |
| 1289 field.SetPrecompiledInitializer(initializer); | 1289 field.SetPrecompiledInitializer(initializer); |
| 1290 } | 1290 } |
| 1291 | 1291 |
| 1292 | 1292 |
| 1293 RawObject* Compiler::EvaluateStaticInitializer(const Field& field) { | 1293 RawObject* Compiler::EvaluateStaticInitializer(const Field& field) { |
| 1294 ASSERT(field.is_static()); | 1294 ASSERT(field.is_static()); |
| 1295 // The VM sets the field's value to transiton_sentinel prior to | 1295 // The VM sets the field's value to transiton_sentinel prior to |
| 1296 // evaluating the initializer value. | 1296 // evaluating the initializer value. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1307 StackZone zone(thread); | 1307 StackZone zone(thread); |
| 1308 ParsedFunction* parsed_function = | 1308 ParsedFunction* parsed_function = |
| 1309 Parser::ParseStaticFieldInitializer(field); | 1309 Parser::ParseStaticFieldInitializer(field); |
| 1310 | 1310 |
| 1311 parsed_function->AllocateVariables(); | 1311 parsed_function->AllocateVariables(); |
| 1312 // Non-optimized code generator. | 1312 // Non-optimized code generator. |
| 1313 DartCompilationPipeline pipeline; | 1313 DartCompilationPipeline pipeline; |
| 1314 CompileParsedFunctionHelper(&pipeline, | 1314 CompileParsedFunctionHelper(&pipeline, |
| 1315 parsed_function, | 1315 parsed_function, |
| 1316 false, // optimized | 1316 false, // optimized |
| 1317 Isolate::kNoDeoptId); | 1317 Thread::kNoDeoptId); |
| 1318 initializer = parsed_function->function().raw(); | 1318 initializer = parsed_function->function().raw(); |
| 1319 Code::Handle(initializer.unoptimized_code()).set_var_descriptors( | 1319 Code::Handle(initializer.unoptimized_code()).set_var_descriptors( |
| 1320 Object::empty_var_descriptors()); | 1320 Object::empty_var_descriptors()); |
| 1321 } else { | 1321 } else { |
| 1322 initializer ^= field.PrecompiledInitializer(); | 1322 initializer ^= field.PrecompiledInitializer(); |
| 1323 } | 1323 } |
| 1324 // Invoke the function to evaluate the expression. | 1324 // Invoke the function to evaluate the expression. |
| 1325 return DartEntry::InvokeFunction(initializer, Object::empty_array()); | 1325 return DartEntry::InvokeFunction(initializer, Object::empty_array()); |
| 1326 } else { | 1326 } else { |
| 1327 Thread* const thread = Thread::Current(); | 1327 Thread* const thread = Thread::Current(); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1377 fragment->scope()->AddVariable(parsed_function->EnsureExpressionTemp()); | 1377 fragment->scope()->AddVariable(parsed_function->EnsureExpressionTemp()); |
| 1378 fragment->scope()->AddVariable( | 1378 fragment->scope()->AddVariable( |
| 1379 parsed_function->current_context_var()); | 1379 parsed_function->current_context_var()); |
| 1380 parsed_function->AllocateVariables(); | 1380 parsed_function->AllocateVariables(); |
| 1381 | 1381 |
| 1382 // Non-optimized code generator. | 1382 // Non-optimized code generator. |
| 1383 DartCompilationPipeline pipeline; | 1383 DartCompilationPipeline pipeline; |
| 1384 CompileParsedFunctionHelper(&pipeline, | 1384 CompileParsedFunctionHelper(&pipeline, |
| 1385 parsed_function, | 1385 parsed_function, |
| 1386 false, | 1386 false, |
| 1387 Isolate::kNoDeoptId); | 1387 Thread::kNoDeoptId); |
| 1388 Code::Handle(func.unoptimized_code()).set_var_descriptors( | 1388 Code::Handle(func.unoptimized_code()).set_var_descriptors( |
| 1389 Object::empty_var_descriptors()); | 1389 Object::empty_var_descriptors()); |
| 1390 | 1390 |
| 1391 const Object& result = PassiveObject::Handle( | 1391 const Object& result = PassiveObject::Handle( |
| 1392 DartEntry::InvokeFunction(func, Object::empty_array())); | 1392 DartEntry::InvokeFunction(func, Object::empty_array())); |
| 1393 return result.raw(); | 1393 return result.raw(); |
| 1394 } else { | 1394 } else { |
| 1395 Thread* const thread = Thread::Current(); | 1395 Thread* const thread = Thread::Current(); |
| 1396 Isolate* const isolate = thread->isolate(); | 1396 Isolate* const isolate = thread->isolate(); |
| 1397 const Object& result = | 1397 const Object& result = |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1557 isolate->current_zone(), GrowableObjectArray::New())); | 1557 isolate->current_zone(), GrowableObjectArray::New())); |
| 1558 start_task = true; | 1558 start_task = true; |
| 1559 } | 1559 } |
| 1560 } | 1560 } |
| 1561 if (start_task) { | 1561 if (start_task) { |
| 1562 Dart::thread_pool()->Run(isolate->background_compiler()); | 1562 Dart::thread_pool()->Run(isolate->background_compiler()); |
| 1563 } | 1563 } |
| 1564 } | 1564 } |
| 1565 | 1565 |
| 1566 } // namespace dart | 1566 } // namespace dart |
| OLD | NEW |