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/object.h" | 5 #include "vm/object.h" |
6 | 6 |
7 #include "include/dart_api.h" | 7 #include "include/dart_api.h" |
8 #include "platform/assert.h" | 8 #include "platform/assert.h" |
9 #include "vm/assembler.h" | 9 #include "vm/assembler.h" |
10 #include "vm/cpu.h" | 10 #include "vm/cpu.h" |
(...skipping 687 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
698 Class::NewTypedDataClass(kTypedDataInt8ArrayCid); | 698 Class::NewTypedDataClass(kTypedDataInt8ArrayCid); |
699 | 699 |
700 // Allocate and initialize the empty_array instance. | 700 // Allocate and initialize the empty_array instance. |
701 { | 701 { |
702 uword address = heap->Allocate(Array::InstanceSize(0), Heap::kOld); | 702 uword address = heap->Allocate(Array::InstanceSize(0), Heap::kOld); |
703 InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(0), true); | 703 InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(0), true); |
704 Array::initializeHandle( | 704 Array::initializeHandle( |
705 empty_array_, | 705 empty_array_, |
706 reinterpret_cast<RawArray*>(address + kHeapObjectTag)); | 706 reinterpret_cast<RawArray*>(address + kHeapObjectTag)); |
707 empty_array_->StoreSmi(&empty_array_->raw_ptr()->length_, Smi::New(0)); | 707 empty_array_->StoreSmi(&empty_array_->raw_ptr()->length_, Smi::New(0)); |
| 708 empty_array_->SetCanonical(); |
708 } | 709 } |
709 | 710 |
710 Smi& smi = Smi::Handle(); | 711 Smi& smi = Smi::Handle(); |
711 // Allocate and initialize the zero_array instance. | 712 // Allocate and initialize the zero_array instance. |
712 { | 713 { |
713 uword address = heap->Allocate(Array::InstanceSize(1), Heap::kOld); | 714 uword address = heap->Allocate(Array::InstanceSize(1), Heap::kOld); |
714 InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(1), true); | 715 InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(1), true); |
715 Array::initializeHandle( | 716 Array::initializeHandle( |
716 zero_array_, | 717 zero_array_, |
717 reinterpret_cast<RawArray*>(address + kHeapObjectTag)); | 718 reinterpret_cast<RawArray*>(address + kHeapObjectTag)); |
718 zero_array_->StoreSmi(&zero_array_->raw_ptr()->length_, Smi::New(1)); | 719 zero_array_->StoreSmi(&zero_array_->raw_ptr()->length_, Smi::New(1)); |
719 smi = Smi::New(0); | 720 smi = Smi::New(0); |
720 zero_array_->SetAt(0, smi); | 721 zero_array_->SetAt(0, smi); |
| 722 zero_array_->SetCanonical(); |
721 } | 723 } |
722 | 724 |
723 // Allocate and initialize the canonical empty context scope object. | 725 // Allocate and initialize the canonical empty context scope object. |
724 { | 726 { |
725 uword address = heap->Allocate(ContextScope::InstanceSize(0), Heap::kOld); | 727 uword address = heap->Allocate(ContextScope::InstanceSize(0), Heap::kOld); |
726 InitializeObject(address, | 728 InitializeObject(address, |
727 kContextScopeCid, | 729 kContextScopeCid, |
728 ContextScope::InstanceSize(0), | 730 ContextScope::InstanceSize(0), |
729 true); | 731 true); |
730 ContextScope::initializeHandle( | 732 ContextScope::initializeHandle( |
731 empty_context_scope_, | 733 empty_context_scope_, |
732 reinterpret_cast<RawContextScope*>(address + kHeapObjectTag)); | 734 reinterpret_cast<RawContextScope*>(address + kHeapObjectTag)); |
733 empty_context_scope_->StoreNonPointer( | 735 empty_context_scope_->StoreNonPointer( |
734 &empty_context_scope_->raw_ptr()->num_variables_, 0); | 736 &empty_context_scope_->raw_ptr()->num_variables_, 0); |
735 empty_context_scope_->StoreNonPointer( | 737 empty_context_scope_->StoreNonPointer( |
736 &empty_context_scope_->raw_ptr()->is_implicit_, true); | 738 &empty_context_scope_->raw_ptr()->is_implicit_, true); |
| 739 empty_context_scope_->SetCanonical(); |
737 } | 740 } |
738 | 741 |
739 // Allocate and initialize the canonical empty object pool object. | 742 // Allocate and initialize the canonical empty object pool object. |
740 { | 743 { |
741 uword address = | 744 uword address = |
742 heap->Allocate(ObjectPool::InstanceSize(0), Heap::kOld); | 745 heap->Allocate(ObjectPool::InstanceSize(0), Heap::kOld); |
743 InitializeObject(address, | 746 InitializeObject(address, |
744 kObjectPoolCid, | 747 kObjectPoolCid, |
745 ObjectPool::InstanceSize(0), | 748 ObjectPool::InstanceSize(0), |
746 true); | 749 true); |
747 ObjectPool::initializeHandle( | 750 ObjectPool::initializeHandle( |
748 empty_object_pool_, | 751 empty_object_pool_, |
749 reinterpret_cast<RawObjectPool*>(address + kHeapObjectTag)); | 752 reinterpret_cast<RawObjectPool*>(address + kHeapObjectTag)); |
750 empty_object_pool_->StoreNonPointer( | 753 empty_object_pool_->StoreNonPointer( |
751 &empty_object_pool_->raw_ptr()->length_, 0); | 754 &empty_object_pool_->raw_ptr()->length_, 0); |
| 755 empty_object_pool_->SetCanonical(); |
752 } | 756 } |
753 | 757 |
754 // Allocate and initialize the empty_descriptors instance. | 758 // Allocate and initialize the empty_descriptors instance. |
755 { | 759 { |
756 uword address = heap->Allocate(PcDescriptors::InstanceSize(0), Heap::kOld); | 760 uword address = heap->Allocate(PcDescriptors::InstanceSize(0), Heap::kOld); |
757 InitializeObject(address, kPcDescriptorsCid, | 761 InitializeObject(address, kPcDescriptorsCid, |
758 PcDescriptors::InstanceSize(0), | 762 PcDescriptors::InstanceSize(0), |
759 true); | 763 true); |
760 PcDescriptors::initializeHandle( | 764 PcDescriptors::initializeHandle( |
761 empty_descriptors_, | 765 empty_descriptors_, |
762 reinterpret_cast<RawPcDescriptors*>(address + kHeapObjectTag)); | 766 reinterpret_cast<RawPcDescriptors*>(address + kHeapObjectTag)); |
763 empty_descriptors_->StoreNonPointer(&empty_descriptors_->raw_ptr()->length_, | 767 empty_descriptors_->StoreNonPointer(&empty_descriptors_->raw_ptr()->length_, |
764 0); | 768 0); |
| 769 empty_descriptors_->SetCanonical(); |
765 } | 770 } |
766 | 771 |
767 // Allocate and initialize the canonical empty variable descriptor object. | 772 // Allocate and initialize the canonical empty variable descriptor object. |
768 { | 773 { |
769 uword address = | 774 uword address = |
770 heap->Allocate(LocalVarDescriptors::InstanceSize(0), Heap::kOld); | 775 heap->Allocate(LocalVarDescriptors::InstanceSize(0), Heap::kOld); |
771 InitializeObject(address, | 776 InitializeObject(address, |
772 kLocalVarDescriptorsCid, | 777 kLocalVarDescriptorsCid, |
773 LocalVarDescriptors::InstanceSize(0), | 778 LocalVarDescriptors::InstanceSize(0), |
774 true); | 779 true); |
775 LocalVarDescriptors::initializeHandle( | 780 LocalVarDescriptors::initializeHandle( |
776 empty_var_descriptors_, | 781 empty_var_descriptors_, |
777 reinterpret_cast<RawLocalVarDescriptors*>(address + kHeapObjectTag)); | 782 reinterpret_cast<RawLocalVarDescriptors*>(address + kHeapObjectTag)); |
778 empty_var_descriptors_->StoreNonPointer( | 783 empty_var_descriptors_->StoreNonPointer( |
779 &empty_var_descriptors_->raw_ptr()->num_entries_, 0); | 784 &empty_var_descriptors_->raw_ptr()->num_entries_, 0); |
| 785 empty_var_descriptors_->SetCanonical(); |
780 } | 786 } |
781 | 787 |
782 // Allocate and initialize the canonical empty exception handler info object. | 788 // Allocate and initialize the canonical empty exception handler info object. |
783 // The vast majority of all functions do not contain an exception handler | 789 // The vast majority of all functions do not contain an exception handler |
784 // and can share this canonical descriptor. | 790 // and can share this canonical descriptor. |
785 { | 791 { |
786 uword address = | 792 uword address = |
787 heap->Allocate(ExceptionHandlers::InstanceSize(0), Heap::kOld); | 793 heap->Allocate(ExceptionHandlers::InstanceSize(0), Heap::kOld); |
788 InitializeObject(address, | 794 InitializeObject(address, |
789 kExceptionHandlersCid, | 795 kExceptionHandlersCid, |
790 ExceptionHandlers::InstanceSize(0), | 796 ExceptionHandlers::InstanceSize(0), |
791 true); | 797 true); |
792 ExceptionHandlers::initializeHandle( | 798 ExceptionHandlers::initializeHandle( |
793 empty_exception_handlers_, | 799 empty_exception_handlers_, |
794 reinterpret_cast<RawExceptionHandlers*>(address + kHeapObjectTag)); | 800 reinterpret_cast<RawExceptionHandlers*>(address + kHeapObjectTag)); |
795 empty_exception_handlers_->StoreNonPointer( | 801 empty_exception_handlers_->StoreNonPointer( |
796 &empty_exception_handlers_->raw_ptr()->num_entries_, 0); | 802 &empty_exception_handlers_->raw_ptr()->num_entries_, 0); |
| 803 empty_exception_handlers_->SetCanonical(); |
797 } | 804 } |
798 | 805 |
799 // The VM isolate snapshot object table is initialized to an empty array | 806 // The VM isolate snapshot object table is initialized to an empty array |
800 // as we do not have any VM isolate snapshot at this time. | 807 // as we do not have any VM isolate snapshot at this time. |
801 *vm_isolate_snapshot_object_table_ = Object::empty_array().raw(); | 808 *vm_isolate_snapshot_object_table_ = Object::empty_array().raw(); |
802 | 809 |
803 cls = Class::New<Instance>(kDynamicCid); | 810 cls = Class::New<Instance>(kDynamicCid); |
804 cls.set_is_abstract(); | 811 cls.set_is_abstract(); |
805 cls.set_num_type_arguments(0); | 812 cls.set_num_type_arguments(0); |
806 cls.set_num_own_type_arguments(0); | 813 cls.set_num_own_type_arguments(0); |
(...skipping 3607 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4414 if (canonical_value.Equals(value)) { | 4421 if (canonical_value.Equals(value)) { |
4415 ASSERT(canonical_value.IsCanonical()); | 4422 ASSERT(canonical_value.IsCanonical()); |
4416 return canonical_value.raw(); | 4423 return canonical_value.raw(); |
4417 } | 4424 } |
4418 *index = *index + 1; | 4425 *index = *index + 1; |
4419 } | 4426 } |
4420 return Bigint::null(); | 4427 return Bigint::null(); |
4421 } | 4428 } |
4422 | 4429 |
4423 | 4430 |
| 4431 class CanonicalInstanceKey { |
| 4432 public: |
| 4433 explicit CanonicalInstanceKey(const Instance& key) : key_(key) { |
| 4434 ASSERT(!(key.IsString() || key.IsInteger() || key.IsAbstractType())); |
| 4435 } |
| 4436 bool Matches(const Instance& obj) const { |
| 4437 ASSERT(!(obj.IsString() || obj.IsInteger() || obj.IsAbstractType())); |
| 4438 if (key_.CanonicalizeEquals(obj)) { |
| 4439 ASSERT(obj.IsCanonical()); |
| 4440 return true; |
| 4441 } |
| 4442 return false; |
| 4443 } |
| 4444 uword Hash() const { |
| 4445 return key_.ComputeCanonicalTableHash(); |
| 4446 } |
| 4447 const Instance& key_; |
| 4448 |
| 4449 private: |
| 4450 DISALLOW_ALLOCATION(); |
| 4451 }; |
| 4452 |
| 4453 |
| 4454 // Traits for looking up Canonical Instances based on a hash of the fields. |
| 4455 class CanonicalInstanceTraits { |
| 4456 public: |
| 4457 static const char* Name() { return "CanonicalInstanceTraits"; } |
| 4458 static bool ReportStats() { return false; } |
| 4459 |
| 4460 // Called when growing the table. |
| 4461 static bool IsMatch(const Object& a, const Object& b) { |
| 4462 ASSERT(!(a.IsString() || a.IsInteger() || a.IsAbstractType())); |
| 4463 ASSERT(!(b.IsString() || b.IsInteger() || b.IsAbstractType())); |
| 4464 return a.raw() == b.raw(); |
| 4465 } |
| 4466 static bool IsMatch(const CanonicalInstanceKey& a, const Object& b) { |
| 4467 return a.Matches(Instance::Cast(b)); |
| 4468 } |
| 4469 static uword Hash(const Object& key) { |
| 4470 ASSERT(!(key.IsString() || key.IsNumber() || key.IsAbstractType())); |
| 4471 ASSERT(key.IsInstance()); |
| 4472 return Instance::Cast(key).ComputeCanonicalTableHash(); |
| 4473 } |
| 4474 static uword Hash(const CanonicalInstanceKey& key) { |
| 4475 return key.Hash(); |
| 4476 } |
| 4477 static RawObject* NewKey(const CanonicalInstanceKey& obj) { |
| 4478 return obj.key_.raw(); |
| 4479 } |
| 4480 }; |
| 4481 typedef UnorderedHashSet <CanonicalInstanceTraits> CanonicalInstancesSet; |
| 4482 |
| 4483 |
4424 RawInstance* Class::LookupCanonicalInstance(Zone* zone, | 4484 RawInstance* Class::LookupCanonicalInstance(Zone* zone, |
4425 const Instance& value, | 4485 const Instance& value) const { |
4426 intptr_t* index) const { | |
4427 ASSERT(this->raw() == value.clazz()); | 4486 ASSERT(this->raw() == value.clazz()); |
4428 const Array& constants = Array::Handle(zone, this->constants()); | |
4429 const intptr_t constants_len = constants.Length(); | |
4430 // Linear search to see whether this value is already present in the | |
4431 // list of canonicalized constants. | |
4432 Instance& canonical_value = Instance::Handle(zone); | 4487 Instance& canonical_value = Instance::Handle(zone); |
4433 while (*index < constants_len) { | 4488 if (this->constants() != Object::empty_array().raw()) { |
4434 canonical_value ^= constants.At(*index); | 4489 CanonicalInstancesSet constants(zone, this->constants()); |
4435 if (canonical_value.IsNull()) { | 4490 canonical_value ^= constants.GetOrNull(CanonicalInstanceKey(value)); |
4436 break; | 4491 this->set_constants(constants.Release()); |
4437 } | |
4438 if (value.CanonicalizeEquals(canonical_value)) { | |
4439 ASSERT(canonical_value.IsCanonical()); | |
4440 return canonical_value.raw(); | |
4441 } | |
4442 *index = *index + 1; | |
4443 } | 4492 } |
4444 return Instance::null(); | 4493 return canonical_value.raw(); |
4445 } | 4494 } |
4446 | 4495 |
4447 | 4496 |
4448 void Class::InsertCanonicalConstant(intptr_t index, | 4497 RawInstance* Class::InsertCanonicalConstant(Zone* zone, |
4449 const Instance& constant) const { | 4498 const Instance& constant) const { |
4450 // The constant needs to be added to the list. Grow the list if it is full. | 4499 ASSERT(this->raw() == constant.clazz()); |
4451 Array& canonical_list = Array::Handle(constants()); | 4500 Instance& canonical_value = Instance::Handle(zone); |
4452 const intptr_t list_len = canonical_list.Length(); | 4501 if (this->constants() == Object::empty_array().raw()) { |
4453 if (index >= list_len) { | 4502 CanonicalInstancesSet constants( |
4454 const intptr_t new_length = (list_len == 0) ? 4 : list_len + 4; | 4503 HashTables::New<CanonicalInstancesSet>(128, Heap::kOld)); |
4455 const Array& new_canonical_list = | 4504 canonical_value ^= constants.InsertNewOrGet(CanonicalInstanceKey(constant)); |
4456 Array::Handle(Array::Grow(canonical_list, new_length, Heap::kOld)); | 4505 this->set_constants(constants.Release()); |
4457 set_constants(new_canonical_list); | |
4458 new_canonical_list.SetAt(index, constant); | |
4459 } else { | 4506 } else { |
4460 canonical_list.SetAt(index, constant); | 4507 CanonicalInstancesSet constants(Thread::Current()->zone(), |
| 4508 this->constants()); |
| 4509 canonical_value ^= constants.InsertNewOrGet(CanonicalInstanceKey(constant)); |
| 4510 this->set_constants(constants.Release()); |
4461 } | 4511 } |
| 4512 return canonical_value.raw(); |
4462 } | 4513 } |
4463 | 4514 |
4464 | 4515 |
4465 void Class::InsertCanonicalNumber(Zone* zone, | 4516 void Class::InsertCanonicalNumber(Zone* zone, |
4466 intptr_t index, | 4517 intptr_t index, |
4467 const Number& constant) const { | 4518 const Number& constant) const { |
4468 // The constant needs to be added to the list. Grow the list if it is full. | 4519 // The constant needs to be added to the list. Grow the list if it is full. |
4469 Array& canonical_list = Array::Handle(zone, constants()); | 4520 Array& canonical_list = Array::Handle(zone, constants()); |
4470 const intptr_t list_len = canonical_list.Length(); | 4521 const intptr_t list_len = canonical_list.Length(); |
4471 if (index >= list_len) { | 4522 if (index >= list_len) { |
4472 const intptr_t new_length = (list_len == 0) ? 4 : list_len + 4; | 4523 const intptr_t new_length = (list_len == 0) ? 4 : list_len + 4; |
4473 canonical_list ^= Array::Grow(canonical_list, new_length, Heap::kOld); | 4524 canonical_list ^= Array::Grow(canonical_list, new_length, Heap::kOld); |
4474 set_constants(canonical_list); | 4525 set_constants(canonical_list); |
4475 } | 4526 } |
4476 canonical_list.SetAt(index, constant); | 4527 canonical_list.SetAt(index, constant); |
4477 } | 4528 } |
4478 | 4529 |
4479 | 4530 |
| 4531 void Class::RehashConstants(Zone* zone) const { |
| 4532 intptr_t cid = id(); |
| 4533 if ((cid == kMintCid) || (cid == kBigintCid) || (cid == kDoubleCid)) { |
| 4534 // Constants stored as a plain list, no rehashing needed. |
| 4535 return; |
| 4536 } |
| 4537 |
| 4538 const Array& old_constants = Array::Handle(zone, constants()); |
| 4539 if (old_constants.Length() == 0) return; |
| 4540 |
| 4541 set_constants(Object::empty_array()); |
| 4542 CanonicalInstancesSet set(zone, old_constants.raw()); |
| 4543 Instance& constant = Instance::Handle(zone); |
| 4544 CanonicalInstancesSet::Iterator it(&set); |
| 4545 while (it.MoveNext()) { |
| 4546 constant ^= set.GetKey(it.Current()); |
| 4547 ASSERT(!constant.IsNull()); |
| 4548 InsertCanonicalConstant(zone, constant); |
| 4549 } |
| 4550 set.Release(); |
| 4551 } |
| 4552 |
| 4553 |
4480 RawUnresolvedClass* UnresolvedClass::New(const LibraryPrefix& library_prefix, | 4554 RawUnresolvedClass* UnresolvedClass::New(const LibraryPrefix& library_prefix, |
4481 const String& ident, | 4555 const String& ident, |
4482 TokenPosition token_pos) { | 4556 TokenPosition token_pos) { |
4483 const UnresolvedClass& type = UnresolvedClass::Handle(UnresolvedClass::New()); | 4557 const UnresolvedClass& type = UnresolvedClass::Handle(UnresolvedClass::New()); |
4484 type.set_library_prefix(library_prefix); | 4558 type.set_library_prefix(library_prefix); |
4485 type.set_ident(ident); | 4559 type.set_ident(ident); |
4486 type.set_token_pos(token_pos); | 4560 type.set_token_pos(token_pos); |
4487 return type.raw(); | 4561 return type.raw(); |
4488 } | 4562 } |
4489 | 4563 |
(...skipping 10257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
14747 return true; // "===". | 14821 return true; // "===". |
14748 } | 14822 } |
14749 | 14823 |
14750 if (other.IsNull() || (this->clazz() != other.clazz())) { | 14824 if (other.IsNull() || (this->clazz() != other.clazz())) { |
14751 return false; | 14825 return false; |
14752 } | 14826 } |
14753 | 14827 |
14754 { | 14828 { |
14755 NoSafepointScope no_safepoint; | 14829 NoSafepointScope no_safepoint; |
14756 // Raw bits compare. | 14830 // Raw bits compare. |
14757 const intptr_t instance_size = Class::Handle(this->clazz()).instance_size(); | 14831 const intptr_t instance_size = SizeFromClass(); |
14758 ASSERT(instance_size != 0); | 14832 ASSERT(instance_size != 0); |
| 14833 const intptr_t other_instance_size = other.SizeFromClass(); |
| 14834 ASSERT(other_instance_size != 0); |
| 14835 if (instance_size != other_instance_size) { |
| 14836 return false; |
| 14837 } |
14759 uword this_addr = reinterpret_cast<uword>(this->raw_ptr()); | 14838 uword this_addr = reinterpret_cast<uword>(this->raw_ptr()); |
14760 uword other_addr = reinterpret_cast<uword>(other.raw_ptr()); | 14839 uword other_addr = reinterpret_cast<uword>(other.raw_ptr()); |
14761 for (intptr_t offset = Instance::NextFieldOffset(); | 14840 for (intptr_t offset = Instance::NextFieldOffset(); |
14762 offset < instance_size; | 14841 offset < instance_size; |
14763 offset += kWordSize) { | 14842 offset += kWordSize) { |
14764 if ((*reinterpret_cast<RawObject**>(this_addr + offset)) != | 14843 if ((*reinterpret_cast<RawObject**>(this_addr + offset)) != |
14765 (*reinterpret_cast<RawObject**>(other_addr + offset))) { | 14844 (*reinterpret_cast<RawObject**>(other_addr + offset))) { |
14766 return false; | 14845 return false; |
14767 } | 14846 } |
14768 } | 14847 } |
14769 } | 14848 } |
14770 return true; | 14849 return true; |
14771 } | 14850 } |
14772 | 14851 |
14773 | 14852 |
| 14853 uword Instance::ComputeCanonicalTableHash() const { |
| 14854 ASSERT(!IsNull()); |
| 14855 NoSafepointScope no_safepoint; |
| 14856 const intptr_t instance_size = SizeFromClass(); |
| 14857 ASSERT(instance_size != 0); |
| 14858 uword hash = instance_size; |
| 14859 uword this_addr = reinterpret_cast<uword>(this->raw_ptr()); |
| 14860 for (intptr_t offset = Instance::NextFieldOffset(); |
| 14861 offset < instance_size; |
| 14862 offset += kWordSize) { |
| 14863 uword value = reinterpret_cast<uword>( |
| 14864 *reinterpret_cast<RawObject**>(this_addr + offset)); |
| 14865 hash = CombineHashes(hash, value); |
| 14866 } |
| 14867 return FinalizeHash(hash); |
| 14868 } |
| 14869 |
| 14870 |
14774 #if defined(DEBUG) | 14871 #if defined(DEBUG) |
14775 class CheckForPointers : public ObjectPointerVisitor { | 14872 class CheckForPointers : public ObjectPointerVisitor { |
14776 public: | 14873 public: |
14777 explicit CheckForPointers(Isolate* isolate) | 14874 explicit CheckForPointers(Isolate* isolate) |
14778 : ObjectPointerVisitor(isolate), has_pointers_(false) {} | 14875 : ObjectPointerVisitor(isolate), has_pointers_(false) {} |
14779 | 14876 |
14780 bool has_pointers() const { return has_pointers_; } | 14877 bool has_pointers() const { return has_pointers_; } |
14781 | 14878 |
14782 void VisitPointers(RawObject** first, RawObject** last) { | 14879 void VisitPointers(RawObject** first, RawObject** last) { |
14783 if (first != last) { | 14880 if (first != last) { |
14784 has_pointers_ = true; | 14881 has_pointers_ = true; |
14785 } | 14882 } |
14786 } | 14883 } |
14787 | 14884 |
14788 private: | 14885 private: |
14789 bool has_pointers_; | 14886 bool has_pointers_; |
14790 | 14887 |
14791 DISALLOW_COPY_AND_ASSIGN(CheckForPointers); | 14888 DISALLOW_COPY_AND_ASSIGN(CheckForPointers); |
14792 }; | 14889 }; |
14793 #endif // DEBUG | 14890 #endif // DEBUG |
14794 | 14891 |
14795 | 14892 |
14796 bool Instance::CheckAndCanonicalizeFields(Zone* zone, | 14893 bool Instance::CheckAndCanonicalizeFields(Thread* thread, |
14797 const char** error_str) const { | 14894 const char** error_str) const { |
14798 const Class& cls = Class::Handle(zone, this->clazz()); | 14895 if (GetClassId() >= kNumPredefinedCids) { |
14799 if (cls.id() >= kNumPredefinedCids) { | |
14800 // Iterate over all fields, canonicalize numbers and strings, expect all | 14896 // Iterate over all fields, canonicalize numbers and strings, expect all |
14801 // other instances to be canonical otherwise report error (return false). | 14897 // other instances to be canonical otherwise report error (return false). |
| 14898 Zone* zone = thread->zone(); |
14802 Object& obj = Object::Handle(zone); | 14899 Object& obj = Object::Handle(zone); |
14803 intptr_t end_field_offset = cls.instance_size() - kWordSize; | 14900 intptr_t end_field_offset = SizeFromClass() - kWordSize; |
14804 for (intptr_t field_offset = 0; | 14901 for (intptr_t field_offset = 0; |
14805 field_offset <= end_field_offset; | 14902 field_offset <= end_field_offset; |
14806 field_offset += kWordSize) { | 14903 field_offset += kWordSize) { |
14807 obj = *this->FieldAddrAtOffset(field_offset); | 14904 obj = *this->FieldAddrAtOffset(field_offset); |
14808 if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { | 14905 if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { |
14809 if (obj.IsNumber() || obj.IsString()) { | 14906 if (obj.IsNumber() || obj.IsString()) { |
14810 obj = Instance::Cast(obj).CheckAndCanonicalize(NULL); | 14907 obj = Instance::Cast(obj).CheckAndCanonicalize(thread, NULL); |
14811 ASSERT(!obj.IsNull()); | 14908 ASSERT(!obj.IsNull()); |
14812 this->SetFieldAtOffset(field_offset, obj); | 14909 this->SetFieldAtOffset(field_offset, obj); |
14813 } else { | 14910 } else { |
14814 ASSERT(error_str != NULL); | 14911 ASSERT(error_str != NULL); |
14815 char* chars = OS::SCreate(zone, "field: %s\n", obj.ToCString()); | 14912 char* chars = OS::SCreate(zone, "field: %s\n", obj.ToCString()); |
14816 *error_str = chars; | 14913 *error_str = chars; |
14817 return false; | 14914 return false; |
14818 } | 14915 } |
14819 } | 14916 } |
14820 } | 14917 } |
14821 } else { | 14918 } else { |
14822 #if defined(DEBUG) | 14919 #if defined(DEBUG) |
14823 // Make sure that we are not missing any fields. | 14920 // Make sure that we are not missing any fields. |
14824 CheckForPointers has_pointers(Isolate::Current()); | 14921 CheckForPointers has_pointers(Isolate::Current()); |
14825 this->raw()->VisitPointers(&has_pointers); | 14922 this->raw()->VisitPointers(&has_pointers); |
14826 ASSERT(!has_pointers.has_pointers()); | 14923 ASSERT(!has_pointers.has_pointers()); |
14827 #endif // DEBUG | 14924 #endif // DEBUG |
14828 } | 14925 } |
14829 return true; | 14926 return true; |
14830 } | 14927 } |
14831 | 14928 |
14832 | 14929 |
14833 RawInstance* Instance::CheckAndCanonicalize(const char** error_str) const { | 14930 RawInstance* Instance::CheckAndCanonicalize(Thread* thread, |
| 14931 const char** error_str) const { |
14834 ASSERT(!IsNull()); | 14932 ASSERT(!IsNull()); |
14835 if (this->IsCanonical()) { | 14933 if (this->IsCanonical()) { |
14836 return this->raw(); | 14934 return this->raw(); |
14837 } | 14935 } |
14838 Thread* thread = Thread::Current(); | 14936 if (!CheckAndCanonicalizeFields(thread, error_str)) { |
14839 Zone* zone = thread->zone(); | |
14840 if (!CheckAndCanonicalizeFields(zone, error_str)) { | |
14841 return Instance::null(); | 14937 return Instance::null(); |
14842 } | 14938 } |
| 14939 Zone* zone = thread->zone(); |
14843 Isolate* isolate = thread->isolate(); | 14940 Isolate* isolate = thread->isolate(); |
14844 Instance& result = Instance::Handle(zone); | 14941 Instance& result = Instance::Handle(zone); |
14845 const Class& cls = Class::Handle(zone, this->clazz()); | 14942 const Class& cls = Class::Handle(zone, this->clazz()); |
14846 intptr_t index = 0; | |
14847 result ^= cls.LookupCanonicalInstance(zone, *this, &index); | |
14848 if (!result.IsNull()) { | |
14849 return result.raw(); | |
14850 } | |
14851 { | 14943 { |
14852 SafepointMutexLocker ml(isolate->constant_canonicalization_mutex()); | 14944 SafepointMutexLocker ml(isolate->constant_canonicalization_mutex()); |
14853 // Retry lookup. | 14945 if (IsNew()) { |
14854 { | 14946 result ^= cls.LookupCanonicalInstance(zone, *this); |
14855 result ^= cls.LookupCanonicalInstance(zone, *this, &index); | |
14856 if (!result.IsNull()) { | 14947 if (!result.IsNull()) { |
14857 return result.raw(); | 14948 return result.raw(); |
14858 } | 14949 } |
14859 } | 14950 ASSERT((isolate == Dart::vm_isolate()) || !InVMHeap()); |
14860 | |
14861 // The value needs to be added to the list. Grow the list if | |
14862 // it is full. | |
14863 result ^= this->raw(); | |
14864 ASSERT((isolate == Dart::vm_isolate()) || !result.InVMHeap()); | |
14865 if (result.IsNew()) { | |
14866 // Create a canonical object in old space. | 14951 // Create a canonical object in old space. |
14867 result ^= Object::Clone(result, Heap::kOld); | 14952 result ^= Object::Clone(*this, Heap::kOld); |
| 14953 } else { |
| 14954 result ^= this->raw(); |
14868 } | 14955 } |
14869 ASSERT(result.IsOld()); | 14956 ASSERT(result.IsOld()); |
14870 result.SetCanonical(); | 14957 result.SetCanonical(); |
14871 cls.InsertCanonicalConstant(index, result); | 14958 return cls.InsertCanonicalConstant(zone, result); |
14872 return result.raw(); | |
14873 } | 14959 } |
14874 } | 14960 } |
14875 | 14961 |
14876 | 14962 |
14877 RawAbstractType* Instance::GetType() const { | 14963 RawAbstractType* Instance::GetType() const { |
14878 if (IsNull()) { | 14964 if (IsNull()) { |
14879 return Type::NullType(); | 14965 return Type::NullType(); |
14880 } | 14966 } |
14881 const Class& cls = Class::Handle(clazz()); | 14967 const Class& cls = Class::Handle(clazz()); |
14882 if (cls.IsClosureClass()) { | 14968 if (cls.IsClosureClass()) { |
(...skipping 2601 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
17484 | 17570 |
17485 RawMixinAppType* MixinAppType::New(const AbstractType& super_type, | 17571 RawMixinAppType* MixinAppType::New(const AbstractType& super_type, |
17486 const Array& mixin_types) { | 17572 const Array& mixin_types) { |
17487 const MixinAppType& result = MixinAppType::Handle(MixinAppType::New()); | 17573 const MixinAppType& result = MixinAppType::Handle(MixinAppType::New()); |
17488 result.set_super_type(super_type); | 17574 result.set_super_type(super_type); |
17489 result.set_mixin_types(mixin_types); | 17575 result.set_mixin_types(mixin_types); |
17490 return result.raw(); | 17576 return result.raw(); |
17491 } | 17577 } |
17492 | 17578 |
17493 | 17579 |
17494 RawInstance* Number::CheckAndCanonicalize(const char** error_str) const { | 17580 RawInstance* Number::CheckAndCanonicalize(Thread* thread, |
| 17581 const char** error_str) const { |
17495 intptr_t cid = GetClassId(); | 17582 intptr_t cid = GetClassId(); |
17496 switch (cid) { | 17583 switch (cid) { |
17497 case kSmiCid: | 17584 case kSmiCid: |
17498 return reinterpret_cast<RawSmi*>(raw_value()); | 17585 return reinterpret_cast<RawSmi*>(raw_value()); |
17499 case kMintCid: | 17586 case kMintCid: |
17500 return Mint::NewCanonical(Mint::Cast(*this).value()); | 17587 return Mint::NewCanonical(Mint::Cast(*this).value()); |
17501 case kDoubleCid: | 17588 case kDoubleCid: |
17502 return Double::NewCanonical(Double::Cast(*this).value()); | 17589 return Double::NewCanonical(Double::Cast(*this).value()); |
17503 case kBigintCid: { | 17590 case kBigintCid: { |
17504 Thread* thread = Thread::Current(); | |
17505 Zone* zone = thread->zone(); | 17591 Zone* zone = thread->zone(); |
17506 Isolate* isolate = thread->isolate(); | 17592 Isolate* isolate = thread->isolate(); |
17507 if (!CheckAndCanonicalizeFields(zone, error_str)) { | 17593 if (!CheckAndCanonicalizeFields(thread, error_str)) { |
17508 return Instance::null(); | 17594 return Instance::null(); |
17509 } | 17595 } |
17510 Bigint& result = Bigint::Handle(zone); | 17596 Bigint& result = Bigint::Handle(zone); |
17511 const Class& cls = Class::Handle(zone, this->clazz()); | 17597 const Class& cls = Class::Handle(zone, this->clazz()); |
17512 intptr_t index = 0; | 17598 intptr_t index = 0; |
17513 result ^= cls.LookupCanonicalBigint(zone, Bigint::Cast(*this), &index); | 17599 result ^= cls.LookupCanonicalBigint(zone, Bigint::Cast(*this), &index); |
17514 if (!result.IsNull()) { | 17600 if (!result.IsNull()) { |
17515 return result.raw(); | 17601 return result.raw(); |
17516 } | 17602 } |
17517 { | 17603 { |
(...skipping 732 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
18250 | 18336 |
18251 for (intptr_t i = 0; i < used; i++) { | 18337 for (intptr_t i = 0; i < used; i++) { |
18252 if (this->DigitAt(i) != other_bgi.DigitAt(i)) { | 18338 if (this->DigitAt(i) != other_bgi.DigitAt(i)) { |
18253 return false; | 18339 return false; |
18254 } | 18340 } |
18255 } | 18341 } |
18256 return true; | 18342 return true; |
18257 } | 18343 } |
18258 | 18344 |
18259 | 18345 |
18260 bool Bigint::CheckAndCanonicalizeFields(Zone* zone, | 18346 bool Bigint::CheckAndCanonicalizeFields(Thread* thread, |
18261 const char** error_str) const { | 18347 const char** error_str) const { |
| 18348 Zone* zone = thread->zone(); |
18262 // Bool field neg should always be canonical. | 18349 // Bool field neg should always be canonical. |
18263 ASSERT(Bool::Handle(zone, neg()).IsCanonical()); | 18350 ASSERT(Bool::Handle(zone, neg()).IsCanonical()); |
18264 // Smi field used is canonical by definition. | 18351 // Smi field used is canonical by definition. |
18265 if (Used() > 0) { | 18352 if (Used() > 0) { |
18266 // Canonicalize TypedData field digits. | 18353 // Canonicalize TypedData field digits. |
18267 TypedData& digits_ = TypedData::Handle(zone, digits()); | 18354 TypedData& digits_ = TypedData::Handle(zone, digits()); |
18268 digits_ ^= digits_.CheckAndCanonicalize(NULL); | 18355 digits_ ^= digits_.CheckAndCanonicalize(thread, NULL); |
18269 ASSERT(!digits_.IsNull()); | 18356 ASSERT(!digits_.IsNull()); |
18270 set_digits(digits_); | 18357 set_digits(digits_); |
18271 } else { | 18358 } else { |
18272 ASSERT(digits() == TypedData::EmptyUint32Array(Thread::Current())); | 18359 ASSERT(digits() == TypedData::EmptyUint32Array(Thread::Current())); |
18273 } | 18360 } |
18274 return true; | 18361 return true; |
18275 } | 18362 } |
18276 | 18363 |
18277 | 18364 |
18278 RawBigint* Bigint::New(Heap::Space space) { | 18365 RawBigint* Bigint::New(Heap::Space space) { |
(...skipping 951 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
19230 intptr_t slen = other.Length(); | 19317 intptr_t slen = other.Length(); |
19231 for (int i = 0; i < slen; i++) { | 19318 for (int i = 0; i < slen; i++) { |
19232 if (this->CharAt(i) != other.CharAt(i)) { | 19319 if (this->CharAt(i) != other.CharAt(i)) { |
19233 return false; | 19320 return false; |
19234 } | 19321 } |
19235 } | 19322 } |
19236 return true; | 19323 return true; |
19237 } | 19324 } |
19238 | 19325 |
19239 | 19326 |
19240 RawInstance* String::CheckAndCanonicalize(const char** error_str) const { | 19327 RawInstance* String::CheckAndCanonicalize(Thread* thread, |
| 19328 const char** error_str) const { |
19241 if (IsCanonical()) { | 19329 if (IsCanonical()) { |
19242 return this->raw(); | 19330 return this->raw(); |
19243 } | 19331 } |
19244 return Symbols::New(Thread::Current(), *this); | 19332 return Symbols::New(Thread::Current(), *this); |
19245 } | 19333 } |
19246 | 19334 |
19247 | 19335 |
19248 RawString* String::New(const char* cstr, Heap::Space space) { | 19336 RawString* String::New(const char* cstr, Heap::Space space) { |
19249 ASSERT(cstr != NULL); | 19337 ASSERT(cstr != NULL); |
19250 intptr_t array_len = strlen(cstr); | 19338 intptr_t array_len = strlen(cstr); |
(...skipping 1436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
20687 return false; | 20775 return false; |
20688 } | 20776 } |
20689 | 20777 |
20690 for (intptr_t i = 0; i < len; i++) { | 20778 for (intptr_t i = 0; i < len; i++) { |
20691 if (this->At(i) != other_arr.At(i)) { | 20779 if (this->At(i) != other_arr.At(i)) { |
20692 return false; | 20780 return false; |
20693 } | 20781 } |
20694 } | 20782 } |
20695 | 20783 |
20696 // Now check if both arrays have the same type arguments. | 20784 // Now check if both arrays have the same type arguments. |
| 20785 if (GetTypeArguments() == other.GetTypeArguments()) { |
| 20786 return true; |
| 20787 } |
20697 const TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments()); | 20788 const TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments()); |
20698 const TypeArguments& other_type_args = TypeArguments::Handle( | 20789 const TypeArguments& other_type_args = TypeArguments::Handle( |
20699 other.GetTypeArguments()); | 20790 other.GetTypeArguments()); |
20700 if (!type_args.Equals(other_type_args)) { | 20791 if (!type_args.Equals(other_type_args)) { |
20701 return false; | 20792 return false; |
20702 } | 20793 } |
20703 return true; | 20794 return true; |
20704 } | 20795 } |
20705 | 20796 |
20706 | 20797 |
| 20798 uword Array::ComputeCanonicalTableHash() const { |
| 20799 ASSERT(!IsNull()); |
| 20800 intptr_t len = Length(); |
| 20801 uword hash = len; |
| 20802 uword value = reinterpret_cast<uword>(GetTypeArguments()); |
| 20803 hash = CombineHashes(hash, value); |
| 20804 for (intptr_t i = 0; i < len; i++) { |
| 20805 value = reinterpret_cast<uword>(At(i)); |
| 20806 hash = CombineHashes(hash, value); |
| 20807 } |
| 20808 return FinalizeHash(hash); |
| 20809 } |
| 20810 |
| 20811 |
20707 RawArray* Array::New(intptr_t len, Heap::Space space) { | 20812 RawArray* Array::New(intptr_t len, Heap::Space space) { |
20708 ASSERT(Isolate::Current()->object_store()->array_class() != Class::null()); | 20813 ASSERT(Isolate::Current()->object_store()->array_class() != Class::null()); |
20709 return New(kClassId, len, space); | 20814 return New(kClassId, len, space); |
20710 } | 20815 } |
20711 | 20816 |
20712 | 20817 |
20713 RawArray* Array::New(intptr_t class_id, intptr_t len, Heap::Space space) { | 20818 RawArray* Array::New(intptr_t class_id, intptr_t len, Heap::Space space) { |
20714 if ((len < 0) || (len > Array::kMaxElements)) { | 20819 if ((len < 0) || (len > Array::kMaxElements)) { |
20715 // This should be caught before we reach here. | 20820 // This should be caught before we reach here. |
20716 FATAL1("Fatal error in Array::New: invalid len %" Pd "\n", len); | 20821 FATAL1("Fatal error in Array::New: invalid len %" Pd "\n", len); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
20838 array.SetLength(used_len); | 20943 array.SetLength(used_len); |
20839 | 20944 |
20840 // Null the GrowableObjectArray, we are removing its backing array. | 20945 // Null the GrowableObjectArray, we are removing its backing array. |
20841 growable_array.SetLength(0); | 20946 growable_array.SetLength(0); |
20842 growable_array.SetData(Object::empty_array()); | 20947 growable_array.SetData(Object::empty_array()); |
20843 | 20948 |
20844 return array.raw(); | 20949 return array.raw(); |
20845 } | 20950 } |
20846 | 20951 |
20847 | 20952 |
20848 bool Array::CheckAndCanonicalizeFields(Zone* zone, | 20953 bool Array::CheckAndCanonicalizeFields(Thread* thread, |
20849 const char** error_str) const { | 20954 const char** error_str) const { |
20850 Object& obj = Object::Handle(zone); | 20955 intptr_t len = Length(); |
20851 // Iterate over all elements, canonicalize numbers and strings, expect all | 20956 if (len > 0) { |
20852 // other instances to be canonical otherwise report error (return false). | 20957 Zone* zone = thread->zone(); |
20853 for (intptr_t i = 0; i < Length(); i++) { | 20958 Object& obj = Object::Handle(zone); |
20854 obj = At(i); | 20959 // Iterate over all elements, canonicalize numbers and strings, expect all |
20855 if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { | 20960 // other instances to be canonical otherwise report error (return false). |
20856 if (obj.IsNumber() || obj.IsString()) { | 20961 for (intptr_t i = 0; i < len; i++) { |
20857 obj = Instance::Cast(obj).CheckAndCanonicalize(NULL); | 20962 obj = At(i); |
20858 ASSERT(!obj.IsNull()); | 20963 if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { |
20859 this->SetAt(i, obj); | 20964 if (obj.IsNumber() || obj.IsString()) { |
20860 } else { | 20965 obj = Instance::Cast(obj).CheckAndCanonicalize(thread, NULL); |
20861 ASSERT(error_str != NULL); | 20966 ASSERT(!obj.IsNull()); |
20862 char* chars = OS::SCreate(Thread::Current()->zone(), | 20967 this->SetAt(i, obj); |
20863 "element at index %" Pd ": %s\n", i, obj.ToCString()); | 20968 } else { |
20864 *error_str = chars; | 20969 ASSERT(error_str != NULL); |
20865 return false; | 20970 char* chars = OS::SCreate( |
| 20971 zone, "element at index %" Pd ": %s\n", i, obj.ToCString()); |
| 20972 *error_str = chars; |
| 20973 return false; |
| 20974 } |
20866 } | 20975 } |
20867 } | 20976 } |
20868 } | 20977 } |
20869 return true; | 20978 return true; |
20870 } | 20979 } |
20871 | 20980 |
20872 | 20981 |
20873 RawImmutableArray* ImmutableArray::New(intptr_t len, | 20982 RawImmutableArray* ImmutableArray::New(intptr_t len, |
20874 Heap::Space space) { | 20983 Heap::Space space) { |
20875 ASSERT(Isolate::Current()->object_store()->immutable_array_class() != | 20984 ASSERT(Isolate::Current()->object_store()->immutable_array_class() != |
(...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
21339 const intptr_t len = this->LengthInBytes(); | 21448 const intptr_t len = this->LengthInBytes(); |
21340 if (len != other_typed_data.LengthInBytes()) { | 21449 if (len != other_typed_data.LengthInBytes()) { |
21341 return false; | 21450 return false; |
21342 } | 21451 } |
21343 NoSafepointScope no_safepoint; | 21452 NoSafepointScope no_safepoint; |
21344 return (len == 0) || | 21453 return (len == 0) || |
21345 (memcmp(DataAddr(0), other_typed_data.DataAddr(0), len) == 0); | 21454 (memcmp(DataAddr(0), other_typed_data.DataAddr(0), len) == 0); |
21346 } | 21455 } |
21347 | 21456 |
21348 | 21457 |
| 21458 uword TypedData::ComputeCanonicalTableHash() const { |
| 21459 const intptr_t len = this->LengthInBytes(); |
| 21460 ASSERT(len != 0); |
| 21461 uword hash = len; |
| 21462 for (intptr_t i = 0; i < len; i++) { |
| 21463 hash = CombineHashes(len, GetUint8(i)); |
| 21464 } |
| 21465 return FinalizeHash(hash); |
| 21466 } |
| 21467 |
| 21468 |
21349 RawTypedData* TypedData::New(intptr_t class_id, | 21469 RawTypedData* TypedData::New(intptr_t class_id, |
21350 intptr_t len, | 21470 intptr_t len, |
21351 Heap::Space space) { | 21471 Heap::Space space) { |
21352 if (len < 0 || len > TypedData::MaxElements(class_id)) { | 21472 if (len < 0 || len > TypedData::MaxElements(class_id)) { |
21353 FATAL1("Fatal error in TypedData::New: invalid len %" Pd "\n", len); | 21473 FATAL1("Fatal error in TypedData::New: invalid len %" Pd "\n", len); |
21354 } | 21474 } |
21355 TypedData& result = TypedData::Handle(); | 21475 TypedData& result = TypedData::Handle(); |
21356 { | 21476 { |
21357 const intptr_t lengthInBytes = len * ElementSizeInBytes(class_id); | 21477 const intptr_t lengthInBytes = len * ElementSizeInBytes(class_id); |
21358 RawObject* raw = Object::Allocate(class_id, | 21478 RawObject* raw = Object::Allocate(class_id, |
(...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
22033 return UserTag::null(); | 22153 return UserTag::null(); |
22034 } | 22154 } |
22035 | 22155 |
22036 | 22156 |
22037 const char* UserTag::ToCString() const { | 22157 const char* UserTag::ToCString() const { |
22038 const String& tag_label = String::Handle(label()); | 22158 const String& tag_label = String::Handle(label()); |
22039 return tag_label.ToCString(); | 22159 return tag_label.ToCString(); |
22040 } | 22160 } |
22041 | 22161 |
22042 } // namespace dart | 22162 } // namespace dart |
OLD | NEW |