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/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 DISALLOW_COPY_AND_ASSIGN(CanonicalInstanceKey); | |
| 4452 }; | |
| 4453 | |
| 4454 | |
| 4455 // Traits for looking up Canonical Instances based on a hash of the fields. | |
| 4456 class CanonicalInstanceTraits { | |
| 4457 public: | |
| 4458 static const char* Name() { return "CanonicalInstanceTraits"; } | |
| 4459 static bool ReportStats() { return false; } | |
| 4460 | |
| 4461 // Called when growing the table. | |
| 4462 static bool IsMatch(const Object& a, const Object& b) { | |
| 4463 ASSERT(!(a.IsString() || a.IsInteger() || a.IsAbstractType())); | |
| 4464 ASSERT(!(b.IsString() || b.IsInteger() || b.IsAbstractType())); | |
| 4465 return a.raw() == b.raw(); | |
| 4466 } | |
| 4467 static bool IsMatch(const CanonicalInstanceKey& a, const Object& b) { | |
| 4468 return a.Matches(Instance::Cast(b)); | |
| 4469 } | |
| 4470 static uword Hash(const Object& key) { | |
| 4471 ASSERT(!(key.IsString() || key.IsNumber() || key.IsAbstractType())); | |
| 4472 ASSERT(key.IsInstance()); | |
| 4473 return Instance::Cast(key).ComputeCanonicalTableHash(); | |
| 4474 } | |
| 4475 static uword Hash(const CanonicalInstanceKey& key) { | |
| 4476 return key.Hash(); | |
| 4477 } | |
| 4478 static RawObject* NewKey(const CanonicalInstanceKey& obj) { | |
| 4479 return obj.key_.raw(); | |
| 4480 } | |
| 4481 }; | |
| 4482 typedef UnorderedHashSet<CanonicalInstanceTraits> CanonicalInstancesSet; | |
| 4483 | |
| 4484 | |
| 4424 RawInstance* Class::LookupCanonicalInstance(Zone* zone, | 4485 RawInstance* Class::LookupCanonicalInstance(Zone* zone, |
| 4425 const Instance& value, | 4486 const Instance& value) const { |
| 4426 intptr_t* index) const { | |
| 4427 ASSERT(this->raw() == value.clazz()); | 4487 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); | 4488 Instance& canonical_value = Instance::Handle(zone); |
| 4433 while (*index < constants_len) { | 4489 if (this->constants() != Object::empty_array().raw()) { |
| 4434 canonical_value ^= constants.At(*index); | 4490 CanonicalInstancesSet constants(zone, this->constants()); |
| 4435 if (canonical_value.IsNull()) { | 4491 canonical_value ^= constants.GetOrNull(CanonicalInstanceKey(value)); |
| 4436 break; | 4492 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 } | 4493 } |
| 4444 return Instance::null(); | 4494 return canonical_value.raw(); |
| 4445 } | 4495 } |
| 4446 | 4496 |
| 4447 | 4497 |
| 4448 void Class::InsertCanonicalConstant(intptr_t index, | 4498 RawInstance* Class::InsertCanonicalConstant(Zone* zone, |
| 4449 const Instance& constant) const { | 4499 const Instance& constant) const { |
| 4450 // The constant needs to be added to the list. Grow the list if it is full. | 4500 ASSERT(this->raw() == constant.clazz()); |
| 4451 Array& canonical_list = Array::Handle(constants()); | 4501 Instance& canonical_value = Instance::Handle(zone); |
| 4452 const intptr_t list_len = canonical_list.Length(); | 4502 if (this->constants() == Object::empty_array().raw()) { |
| 4453 if (index >= list_len) { | 4503 CanonicalInstancesSet constants( |
| 4454 const intptr_t new_length = (list_len == 0) ? 4 : list_len + 4; | 4504 HashTables::New<CanonicalInstancesSet>(128, Heap::kOld)); |
| 4455 const Array& new_canonical_list = | 4505 canonical_value ^= constants.InsertNewOrGet(CanonicalInstanceKey(constant)); |
| 4456 Array::Handle(Array::Grow(canonical_list, new_length, Heap::kOld)); | 4506 this->set_constants(constants.Release()); |
| 4457 set_constants(new_canonical_list); | |
| 4458 new_canonical_list.SetAt(index, constant); | |
| 4459 } else { | 4507 } else { |
| 4460 canonical_list.SetAt(index, constant); | 4508 CanonicalInstancesSet constants(Thread::Current()->zone(), |
| 4509 this->constants()); | |
| 4510 canonical_value ^= constants.InsertNewOrGet(CanonicalInstanceKey(constant)); | |
| 4511 this->set_constants(constants.Release()); | |
| 4461 } | 4512 } |
| 4513 return canonical_value.raw(); | |
| 4462 } | 4514 } |
| 4463 | 4515 |
| 4464 | 4516 |
| 4465 void Class::InsertCanonicalNumber(Zone* zone, | 4517 void Class::InsertCanonicalNumber(Zone* zone, |
| 4466 intptr_t index, | 4518 intptr_t index, |
| 4467 const Number& constant) const { | 4519 const Number& constant) const { |
| 4468 // The constant needs to be added to the list. Grow the list if it is full. | 4520 // The constant needs to be added to the list. Grow the list if it is full. |
| 4469 Array& canonical_list = Array::Handle(zone, constants()); | 4521 Array& canonical_list = Array::Handle(zone, constants()); |
| 4470 const intptr_t list_len = canonical_list.Length(); | 4522 const intptr_t list_len = canonical_list.Length(); |
| 4471 if (index >= list_len) { | 4523 if (index >= list_len) { |
| (...skipping 10274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 14746 return true; // "===". | 14798 return true; // "===". |
| 14747 } | 14799 } |
| 14748 | 14800 |
| 14749 if (other.IsNull() || (this->clazz() != other.clazz())) { | 14801 if (other.IsNull() || (this->clazz() != other.clazz())) { |
| 14750 return false; | 14802 return false; |
| 14751 } | 14803 } |
| 14752 | 14804 |
| 14753 { | 14805 { |
| 14754 NoSafepointScope no_safepoint; | 14806 NoSafepointScope no_safepoint; |
| 14755 // Raw bits compare. | 14807 // Raw bits compare. |
| 14756 const intptr_t instance_size = Class::Handle(this->clazz()).instance_size(); | 14808 const intptr_t instance_size = SizeFromClass(); |
| 14757 ASSERT(instance_size != 0); | 14809 ASSERT(instance_size != 0); |
| 14810 const intptr_t other_instance_size = other.SizeFromClass(); | |
| 14811 ASSERT(other_instance_size != 0); | |
| 14812 if (instance_size != other_instance_size) { | |
| 14813 return false; | |
| 14814 } | |
| 14758 uword this_addr = reinterpret_cast<uword>(this->raw_ptr()); | 14815 uword this_addr = reinterpret_cast<uword>(this->raw_ptr()); |
| 14759 uword other_addr = reinterpret_cast<uword>(other.raw_ptr()); | 14816 uword other_addr = reinterpret_cast<uword>(other.raw_ptr()); |
| 14760 for (intptr_t offset = Instance::NextFieldOffset(); | 14817 for (intptr_t offset = Instance::NextFieldOffset(); |
| 14761 offset < instance_size; | 14818 offset < instance_size; |
| 14762 offset += kWordSize) { | 14819 offset += kWordSize) { |
| 14763 if ((*reinterpret_cast<RawObject**>(this_addr + offset)) != | 14820 if ((*reinterpret_cast<RawObject**>(this_addr + offset)) != |
| 14764 (*reinterpret_cast<RawObject**>(other_addr + offset))) { | 14821 (*reinterpret_cast<RawObject**>(other_addr + offset))) { |
| 14765 return false; | 14822 return false; |
| 14766 } | 14823 } |
| 14767 } | 14824 } |
| 14768 } | 14825 } |
| 14769 return true; | 14826 return true; |
| 14770 } | 14827 } |
| 14771 | 14828 |
| 14772 | 14829 |
| 14830 uword Instance::ComputeCanonicalTableHash() const { | |
| 14831 ASSERT(!IsNull()); | |
| 14832 NoSafepointScope no_safepoint; | |
| 14833 const intptr_t instance_size = SizeFromClass(); | |
| 14834 ASSERT(instance_size != 0); | |
| 14835 uword hash = instance_size; | |
| 14836 uword this_addr = reinterpret_cast<uword>(this->raw_ptr()); | |
| 14837 for (intptr_t offset = Instance::NextFieldOffset(); | |
| 14838 offset < instance_size; | |
| 14839 offset += kWordSize) { | |
| 14840 uword value = reinterpret_cast<uword>( | |
|
rmacnak
2016/04/15 18:19:18
This won't work for precompiled or app snapshots,
| |
| 14841 *reinterpret_cast<RawObject**>(this_addr + offset)); | |
| 14842 hash = CombineHashes(hash, value); | |
| 14843 } | |
| 14844 return FinalizeHash(hash); | |
| 14845 } | |
| 14846 | |
| 14847 | |
| 14773 #if defined(DEBUG) | 14848 #if defined(DEBUG) |
| 14774 class CheckForPointers : public ObjectPointerVisitor { | 14849 class CheckForPointers : public ObjectPointerVisitor { |
| 14775 public: | 14850 public: |
| 14776 explicit CheckForPointers(Isolate* isolate) | 14851 explicit CheckForPointers(Isolate* isolate) |
| 14777 : ObjectPointerVisitor(isolate), has_pointers_(false) {} | 14852 : ObjectPointerVisitor(isolate), has_pointers_(false) {} |
| 14778 | 14853 |
| 14779 bool has_pointers() const { return has_pointers_; } | 14854 bool has_pointers() const { return has_pointers_; } |
| 14780 | 14855 |
| 14781 void VisitPointers(RawObject** first, RawObject** last) { | 14856 void VisitPointers(RawObject** first, RawObject** last) { |
| 14782 if (first != last) { | 14857 if (first != last) { |
| 14783 has_pointers_ = true; | 14858 has_pointers_ = true; |
| 14784 } | 14859 } |
| 14785 } | 14860 } |
| 14786 | 14861 |
| 14787 private: | 14862 private: |
| 14788 bool has_pointers_; | 14863 bool has_pointers_; |
| 14789 | 14864 |
| 14790 DISALLOW_COPY_AND_ASSIGN(CheckForPointers); | 14865 DISALLOW_COPY_AND_ASSIGN(CheckForPointers); |
| 14791 }; | 14866 }; |
| 14792 #endif // DEBUG | 14867 #endif // DEBUG |
| 14793 | 14868 |
| 14794 | 14869 |
| 14795 bool Instance::CheckAndCanonicalizeFields(Zone* zone, | 14870 bool Instance::CheckAndCanonicalizeFields(Thread* thread, |
| 14796 const char** error_str) const { | 14871 const char** error_str) const { |
| 14797 const Class& cls = Class::Handle(zone, this->clazz()); | 14872 if (GetClassId() >= kNumPredefinedCids) { |
| 14798 if (cls.id() >= kNumPredefinedCids) { | |
| 14799 // Iterate over all fields, canonicalize numbers and strings, expect all | 14873 // Iterate over all fields, canonicalize numbers and strings, expect all |
| 14800 // other instances to be canonical otherwise report error (return false). | 14874 // other instances to be canonical otherwise report error (return false). |
| 14875 Zone* zone = thread->zone(); | |
| 14801 Object& obj = Object::Handle(zone); | 14876 Object& obj = Object::Handle(zone); |
| 14802 intptr_t end_field_offset = cls.instance_size() - kWordSize; | 14877 intptr_t end_field_offset = SizeFromClass() - kWordSize; |
| 14803 for (intptr_t field_offset = 0; | 14878 for (intptr_t field_offset = 0; |
| 14804 field_offset <= end_field_offset; | 14879 field_offset <= end_field_offset; |
| 14805 field_offset += kWordSize) { | 14880 field_offset += kWordSize) { |
| 14806 obj = *this->FieldAddrAtOffset(field_offset); | 14881 obj = *this->FieldAddrAtOffset(field_offset); |
| 14807 if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { | 14882 if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { |
| 14808 if (obj.IsNumber() || obj.IsString()) { | 14883 if (obj.IsNumber() || obj.IsString()) { |
| 14809 obj = Instance::Cast(obj).CheckAndCanonicalize(NULL); | 14884 obj = Instance::Cast(obj).CheckAndCanonicalize(thread, NULL); |
| 14810 ASSERT(!obj.IsNull()); | 14885 ASSERT(!obj.IsNull()); |
| 14811 this->SetFieldAtOffset(field_offset, obj); | 14886 this->SetFieldAtOffset(field_offset, obj); |
| 14812 } else { | 14887 } else { |
| 14813 ASSERT(error_str != NULL); | 14888 ASSERT(error_str != NULL); |
| 14814 char* chars = OS::SCreate(zone, "field: %s\n", obj.ToCString()); | 14889 char* chars = OS::SCreate(zone, "field: %s\n", obj.ToCString()); |
| 14815 *error_str = chars; | 14890 *error_str = chars; |
| 14816 return false; | 14891 return false; |
| 14817 } | 14892 } |
| 14818 } | 14893 } |
| 14819 } | 14894 } |
| 14820 } else { | 14895 } else { |
| 14821 #if defined(DEBUG) | 14896 #if defined(DEBUG) |
| 14822 // Make sure that we are not missing any fields. | 14897 // Make sure that we are not missing any fields. |
| 14823 CheckForPointers has_pointers(Isolate::Current()); | 14898 CheckForPointers has_pointers(Isolate::Current()); |
| 14824 this->raw()->VisitPointers(&has_pointers); | 14899 this->raw()->VisitPointers(&has_pointers); |
| 14825 ASSERT(!has_pointers.has_pointers()); | 14900 ASSERT(!has_pointers.has_pointers()); |
| 14826 #endif // DEBUG | 14901 #endif // DEBUG |
| 14827 } | 14902 } |
| 14828 return true; | 14903 return true; |
| 14829 } | 14904 } |
| 14830 | 14905 |
| 14831 | 14906 |
| 14832 RawInstance* Instance::CheckAndCanonicalize(const char** error_str) const { | 14907 RawInstance* Instance::CheckAndCanonicalize(Thread* thread, |
| 14908 const char** error_str) const { | |
| 14833 ASSERT(!IsNull()); | 14909 ASSERT(!IsNull()); |
| 14834 if (this->IsCanonical()) { | 14910 if (this->IsCanonical()) { |
| 14835 return this->raw(); | 14911 return this->raw(); |
| 14836 } | 14912 } |
| 14837 Thread* thread = Thread::Current(); | 14913 if (!CheckAndCanonicalizeFields(thread, error_str)) { |
| 14838 Zone* zone = thread->zone(); | |
| 14839 if (!CheckAndCanonicalizeFields(zone, error_str)) { | |
| 14840 return Instance::null(); | 14914 return Instance::null(); |
| 14841 } | 14915 } |
| 14916 Zone* zone = thread->zone(); | |
| 14842 Isolate* isolate = thread->isolate(); | 14917 Isolate* isolate = thread->isolate(); |
| 14843 Instance& result = Instance::Handle(zone); | 14918 Instance& result = Instance::Handle(zone); |
| 14844 const Class& cls = Class::Handle(zone, this->clazz()); | 14919 const Class& cls = Class::Handle(zone, this->clazz()); |
| 14845 intptr_t index = 0; | |
| 14846 result ^= cls.LookupCanonicalInstance(zone, *this, &index); | |
| 14847 if (!result.IsNull()) { | |
| 14848 return result.raw(); | |
| 14849 } | |
| 14850 { | 14920 { |
| 14851 SafepointMutexLocker ml(isolate->constant_canonicalization_mutex()); | 14921 SafepointMutexLocker ml(isolate->constant_canonicalization_mutex()); |
| 14852 // Retry lookup. | 14922 if (IsNew()) { |
| 14853 { | 14923 result ^= cls.LookupCanonicalInstance(zone, *this); |
| 14854 result ^= cls.LookupCanonicalInstance(zone, *this, &index); | |
| 14855 if (!result.IsNull()) { | 14924 if (!result.IsNull()) { |
| 14856 return result.raw(); | 14925 return result.raw(); |
| 14857 } | 14926 } |
| 14858 } | 14927 ASSERT((isolate == Dart::vm_isolate()) || !InVMHeap()); |
| 14859 | |
| 14860 // The value needs to be added to the list. Grow the list if | |
| 14861 // it is full. | |
| 14862 result ^= this->raw(); | |
| 14863 ASSERT((isolate == Dart::vm_isolate()) || !result.InVMHeap()); | |
| 14864 if (result.IsNew()) { | |
| 14865 // Create a canonical object in old space. | 14928 // Create a canonical object in old space. |
| 14866 result ^= Object::Clone(result, Heap::kOld); | 14929 result ^= Object::Clone(*this, Heap::kOld); |
| 14930 } else { | |
| 14931 result ^= this->raw(); | |
| 14867 } | 14932 } |
| 14868 ASSERT(result.IsOld()); | 14933 ASSERT(result.IsOld()); |
| 14869 result.SetCanonical(); | 14934 result.SetCanonical(); |
| 14870 cls.InsertCanonicalConstant(index, result); | 14935 return cls.InsertCanonicalConstant(zone, result); |
| 14871 return result.raw(); | |
| 14872 } | 14936 } |
| 14873 } | 14937 } |
| 14874 | 14938 |
| 14875 | 14939 |
| 14876 RawAbstractType* Instance::GetType() const { | 14940 RawAbstractType* Instance::GetType() const { |
| 14877 if (IsNull()) { | 14941 if (IsNull()) { |
| 14878 return Type::NullType(); | 14942 return Type::NullType(); |
| 14879 } | 14943 } |
| 14880 const Class& cls = Class::Handle(clazz()); | 14944 const Class& cls = Class::Handle(clazz()); |
| 14881 if (cls.IsClosureClass()) { | 14945 if (cls.IsClosureClass()) { |
| (...skipping 2598 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 17480 | 17544 |
| 17481 RawMixinAppType* MixinAppType::New(const AbstractType& super_type, | 17545 RawMixinAppType* MixinAppType::New(const AbstractType& super_type, |
| 17482 const Array& mixin_types) { | 17546 const Array& mixin_types) { |
| 17483 const MixinAppType& result = MixinAppType::Handle(MixinAppType::New()); | 17547 const MixinAppType& result = MixinAppType::Handle(MixinAppType::New()); |
| 17484 result.set_super_type(super_type); | 17548 result.set_super_type(super_type); |
| 17485 result.set_mixin_types(mixin_types); | 17549 result.set_mixin_types(mixin_types); |
| 17486 return result.raw(); | 17550 return result.raw(); |
| 17487 } | 17551 } |
| 17488 | 17552 |
| 17489 | 17553 |
| 17490 RawInstance* Number::CheckAndCanonicalize(const char** error_str) const { | 17554 RawInstance* Number::CheckAndCanonicalize(Thread* thread, |
| 17555 const char** error_str) const { | |
| 17491 intptr_t cid = GetClassId(); | 17556 intptr_t cid = GetClassId(); |
| 17492 switch (cid) { | 17557 switch (cid) { |
| 17493 case kSmiCid: | 17558 case kSmiCid: |
| 17494 return reinterpret_cast<RawSmi*>(raw_value()); | 17559 return reinterpret_cast<RawSmi*>(raw_value()); |
| 17495 case kMintCid: | 17560 case kMintCid: |
| 17496 return Mint::NewCanonical(Mint::Cast(*this).value()); | 17561 return Mint::NewCanonical(Mint::Cast(*this).value()); |
| 17497 case kDoubleCid: | 17562 case kDoubleCid: |
| 17498 return Double::NewCanonical(Double::Cast(*this).value()); | 17563 return Double::NewCanonical(Double::Cast(*this).value()); |
| 17499 case kBigintCid: { | 17564 case kBigintCid: { |
| 17500 Thread* thread = Thread::Current(); | |
| 17501 Zone* zone = thread->zone(); | 17565 Zone* zone = thread->zone(); |
| 17502 Isolate* isolate = thread->isolate(); | 17566 Isolate* isolate = thread->isolate(); |
| 17503 if (!CheckAndCanonicalizeFields(zone, error_str)) { | 17567 if (!CheckAndCanonicalizeFields(thread, error_str)) { |
| 17504 return Instance::null(); | 17568 return Instance::null(); |
| 17505 } | 17569 } |
| 17506 Bigint& result = Bigint::Handle(zone); | 17570 Bigint& result = Bigint::Handle(zone); |
| 17507 const Class& cls = Class::Handle(zone, this->clazz()); | 17571 const Class& cls = Class::Handle(zone, this->clazz()); |
| 17508 intptr_t index = 0; | 17572 intptr_t index = 0; |
| 17509 result ^= cls.LookupCanonicalBigint(zone, Bigint::Cast(*this), &index); | 17573 result ^= cls.LookupCanonicalBigint(zone, Bigint::Cast(*this), &index); |
| 17510 if (!result.IsNull()) { | 17574 if (!result.IsNull()) { |
| 17511 return result.raw(); | 17575 return result.raw(); |
| 17512 } | 17576 } |
| 17513 { | 17577 { |
| (...skipping 732 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 18246 | 18310 |
| 18247 for (intptr_t i = 0; i < used; i++) { | 18311 for (intptr_t i = 0; i < used; i++) { |
| 18248 if (this->DigitAt(i) != other_bgi.DigitAt(i)) { | 18312 if (this->DigitAt(i) != other_bgi.DigitAt(i)) { |
| 18249 return false; | 18313 return false; |
| 18250 } | 18314 } |
| 18251 } | 18315 } |
| 18252 return true; | 18316 return true; |
| 18253 } | 18317 } |
| 18254 | 18318 |
| 18255 | 18319 |
| 18256 bool Bigint::CheckAndCanonicalizeFields(Zone* zone, | 18320 bool Bigint::CheckAndCanonicalizeFields(Thread* thread, |
| 18257 const char** error_str) const { | 18321 const char** error_str) const { |
| 18322 Zone* zone = thread->zone(); | |
| 18258 // Bool field neg should always be canonical. | 18323 // Bool field neg should always be canonical. |
| 18259 ASSERT(Bool::Handle(zone, neg()).IsCanonical()); | 18324 ASSERT(Bool::Handle(zone, neg()).IsCanonical()); |
| 18260 // Smi field used is canonical by definition. | 18325 // Smi field used is canonical by definition. |
| 18261 if (Used() > 0) { | 18326 if (Used() > 0) { |
| 18262 // Canonicalize TypedData field digits. | 18327 // Canonicalize TypedData field digits. |
| 18263 TypedData& digits_ = TypedData::Handle(zone, digits()); | 18328 TypedData& digits_ = TypedData::Handle(zone, digits()); |
| 18264 digits_ ^= digits_.CheckAndCanonicalize(NULL); | 18329 digits_ ^= digits_.CheckAndCanonicalize(thread, NULL); |
| 18265 ASSERT(!digits_.IsNull()); | 18330 ASSERT(!digits_.IsNull()); |
| 18266 set_digits(digits_); | 18331 set_digits(digits_); |
| 18267 } else { | 18332 } else { |
| 18268 ASSERT(digits() == TypedData::EmptyUint32Array(Thread::Current())); | 18333 ASSERT(digits() == TypedData::EmptyUint32Array(Thread::Current())); |
| 18269 } | 18334 } |
| 18270 return true; | 18335 return true; |
| 18271 } | 18336 } |
| 18272 | 18337 |
| 18273 | 18338 |
| 18274 RawBigint* Bigint::New(Heap::Space space) { | 18339 RawBigint* Bigint::New(Heap::Space space) { |
| (...skipping 951 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 19226 intptr_t slen = other.Length(); | 19291 intptr_t slen = other.Length(); |
| 19227 for (int i = 0; i < slen; i++) { | 19292 for (int i = 0; i < slen; i++) { |
| 19228 if (this->CharAt(i) != other.CharAt(i)) { | 19293 if (this->CharAt(i) != other.CharAt(i)) { |
| 19229 return false; | 19294 return false; |
| 19230 } | 19295 } |
| 19231 } | 19296 } |
| 19232 return true; | 19297 return true; |
| 19233 } | 19298 } |
| 19234 | 19299 |
| 19235 | 19300 |
| 19236 RawInstance* String::CheckAndCanonicalize(const char** error_str) const { | 19301 RawInstance* String::CheckAndCanonicalize(Thread* thread, |
| 19302 const char** error_str) const { | |
| 19237 if (IsCanonical()) { | 19303 if (IsCanonical()) { |
| 19238 return this->raw(); | 19304 return this->raw(); |
| 19239 } | 19305 } |
| 19240 return Symbols::New(Thread::Current(), *this); | 19306 return Symbols::New(Thread::Current(), *this); |
| 19241 } | 19307 } |
| 19242 | 19308 |
| 19243 | 19309 |
| 19244 RawString* String::New(const char* cstr, Heap::Space space) { | 19310 RawString* String::New(const char* cstr, Heap::Space space) { |
| 19245 ASSERT(cstr != NULL); | 19311 ASSERT(cstr != NULL); |
| 19246 intptr_t array_len = strlen(cstr); | 19312 intptr_t array_len = strlen(cstr); |
| (...skipping 1436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 20683 return false; | 20749 return false; |
| 20684 } | 20750 } |
| 20685 | 20751 |
| 20686 for (intptr_t i = 0; i < len; i++) { | 20752 for (intptr_t i = 0; i < len; i++) { |
| 20687 if (this->At(i) != other_arr.At(i)) { | 20753 if (this->At(i) != other_arr.At(i)) { |
| 20688 return false; | 20754 return false; |
| 20689 } | 20755 } |
| 20690 } | 20756 } |
| 20691 | 20757 |
| 20692 // Now check if both arrays have the same type arguments. | 20758 // Now check if both arrays have the same type arguments. |
| 20759 if (GetTypeArguments() == other.GetTypeArguments()) { | |
| 20760 return true; | |
| 20761 } | |
| 20693 const TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments()); | 20762 const TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments()); |
| 20694 const TypeArguments& other_type_args = TypeArguments::Handle( | 20763 const TypeArguments& other_type_args = TypeArguments::Handle( |
| 20695 other.GetTypeArguments()); | 20764 other.GetTypeArguments()); |
| 20696 if (!type_args.Equals(other_type_args)) { | 20765 if (!type_args.Equals(other_type_args)) { |
| 20697 return false; | 20766 return false; |
| 20698 } | 20767 } |
| 20699 return true; | 20768 return true; |
| 20700 } | 20769 } |
| 20701 | 20770 |
| 20702 | 20771 |
| 20772 uword Array::ComputeCanonicalTableHash() const { | |
| 20773 ASSERT(!IsNull()); | |
| 20774 intptr_t len = Length(); | |
| 20775 uword hash = len; | |
| 20776 uword value = reinterpret_cast<uword>(GetTypeArguments()); | |
| 20777 hash = CombineHashes(hash, value); | |
| 20778 for (intptr_t i = 0; i < len; i++) { | |
| 20779 value = reinterpret_cast<uword>(At(i)); | |
| 20780 hash = CombineHashes(hash, value); | |
| 20781 } | |
| 20782 return FinalizeHash(hash); | |
| 20783 } | |
| 20784 | |
| 20785 | |
| 20703 RawArray* Array::New(intptr_t len, Heap::Space space) { | 20786 RawArray* Array::New(intptr_t len, Heap::Space space) { |
| 20704 ASSERT(Isolate::Current()->object_store()->array_class() != Class::null()); | 20787 ASSERT(Isolate::Current()->object_store()->array_class() != Class::null()); |
| 20705 return New(kClassId, len, space); | 20788 return New(kClassId, len, space); |
| 20706 } | 20789 } |
| 20707 | 20790 |
| 20708 | 20791 |
| 20709 RawArray* Array::New(intptr_t class_id, intptr_t len, Heap::Space space) { | 20792 RawArray* Array::New(intptr_t class_id, intptr_t len, Heap::Space space) { |
| 20710 if ((len < 0) || (len > Array::kMaxElements)) { | 20793 if ((len < 0) || (len > Array::kMaxElements)) { |
| 20711 // This should be caught before we reach here. | 20794 // This should be caught before we reach here. |
| 20712 FATAL1("Fatal error in Array::New: invalid len %" Pd "\n", len); | 20795 FATAL1("Fatal error in Array::New: invalid len %" Pd "\n", len); |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 20834 array.SetLength(used_len); | 20917 array.SetLength(used_len); |
| 20835 | 20918 |
| 20836 // Null the GrowableObjectArray, we are removing its backing array. | 20919 // Null the GrowableObjectArray, we are removing its backing array. |
| 20837 growable_array.SetLength(0); | 20920 growable_array.SetLength(0); |
| 20838 growable_array.SetData(Object::empty_array()); | 20921 growable_array.SetData(Object::empty_array()); |
| 20839 | 20922 |
| 20840 return array.raw(); | 20923 return array.raw(); |
| 20841 } | 20924 } |
| 20842 | 20925 |
| 20843 | 20926 |
| 20844 bool Array::CheckAndCanonicalizeFields(Zone* zone, | 20927 bool Array::CheckAndCanonicalizeFields(Thread* thread, |
| 20845 const char** error_str) const { | 20928 const char** error_str) const { |
| 20846 Object& obj = Object::Handle(zone); | 20929 intptr_t len = Length(); |
| 20847 // Iterate over all elements, canonicalize numbers and strings, expect all | 20930 if (len > 0) { |
| 20848 // other instances to be canonical otherwise report error (return false). | 20931 Zone* zone = thread->zone(); |
| 20849 for (intptr_t i = 0; i < Length(); i++) { | 20932 Object& obj = Object::Handle(zone); |
| 20850 obj = At(i); | 20933 // Iterate over all elements, canonicalize numbers and strings, expect all |
| 20851 if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { | 20934 // other instances to be canonical otherwise report error (return false). |
| 20852 if (obj.IsNumber() || obj.IsString()) { | 20935 for (intptr_t i = 0; i < len; i++) { |
| 20853 obj = Instance::Cast(obj).CheckAndCanonicalize(NULL); | 20936 obj = At(i); |
| 20854 ASSERT(!obj.IsNull()); | 20937 if (obj.IsInstance() && !obj.IsSmi() && !obj.IsCanonical()) { |
| 20855 this->SetAt(i, obj); | 20938 if (obj.IsNumber() || obj.IsString()) { |
| 20856 } else { | 20939 obj = Instance::Cast(obj).CheckAndCanonicalize(thread, NULL); |
| 20857 ASSERT(error_str != NULL); | 20940 ASSERT(!obj.IsNull()); |
| 20858 char* chars = OS::SCreate(Thread::Current()->zone(), | 20941 this->SetAt(i, obj); |
| 20859 "element at index %" Pd ": %s\n", i, obj.ToCString()); | 20942 } else { |
| 20860 *error_str = chars; | 20943 ASSERT(error_str != NULL); |
| 20861 return false; | 20944 char* chars = OS::SCreate( |
| 20945 zone, "element at index %" Pd ": %s\n", i, obj.ToCString()); | |
| 20946 *error_str = chars; | |
| 20947 return false; | |
| 20948 } | |
| 20862 } | 20949 } |
| 20863 } | 20950 } |
| 20864 } | 20951 } |
| 20865 return true; | 20952 return true; |
| 20866 } | 20953 } |
| 20867 | 20954 |
| 20868 | 20955 |
| 20869 RawImmutableArray* ImmutableArray::New(intptr_t len, | 20956 RawImmutableArray* ImmutableArray::New(intptr_t len, |
| 20870 Heap::Space space) { | 20957 Heap::Space space) { |
| 20871 ASSERT(Isolate::Current()->object_store()->immutable_array_class() != | 20958 ASSERT(Isolate::Current()->object_store()->immutable_array_class() != |
| (...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 21335 const intptr_t len = this->LengthInBytes(); | 21422 const intptr_t len = this->LengthInBytes(); |
| 21336 if (len != other_typed_data.LengthInBytes()) { | 21423 if (len != other_typed_data.LengthInBytes()) { |
| 21337 return false; | 21424 return false; |
| 21338 } | 21425 } |
| 21339 NoSafepointScope no_safepoint; | 21426 NoSafepointScope no_safepoint; |
| 21340 return (len == 0) || | 21427 return (len == 0) || |
| 21341 (memcmp(DataAddr(0), other_typed_data.DataAddr(0), len) == 0); | 21428 (memcmp(DataAddr(0), other_typed_data.DataAddr(0), len) == 0); |
| 21342 } | 21429 } |
| 21343 | 21430 |
| 21344 | 21431 |
| 21432 uword TypedData::ComputeCanonicalTableHash() const { | |
| 21433 const intptr_t len = this->LengthInBytes(); | |
| 21434 ASSERT(len != 0); | |
| 21435 uword hash = len; | |
| 21436 for (intptr_t i = 0; i < len; i++) { | |
| 21437 hash = CombineHashes(len, GetUint8(i)); | |
| 21438 } | |
| 21439 return FinalizeHash(hash); | |
| 21440 } | |
| 21441 | |
| 21442 | |
| 21345 RawTypedData* TypedData::New(intptr_t class_id, | 21443 RawTypedData* TypedData::New(intptr_t class_id, |
| 21346 intptr_t len, | 21444 intptr_t len, |
| 21347 Heap::Space space) { | 21445 Heap::Space space) { |
| 21348 if (len < 0 || len > TypedData::MaxElements(class_id)) { | 21446 if (len < 0 || len > TypedData::MaxElements(class_id)) { |
| 21349 FATAL1("Fatal error in TypedData::New: invalid len %" Pd "\n", len); | 21447 FATAL1("Fatal error in TypedData::New: invalid len %" Pd "\n", len); |
| 21350 } | 21448 } |
| 21351 TypedData& result = TypedData::Handle(); | 21449 TypedData& result = TypedData::Handle(); |
| 21352 { | 21450 { |
| 21353 const intptr_t lengthInBytes = len * ElementSizeInBytes(class_id); | 21451 const intptr_t lengthInBytes = len * ElementSizeInBytes(class_id); |
| 21354 RawObject* raw = Object::Allocate(class_id, | 21452 RawObject* raw = Object::Allocate(class_id, |
| (...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 22029 return UserTag::null(); | 22127 return UserTag::null(); |
| 22030 } | 22128 } |
| 22031 | 22129 |
| 22032 | 22130 |
| 22033 const char* UserTag::ToCString() const { | 22131 const char* UserTag::ToCString() const { |
| 22034 const String& tag_label = String::Handle(label()); | 22132 const String& tag_label = String::Handle(label()); |
| 22035 return tag_label.ToCString(); | 22133 return tag_label.ToCString(); |
| 22036 } | 22134 } |
| 22037 | 22135 |
| 22038 } // namespace dart | 22136 } // namespace dart |
| OLD | NEW |