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/code_generator.h" | 5 #include "vm/code_generator.h" |
6 | 6 |
7 #include "vm/assembler_macros.h" | 7 #include "vm/assembler_macros.h" |
8 #include "vm/ast.h" | 8 #include "vm/ast.h" |
9 #include "vm/code_patcher.h" | 9 #include "vm/code_patcher.h" |
10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
353 const AbstractType& type, | 353 const AbstractType& type, |
354 const AbstractTypeArguments& instantiator_type_arguments, | 354 const AbstractTypeArguments& instantiator_type_arguments, |
355 const Bool& result) { | 355 const Bool& result) { |
356 DartFrameIterator iterator; | 356 DartFrameIterator iterator; |
357 StackFrame* caller_frame = iterator.NextFrame(); | 357 StackFrame* caller_frame = iterator.NextFrame(); |
358 ASSERT(caller_frame != NULL); | 358 ASSERT(caller_frame != NULL); |
359 | 359 |
360 const Type& instance_type = Type::Handle(instance.GetType()); | 360 const Type& instance_type = Type::Handle(instance.GetType()); |
361 ASSERT(instance_type.IsInstantiated()); | 361 ASSERT(instance_type.IsInstantiated()); |
362 if (type.IsInstantiated()) { | 362 if (type.IsInstantiated()) { |
363 OS::Print("%s: '%s' %d %s '%s' %d (pc: 0x%x).\n", | 363 OS::Print("%s: '%s' %"Pd" %s '%s' %"Pd" (pc: %#"Px").\n", |
364 message, | 364 message, |
365 String::Handle(instance_type.Name()).ToCString(), | 365 String::Handle(instance_type.Name()).ToCString(), |
366 Class::Handle(instance_type.type_class()).id(), | 366 Class::Handle(instance_type.type_class()).id(), |
367 (result.raw() == Bool::True()) ? "is" : "is !", | 367 (result.raw() == Bool::True()) ? "is" : "is !", |
368 String::Handle(type.Name()).ToCString(), | 368 String::Handle(type.Name()).ToCString(), |
369 Class::Handle(type.type_class()).id(), | 369 Class::Handle(type.type_class()).id(), |
370 caller_frame->pc()); | 370 caller_frame->pc()); |
371 } else { | 371 } else { |
372 // Instantiate type before printing. | 372 // Instantiate type before printing. |
373 const AbstractType& instantiated_type = | 373 const AbstractType& instantiated_type = |
374 AbstractType::Handle(type.InstantiateFrom(instantiator_type_arguments)); | 374 AbstractType::Handle(type.InstantiateFrom(instantiator_type_arguments)); |
375 OS::Print("%s: '%s' %s '%s' instantiated from '%s' (pc: 0x%x).\n", | 375 OS::Print("%s: '%s' %s '%s' instantiated from '%s' (pc: %#"Px").\n", |
376 message, | 376 message, |
377 String::Handle(instance_type.Name()).ToCString(), | 377 String::Handle(instance_type.Name()).ToCString(), |
378 (result.raw() == Bool::True()) ? "is" : "is !", | 378 (result.raw() == Bool::True()) ? "is" : "is !", |
379 String::Handle(instantiated_type.Name()).ToCString(), | 379 String::Handle(instantiated_type.Name()).ToCString(), |
380 String::Handle(type.Name()).ToCString(), | 380 String::Handle(type.Name()).ToCString(), |
381 caller_frame->pc()); | 381 caller_frame->pc()); |
382 } | 382 } |
383 const Function& function = Function::Handle( | 383 const Function& function = Function::Handle( |
384 caller_frame->LookupDartFunction()); | 384 caller_frame->LookupDartFunction()); |
385 OS::Print(" -> Function %s\n", function.ToFullyQualifiedCString()); | 385 OS::Print(" -> Function %s\n", function.ToFullyQualifiedCString()); |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
497 i, | 497 i, |
498 &last_instance_class_id, | 498 &last_instance_class_id, |
499 &last_instance_type_arguments, | 499 &last_instance_type_arguments, |
500 &last_instantiator_type_arguments, | 500 &last_instantiator_type_arguments, |
501 &last_result); | 501 &last_result); |
502 if ((last_instance_class_id == instance_class.id()) && | 502 if ((last_instance_class_id == instance_class.id()) && |
503 (last_instance_type_arguments.raw() == instance_type_arguments.raw()) && | 503 (last_instance_type_arguments.raw() == instance_type_arguments.raw()) && |
504 (last_instantiator_type_arguments.raw() == | 504 (last_instantiator_type_arguments.raw() == |
505 instantiator_type_arguments.raw())) { | 505 instantiator_type_arguments.raw())) { |
506 if (FLAG_trace_type_checks) { | 506 if (FLAG_trace_type_checks) { |
507 OS::Print("%d ", i); | 507 OS::Print("%"Pd" ", i); |
508 if (type_arguments_replaced) { | 508 if (type_arguments_replaced) { |
509 PrintTypeCheck("Duplicate cache entry (canonical.)", instance, type, | 509 PrintTypeCheck("Duplicate cache entry (canonical.)", instance, type, |
510 instantiator_type_arguments, result); | 510 instantiator_type_arguments, result); |
511 } else { | 511 } else { |
512 PrintTypeCheck("WARNING Duplicate cache entry", instance, type, | 512 PrintTypeCheck("WARNING Duplicate cache entry", instance, type, |
513 instantiator_type_arguments, result); | 513 instantiator_type_arguments, result); |
514 } | 514 } |
515 } | 515 } |
516 // Can occur if we have canonicalized arguments. | 516 // Can occur if we have canonicalized arguments. |
517 // TODO(srdjan): Investigate why this assert can fail. | 517 // TODO(srdjan): Investigate why this assert can fail. |
518 // ASSERT(type_arguments_replaced); | 518 // ASSERT(type_arguments_replaced); |
519 return; | 519 return; |
520 } | 520 } |
521 } | 521 } |
522 new_cache.AddCheck(instance_class.id(), | 522 new_cache.AddCheck(instance_class.id(), |
523 instance_type_arguments, | 523 instance_type_arguments, |
524 instantiator_type_arguments, | 524 instantiator_type_arguments, |
525 result); | 525 result); |
526 if (FLAG_trace_type_checks) { | 526 if (FLAG_trace_type_checks) { |
527 AbstractType& test_type = AbstractType::Handle(type.raw()); | 527 AbstractType& test_type = AbstractType::Handle(type.raw()); |
528 if (!test_type.IsInstantiated()) { | 528 if (!test_type.IsInstantiated()) { |
529 test_type = type.InstantiateFrom(instantiator_type_arguments); | 529 test_type = type.InstantiateFrom(instantiator_type_arguments); |
530 } | 530 } |
531 OS::Print(" Updated test cache 0x%x ix:%d:\n" | 531 OS::Print(" Updated test cache %p ix:%"Pd":\n" |
532 " [0x%x %s %d, 0x%x %s]\n" | 532 " [%p %s %"Pd", %p %s]\n" |
533 " [0x%x %s %d, 0x%x %s] %s\n", | 533 " [%p %s %"Pd", %p %s] %s\n", |
534 new_cache.raw(), | 534 new_cache.raw(), |
535 len, | 535 len, |
536 instance_class.raw(), | 536 instance_class.raw(), |
537 instance_class.ToCString(), | 537 instance_class.ToCString(), |
538 instance_class.id(), | 538 instance_class.id(), |
539 instance_type_arguments.raw(), | 539 instance_type_arguments.raw(), |
540 instance_type_arguments.ToCString(), | 540 instance_type_arguments.ToCString(), |
541 test_type.type_class(), | 541 test_type.type_class(), |
542 Class::Handle(test_type.type_class()).ToCString(), | 542 Class::Handle(test_type.type_class()).ToCString(), |
543 Class::Handle(test_type.type_class()).id(), | 543 Class::Handle(test_type.type_class()).id(), |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
753 ASSERT(caller_frame != NULL); | 753 ASSERT(caller_frame != NULL); |
754 uword target = 0; | 754 uword target = 0; |
755 Function& target_function = Function::Handle(); | 755 Function& target_function = Function::Handle(); |
756 CodePatcher::GetStaticCallAt(caller_frame->pc(), &target_function, &target); | 756 CodePatcher::GetStaticCallAt(caller_frame->pc(), &target_function, &target); |
757 ASSERT(target_function.HasCode()); | 757 ASSERT(target_function.HasCode()); |
758 uword new_target = Code::Handle(target_function.CurrentCode()).EntryPoint(); | 758 uword new_target = Code::Handle(target_function.CurrentCode()).EntryPoint(); |
759 // Verify that we are not patching repeatedly. | 759 // Verify that we are not patching repeatedly. |
760 ASSERT(target != new_target); | 760 ASSERT(target != new_target); |
761 CodePatcher::PatchStaticCallAt(caller_frame->pc(), new_target); | 761 CodePatcher::PatchStaticCallAt(caller_frame->pc(), new_target); |
762 if (FLAG_trace_patching) { | 762 if (FLAG_trace_patching) { |
763 OS::Print("PatchStaticCall: patching from 0x%x to '%s' 0x%x\n", | 763 OS::Print("PatchStaticCall: patching from %#"Px" to '%s' %#"Px"\n", |
764 caller_frame->pc(), | 764 caller_frame->pc(), |
765 target_function.ToFullyQualifiedCString(), | 765 target_function.ToFullyQualifiedCString(), |
766 new_target); | 766 new_target); |
767 } | 767 } |
768 } | 768 } |
769 | 769 |
770 | 770 |
771 // Resolves and compiles the target function of an instance call, updates | 771 // Resolves and compiles the target function of an instance call, updates |
772 // function cache of the receiver's class and returns the compiled code or null. | 772 // function cache of the receiver's class and returns the compiled code or null. |
773 // Only the number of named arguments is checked, but not the actual names. | 773 // Only the number of named arguments is checked, but not the actual names. |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
914 target_function); | 914 target_function); |
915 } else { | 915 } else { |
916 GrowableArray<intptr_t> class_ids(args.length()); | 916 GrowableArray<intptr_t> class_ids(args.length()); |
917 ASSERT(ic_data.num_args_tested() == args.length()); | 917 ASSERT(ic_data.num_args_tested() == args.length()); |
918 for (intptr_t i = 0; i < args.length(); i++) { | 918 for (intptr_t i = 0; i < args.length(); i++) { |
919 class_ids.Add(Class::Handle(args[i]->clazz()).id()); | 919 class_ids.Add(Class::Handle(args[i]->clazz()).id()); |
920 } | 920 } |
921 ic_data.AddCheck(class_ids, target_function); | 921 ic_data.AddCheck(class_ids, target_function); |
922 } | 922 } |
923 if (FLAG_trace_ic) { | 923 if (FLAG_trace_ic) { |
924 OS::Print("InlineCacheMissHandler %d call at 0x%x' " | 924 OS::Print("InlineCacheMissHandler %d call at %#"Px"' " |
925 "adding <%s> id:%d -> <%s>\n", | 925 "adding <%s> id:%"Pd" -> <%s>\n", |
926 args.length(), | 926 args.length(), |
927 caller_frame->pc(), | 927 caller_frame->pc(), |
928 Class::Handle(receiver.clazz()).ToCString(), | 928 Class::Handle(receiver.clazz()).ToCString(), |
929 Class::Handle(receiver.clazz()).id(), | 929 Class::Handle(receiver.clazz()).id(), |
930 target_function.ToCString()); | 930 target_function.ToCString()); |
931 } | 931 } |
932 return target_function.raw(); | 932 return target_function.raw(); |
933 } | 933 } |
934 | 934 |
935 | 935 |
(...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1276 } | 1276 } |
1277 } | 1277 } |
1278 | 1278 |
1279 | 1279 |
1280 static void PrintCaller(const char* msg) { | 1280 static void PrintCaller(const char* msg) { |
1281 DartFrameIterator iterator; | 1281 DartFrameIterator iterator; |
1282 StackFrame* top_frame = iterator.NextFrame(); | 1282 StackFrame* top_frame = iterator.NextFrame(); |
1283 ASSERT(top_frame != NULL); | 1283 ASSERT(top_frame != NULL); |
1284 const Function& top_function = Function::Handle( | 1284 const Function& top_function = Function::Handle( |
1285 top_frame->LookupDartFunction()); | 1285 top_frame->LookupDartFunction()); |
1286 OS::Print("Failed: '%s' %s @ 0x%x\n", | 1286 OS::Print("Failed: '%s' %s @ %#"Px"\n", |
1287 msg, top_function.ToFullyQualifiedCString(), top_frame->pc()); | 1287 msg, top_function.ToFullyQualifiedCString(), top_frame->pc()); |
1288 StackFrame* caller_frame = iterator.NextFrame(); | 1288 StackFrame* caller_frame = iterator.NextFrame(); |
1289 if (caller_frame != NULL) { | 1289 if (caller_frame != NULL) { |
1290 const Function& caller_function = Function::Handle( | 1290 const Function& caller_function = Function::Handle( |
1291 caller_frame->LookupDartFunction()); | 1291 caller_frame->LookupDartFunction()); |
1292 const Code& code = Code::Handle(caller_frame->LookupDartCode()); | 1292 const Code& code = Code::Handle(caller_frame->LookupDartCode()); |
1293 OS::Print(" -> caller: %s (%s)\n", | 1293 OS::Print(" -> caller: %s (%s)\n", |
1294 caller_function.ToFullyQualifiedCString(), | 1294 caller_function.ToFullyQualifiedCString(), |
1295 code.is_optimized() ? "optimized" : "unoptimized"); | 1295 code.is_optimized() ? "optimized" : "unoptimized"); |
1296 } | 1296 } |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1378 ASSERT(frame->IsDartFrame()); | 1378 ASSERT(frame->IsDartFrame()); |
1379 uword target = 0; | 1379 uword target = 0; |
1380 Function& target_function = Function::Handle(); | 1380 Function& target_function = Function::Handle(); |
1381 CodePatcher::GetStaticCallAt(frame->pc(), &target_function, &target); | 1381 CodePatcher::GetStaticCallAt(frame->pc(), &target_function, &target); |
1382 ASSERT(target_function.HasCode()); | 1382 ASSERT(target_function.HasCode()); |
1383 const uword new_entry_point = | 1383 const uword new_entry_point = |
1384 Code::Handle(function.CurrentCode()).EntryPoint(); | 1384 Code::Handle(function.CurrentCode()).EntryPoint(); |
1385 ASSERT(target != new_entry_point); // Why patch otherwise. | 1385 ASSERT(target != new_entry_point); // Why patch otherwise. |
1386 CodePatcher::PatchStaticCallAt(frame->pc(), new_entry_point); | 1386 CodePatcher::PatchStaticCallAt(frame->pc(), new_entry_point); |
1387 if (FLAG_trace_patching) { | 1387 if (FLAG_trace_patching) { |
1388 OS::Print("FixCallersTarget: patching from 0x%x to '%s' 0x%x\n", | 1388 OS::Print("FixCallersTarget: patching from %#"Px" to '%s' %#"Px"\n", |
1389 frame->pc(), | 1389 frame->pc(), |
1390 target_function.ToFullyQualifiedCString(), | 1390 target_function.ToFullyQualifiedCString(), |
1391 new_entry_point); | 1391 new_entry_point); |
1392 } | 1392 } |
1393 } | 1393 } |
1394 } | 1394 } |
1395 | 1395 |
1396 | 1396 |
1397 static const char* DeoptReasonToText(intptr_t deopt_id) { | 1397 static const char* DeoptReasonToText(intptr_t deopt_id) { |
1398 switch (deopt_id) { | 1398 switch (deopt_id) { |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1529 intptr_t deopt_id, deopt_reason, deopt_index; | 1529 intptr_t deopt_id, deopt_reason, deopt_index; |
1530 GetDeoptIxDescrAtPc(optimized_code, caller_frame->pc(), | 1530 GetDeoptIxDescrAtPc(optimized_code, caller_frame->pc(), |
1531 &deopt_id, &deopt_reason, &deopt_index); | 1531 &deopt_id, &deopt_reason, &deopt_index); |
1532 ASSERT(deopt_id != Isolate::kNoDeoptId); | 1532 ASSERT(deopt_id != Isolate::kNoDeoptId); |
1533 | 1533 |
1534 CopyFrame(optimized_code, *caller_frame); | 1534 CopyFrame(optimized_code, *caller_frame); |
1535 if (FLAG_trace_deopt) { | 1535 if (FLAG_trace_deopt) { |
1536 intptr_t deopt_id, deopt_reason, deopt_index; | 1536 intptr_t deopt_id, deopt_reason, deopt_index; |
1537 GetDeoptIxDescrAtPc(optimized_code, caller_frame->pc(), | 1537 GetDeoptIxDescrAtPc(optimized_code, caller_frame->pc(), |
1538 &deopt_id, &deopt_reason, &deopt_index); | 1538 &deopt_id, &deopt_reason, &deopt_index); |
1539 OS::Print("Deoptimizing (reason %d '%s') at pc 0x%x id %d '%s'\n", | 1539 OS::Print("Deoptimizing (reason %"Pd" '%s') at pc %#"Px" id %"Pd" '%s'\n", |
1540 deopt_reason, | 1540 deopt_reason, |
1541 DeoptReasonToText(deopt_reason), | 1541 DeoptReasonToText(deopt_reason), |
1542 caller_frame->pc(), | 1542 caller_frame->pc(), |
1543 deopt_id, | 1543 deopt_id, |
1544 Function::Handle(optimized_code.function()).ToFullyQualifiedCString()); | 1544 Function::Handle(optimized_code.function()).ToFullyQualifiedCString()); |
1545 } | 1545 } |
1546 | 1546 |
1547 // Compute the stack size of unoptimized frame | 1547 // Compute the stack size of unoptimized frame |
1548 const Array& deopt_info_array = | 1548 const Array& deopt_info_array = |
1549 Array::Handle(optimized_code.deopt_info_array()); | 1549 Array::Handle(optimized_code.deopt_info_array()); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1586 + num_args; | 1586 + num_args; |
1587 DeoptimizationContext deopt_context(start, | 1587 DeoptimizationContext deopt_context(start, |
1588 to_frame_size, | 1588 to_frame_size, |
1589 Array::Handle(code.object_table()), | 1589 Array::Handle(code.object_table()), |
1590 num_args); | 1590 num_args); |
1591 for (intptr_t to_index = 0; to_index < len; to_index++) { | 1591 for (intptr_t to_index = 0; to_index < len; to_index++) { |
1592 deopt_instructions[to_index]->Execute(&deopt_context, to_index); | 1592 deopt_instructions[to_index]->Execute(&deopt_context, to_index); |
1593 } | 1593 } |
1594 if (FLAG_trace_deopt) { | 1594 if (FLAG_trace_deopt) { |
1595 for (intptr_t i = 0; i < len; i++) { | 1595 for (intptr_t i = 0; i < len; i++) { |
1596 OS::Print("*%d. [0x%0" PRIxPTR "] 0x%012" PRIxPTR " [%s]\n", | 1596 OS::Print("*%"Pd". [%p] %#014"Px" [%s]\n", |
1597 i, | 1597 i, |
1598 &start[i], | 1598 &start[i], |
1599 start[i], | 1599 start[i], |
1600 deopt_instructions[i]->ToCString()); | 1600 deopt_instructions[i]->ToCString()); |
1601 } | 1601 } |
1602 } | 1602 } |
1603 } | 1603 } |
1604 | 1604 |
1605 | 1605 |
1606 // The stack has been adjusted to fit all values for unoptimized frame. | 1606 // The stack has been adjusted to fit all values for unoptimized frame. |
(...skipping 16 matching lines...) Expand all Loading... |
1623 intptr_t* frame_copy = isolate->deopt_frame_copy(); | 1623 intptr_t* frame_copy = isolate->deopt_frame_copy(); |
1624 intptr_t* cpu_registers_copy = isolate->deopt_cpu_registers_copy(); | 1624 intptr_t* cpu_registers_copy = isolate->deopt_cpu_registers_copy(); |
1625 double* xmm_registers_copy = isolate->deopt_xmm_registers_copy(); | 1625 double* xmm_registers_copy = isolate->deopt_xmm_registers_copy(); |
1626 | 1626 |
1627 intptr_t deopt_id, deopt_reason, deopt_index; | 1627 intptr_t deopt_id, deopt_reason, deopt_index; |
1628 GetDeoptIxDescrAtPc(optimized_code, caller_frame->pc(), | 1628 GetDeoptIxDescrAtPc(optimized_code, caller_frame->pc(), |
1629 &deopt_id, &deopt_reason, &deopt_index); | 1629 &deopt_id, &deopt_reason, &deopt_index); |
1630 ASSERT(deopt_id != Isolate::kNoDeoptId); | 1630 ASSERT(deopt_id != Isolate::kNoDeoptId); |
1631 uword continue_at_pc = unoptimized_code.GetDeoptBeforePcAtDeoptId(deopt_id); | 1631 uword continue_at_pc = unoptimized_code.GetDeoptBeforePcAtDeoptId(deopt_id); |
1632 if (FLAG_trace_deopt) { | 1632 if (FLAG_trace_deopt) { |
1633 OS::Print(" -> continue at 0x%x\n", continue_at_pc); | 1633 OS::Print(" -> continue at %#"Px"\n", continue_at_pc); |
1634 // TODO(srdjan): If we could allow GC, we could print the line where | 1634 // TODO(srdjan): If we could allow GC, we could print the line where |
1635 // deoptimization occured. | 1635 // deoptimization occured. |
1636 } | 1636 } |
1637 const Array& deopt_info_array = | 1637 const Array& deopt_info_array = |
1638 Array::Handle(optimized_code.deopt_info_array()); | 1638 Array::Handle(optimized_code.deopt_info_array()); |
1639 ASSERT(!deopt_info_array.IsNull()); | 1639 ASSERT(!deopt_info_array.IsNull()); |
1640 DeoptInfo& deopt_info = DeoptInfo::Handle(); | 1640 DeoptInfo& deopt_info = DeoptInfo::Handle(); |
1641 deopt_info ^= deopt_info_array.At(deopt_index); | 1641 deopt_info ^= deopt_info_array.At(deopt_index); |
1642 ASSERT(!deopt_info.IsNull()); | 1642 ASSERT(!deopt_info.IsNull()); |
1643 DeoptimizeWithDeoptInfo(optimized_code, deopt_info, *caller_frame); | 1643 DeoptimizeWithDeoptInfo(optimized_code, deopt_info, *caller_frame); |
(...skipping 21 matching lines...) Expand all Loading... |
1665 DeferredDouble* deferred_double = Isolate::Current()->DetachDeferredDoubles(); | 1665 DeferredDouble* deferred_double = Isolate::Current()->DetachDeferredDoubles(); |
1666 | 1666 |
1667 while (deferred_double != NULL) { | 1667 while (deferred_double != NULL) { |
1668 DeferredDouble* current = deferred_double; | 1668 DeferredDouble* current = deferred_double; |
1669 deferred_double = deferred_double->next(); | 1669 deferred_double = deferred_double->next(); |
1670 | 1670 |
1671 RawDouble** slot = current->slot(); | 1671 RawDouble** slot = current->slot(); |
1672 *slot = Double::New(current->value()); | 1672 *slot = Double::New(current->value()); |
1673 | 1673 |
1674 if (FLAG_trace_deopt) { | 1674 if (FLAG_trace_deopt) { |
1675 OS::Print("materialing double at 0x%" PRIxPTR ": %g\n", | 1675 OS::Print("materialing double at %p: %g\n", |
1676 current->slot(), | 1676 current->slot(), |
1677 current->value()); | 1677 current->value()); |
1678 } | 1678 } |
1679 | 1679 |
1680 delete current; | 1680 delete current; |
1681 } | 1681 } |
1682 } | 1682 } |
1683 | 1683 |
1684 | 1684 |
1685 } // namespace dart | 1685 } // namespace dart |
OLD | NEW |