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/bigint_operations.h" | 10 #include "vm/bigint_operations.h" |
| (...skipping 555 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 566 // declared in RawArray. | 566 // declared in RawArray. |
| 567 cls.set_type_arguments_field_offset(Array::type_arguments_offset()); | 567 cls.set_type_arguments_field_offset(Array::type_arguments_offset()); |
| 568 | 568 |
| 569 // Set up the growable object array class (Has to be done after the array | 569 // Set up the growable object array class (Has to be done after the array |
| 570 // class is setup as one of its field is an array object). | 570 // class is setup as one of its field is an array object). |
| 571 cls = Class::New<GrowableObjectArray>(); | 571 cls = Class::New<GrowableObjectArray>(); |
| 572 object_store->set_growable_object_array_class(cls); | 572 object_store->set_growable_object_array_class(cls); |
| 573 cls.set_type_arguments_field_offset( | 573 cls.set_type_arguments_field_offset( |
| 574 GrowableObjectArray::type_arguments_offset()); | 574 GrowableObjectArray::type_arguments_offset()); |
| 575 | 575 |
| 576 // canonical_type_arguments_ are NULL terminated. | 576 // canonical_type_arguments_ are Smi terminated. |
| 577 array = Array::New(4); | 577 // Last element contains the count of used slots. |
| 578 const intptr_t kInitialCanonicalTypeArgumentsSize = 4; | |
| 579 array = Array::New(kInitialCanonicalTypeArgumentsSize + 1); | |
| 580 array.SetAt(kInitialCanonicalTypeArgumentsSize, Smi::Handle(Smi::New(0))); | |
| 578 object_store->set_canonical_type_arguments(array); | 581 object_store->set_canonical_type_arguments(array); |
| 579 | 582 |
| 580 // Setup type class early in the process. | 583 // Setup type class early in the process. |
| 581 cls = Class::New<Type>(); | 584 cls = Class::New<Type>(); |
| 582 object_store->set_type_class(cls); | 585 object_store->set_type_class(cls); |
| 583 | 586 |
| 584 cls = Class::New<TypeParameter>(); | 587 cls = Class::New<TypeParameter>(); |
| 585 object_store->set_type_parameter_class(cls); | 588 object_store->set_type_parameter_class(cls); |
| 586 | 589 |
| 587 // Pre-allocate the OneByteString class needed by the symbol table. | 590 // Pre-allocate the OneByteString class needed by the symbol table. |
| (...skipping 1905 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2493 } | 2496 } |
| 2494 | 2497 |
| 2495 | 2498 |
| 2496 bool AbstractTypeArguments::IsUninstantiatedIdentity() const { | 2499 bool AbstractTypeArguments::IsUninstantiatedIdentity() const { |
| 2497 // AbstractTypeArguments is an abstract class. | 2500 // AbstractTypeArguments is an abstract class. |
| 2498 UNREACHABLE(); | 2501 UNREACHABLE(); |
| 2499 return false; | 2502 return false; |
| 2500 } | 2503 } |
| 2501 | 2504 |
| 2502 | 2505 |
| 2506 static uword FinalizeHash(uword hash) { | |
| 2507 hash += hash << 3; | |
| 2508 hash ^= hash >> 11; | |
| 2509 hash += hash << 15; | |
| 2510 return hash; | |
| 2511 } | |
| 2512 | |
| 2513 | |
| 2514 uword AbstractTypeArguments::Hash() const { | |
| 2515 if (IsNull()) return 0; | |
| 2516 uword result = 0; | |
| 2517 intptr_t num_types = Length(); | |
| 2518 AbstractType& type = AbstractType::Handle(); | |
| 2519 for (intptr_t i = 0; i < num_types; i++) { | |
| 2520 type = TypeAt(i); | |
| 2521 result += type.Hash(); | |
| 2522 result += result << 10; | |
| 2523 result ^= result >> 6; | |
| 2524 } | |
| 2525 return FinalizeHash(result); | |
| 2526 } | |
| 2527 | |
| 2528 | |
| 2503 RawString* AbstractTypeArguments::SubvectorName( | 2529 RawString* AbstractTypeArguments::SubvectorName( |
| 2504 intptr_t from_index, | 2530 intptr_t from_index, |
| 2505 intptr_t len, | 2531 intptr_t len, |
| 2506 NameVisibility name_visibility) const { | 2532 NameVisibility name_visibility) const { |
| 2507 ASSERT(from_index + len <= Length()); | 2533 ASSERT(from_index + len <= Length()); |
| 2508 String& name = String::Handle(); | 2534 String& name = String::Handle(); |
| 2509 const intptr_t num_strings = 2*len + 1; // "<""T"", ""T"">". | 2535 const intptr_t num_strings = 2*len + 1; // "<""T"", ""T"">". |
| 2510 const Array& strings = Array::Handle(Array::New(num_strings)); | 2536 const Array& strings = Array::Handle(Array::New(num_strings)); |
| 2511 intptr_t s = 0; | 2537 intptr_t s = 0; |
| 2512 strings.SetAt(s++, String::Handle(Symbols::New("<"))); | 2538 strings.SetAt(s++, String::Handle(Symbols::New("<"))); |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2702 } | 2728 } |
| 2703 | 2729 |
| 2704 | 2730 |
| 2705 const char* AbstractTypeArguments::ToCString() const { | 2731 const char* AbstractTypeArguments::ToCString() const { |
| 2706 // AbstractTypeArguments is an abstract class, valid only for representing | 2732 // AbstractTypeArguments is an abstract class, valid only for representing |
| 2707 // null. | 2733 // null. |
| 2708 if (IsNull()) { | 2734 if (IsNull()) { |
| 2709 return "NULL AbstractTypeArguments"; | 2735 return "NULL AbstractTypeArguments"; |
| 2710 } | 2736 } |
| 2711 UNREACHABLE(); | 2737 UNREACHABLE(); |
| 2712 return "AbstractTypeArguments"; | 2738 return NULL; |
|
Ivan Posva
2012/12/12 21:25:10
Please leave this as it was. All other abstract cl
Florian Schneider
2012/12/13 11:59:45
Fine.
| |
| 2713 } | 2739 } |
| 2714 | 2740 |
| 2715 | 2741 |
| 2716 intptr_t TypeArguments::Length() const { | 2742 intptr_t TypeArguments::Length() const { |
| 2717 ASSERT(!IsNull()); | 2743 ASSERT(!IsNull()); |
| 2718 return Smi::Value(raw_ptr()->length_); | 2744 return Smi::Value(raw_ptr()->length_); |
| 2719 } | 2745 } |
| 2720 | 2746 |
| 2721 | 2747 |
| 2722 RawAbstractType* TypeArguments::TypeAt(intptr_t index) const { | 2748 RawAbstractType* TypeArguments::TypeAt(intptr_t index) const { |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2826 | 2852 |
| 2827 | 2853 |
| 2828 void TypeArguments::SetLength(intptr_t value) const { | 2854 void TypeArguments::SetLength(intptr_t value) const { |
| 2829 ASSERT(!IsCanonical()); | 2855 ASSERT(!IsCanonical()); |
| 2830 // This is only safe because we create a new Smi, which does not cause | 2856 // This is only safe because we create a new Smi, which does not cause |
| 2831 // heap allocation. | 2857 // heap allocation. |
| 2832 raw_ptr()->length_ = Smi::New(value); | 2858 raw_ptr()->length_ = Smi::New(value); |
| 2833 } | 2859 } |
| 2834 | 2860 |
| 2835 | 2861 |
| 2862 static void GrowCanonicalTypeArguments(Isolate* isolate, const Array& table) { | |
|
Ivan Posva
2012/12/12 21:25:10
Do not pass Isolate as a parameter unless it is a
| |
| 2863 // Last element of the array is the number of used elements. | |
| 2864 intptr_t table_size = table.Length() - 1; | |
| 2865 intptr_t new_table_size = table_size * 2; | |
| 2866 Array& new_table = Array::Handle(isolate, Array::New(new_table_size + 1)); | |
| 2867 // Copy all elements from the original table to the newly allocated | |
| 2868 // array. | |
| 2869 TypeArguments& element = TypeArguments::Handle(isolate); | |
| 2870 Object& new_element = Object::Handle(isolate); | |
| 2871 for (intptr_t i = 0; i < table_size; i++) { | |
| 2872 element ^= table.At(i); | |
| 2873 if (!element.IsNull()) { | |
| 2874 intptr_t hash = element.Hash(); | |
| 2875 ASSERT(Utils::IsPowerOfTwo(new_table_size)); | |
| 2876 intptr_t index = hash & (new_table_size - 1); | |
| 2877 new_element = new_table.At(index); | |
| 2878 while (!new_element.IsNull()) { | |
| 2879 index = (index + 1) & (new_table_size - 1); // Move to next element. | |
| 2880 new_element = new_table.At(index); | |
| 2881 } | |
| 2882 new_table.SetAt(index, element); | |
| 2883 } | |
| 2884 } | |
| 2885 // Copy used count. | |
| 2886 new_element = table.At(table_size); | |
| 2887 new_table.SetAt(new_table_size, new_element); | |
| 2888 // Remember the new table now. | |
| 2889 isolate->object_store()->set_canonical_type_arguments(new_table); | |
| 2890 } | |
| 2891 | |
| 2892 | |
| 2893 static void InsertIntoCanonicalTypeArguments(Isolate* isolate, | |
|
Ivan Posva
2012/12/12 21:25:10
ditto
| |
| 2894 const Array& table, | |
| 2895 const TypeArguments& arguments, | |
| 2896 intptr_t index) { | |
| 2897 arguments.SetCanonical(); // Mark object as being canonical. | |
| 2898 table.SetAt(index, arguments); // Remember the new element. | |
| 2899 // Update used count. | |
| 2900 // Last element of the array is the number of used elements. | |
| 2901 intptr_t table_size = table.Length() - 1; | |
| 2902 Smi& used = Smi::Handle(isolate); | |
| 2903 used ^= table.At(table_size); | |
| 2904 intptr_t used_elements = used.Value() + 1; | |
| 2905 used = Smi::New(used_elements); | |
| 2906 table.SetAt(table_size, used); | |
| 2907 | |
| 2908 // Rehash if table is 75% full. | |
| 2909 if (used_elements > ((table_size / 4) * 3)) { | |
| 2910 GrowCanonicalTypeArguments(isolate, table); | |
| 2911 } | |
| 2912 } | |
| 2913 | |
| 2914 | |
| 2915 static intptr_t FindIndexInCanonicalTypeArguments( | |
| 2916 Isolate* isolate, | |
|
Ivan Posva
2012/12/12 21:25:10
ditto
Florian Schneider
2012/12/13 11:59:45
This is frequently called in checked mode. The two
| |
| 2917 const Array& table, | |
| 2918 const TypeArguments& arguments, | |
| 2919 intptr_t hash) { | |
| 2920 // Last element of the array is the number of used elements. | |
| 2921 intptr_t table_size = table.Length() - 1; | |
| 2922 ASSERT(Utils::IsPowerOfTwo(table_size)); | |
| 2923 intptr_t index = hash & (table_size - 1); | |
| 2924 | |
| 2925 TypeArguments& current = TypeArguments::Handle(isolate); | |
| 2926 current ^= table.At(index); | |
| 2927 while (!current.IsNull() && !current.Equals(arguments)) { | |
| 2928 index = (index + 1) & (table_size - 1); // Move to next element. | |
| 2929 current ^= table.At(index); | |
| 2930 } | |
| 2931 return index; // Index of element if found or slot into which to add it. | |
| 2932 } | |
| 2933 | |
| 2934 | |
| 2836 RawAbstractTypeArguments* TypeArguments::Canonicalize() const { | 2935 RawAbstractTypeArguments* TypeArguments::Canonicalize() const { |
| 2837 if (IsNull() || IsCanonical()) { | 2936 if (IsNull() || IsCanonical()) { |
| 2838 ASSERT(IsOld()); | 2937 ASSERT(IsOld()); |
| 2839 return this->raw(); | 2938 return this->raw(); |
| 2840 } | 2939 } |
| 2841 ObjectStore* object_store = Isolate::Current()->object_store(); | 2940 Isolate* isolate = Isolate::Current(); |
| 2842 // 'table' must be null terminated. | 2941 ObjectStore* object_store = isolate->object_store(); |
| 2843 Array& table = Array::Handle(object_store->canonical_type_arguments()); | 2942 const Array& table = Array::Handle(isolate, |
| 2943 object_store->canonical_type_arguments()); | |
| 2844 ASSERT(table.Length() > 0); | 2944 ASSERT(table.Length() > 0); |
| 2845 intptr_t index = 0; | 2945 TypeArguments& self = TypeArguments::Handle(isolate, this->raw()); |
|
Ivan Posva
2012/12/12 21:25:10
Why do you need to allocate a new handle here? thi
Florian Schneider
2012/12/13 11:59:45
Done.
| |
| 2846 TypeArguments& result = TypeArguments::Handle(); | 2946 intptr_t index = FindIndexInCanonicalTypeArguments(isolate, |
| 2947 table, | |
| 2948 self, | |
| 2949 Hash()); | |
| 2950 TypeArguments& result = TypeArguments::Handle(isolate); | |
| 2847 result ^= table.At(index); | 2951 result ^= table.At(index); |
| 2848 while (!result.IsNull()) { | 2952 if (result.IsNull()) { |
| 2849 if (this->Equals(result)) { | 2953 // Make sure we have an old space object and add it to the table. |
| 2850 return result.raw(); | 2954 result ^= this->raw(); |
|
Ivan Posva
2012/12/12 21:25:10
if (this->IsNew()) {
result ^= Object::Clone(...
Florian Schneider
2012/12/13 11:59:45
Done.
| |
| 2955 if (result.IsNew()) { | |
| 2956 result ^= Object::Clone(result, Heap::kOld); | |
| 2851 } | 2957 } |
| 2852 result ^= table.At(++index); | 2958 ASSERT(result.IsOld()); |
| 2959 InsertIntoCanonicalTypeArguments(isolate, table, result, index); | |
| 2853 } | 2960 } |
| 2854 // Not found. Add 'this' to table. | 2961 ASSERT(result.Equals(self)); |
| 2855 result ^= this->raw(); | 2962 ASSERT(!result.IsNull()); |
| 2856 if (result.IsNew()) { | 2963 ASSERT(result.IsTypeArguments()); |
| 2857 result ^= Object::Clone(result, Heap::kOld); | |
| 2858 } | |
| 2859 ASSERT(result.IsOld()); | |
| 2860 if (index == table.Length() - 1) { | |
| 2861 table = Array::Grow(table, table.Length() + 4, Heap::kOld); | |
| 2862 object_store->set_canonical_type_arguments(table); | |
| 2863 } | |
| 2864 table.SetAt(index, result); | |
| 2865 result.SetCanonical(); | |
| 2866 return result.raw(); | 2964 return result.raw(); |
| 2867 } | 2965 } |
| 2868 | 2966 |
| 2869 | 2967 |
| 2870 const char* TypeArguments::ToCString() const { | 2968 const char* TypeArguments::ToCString() const { |
| 2871 if (IsNull()) { | 2969 if (IsNull()) { |
| 2872 return "NULL TypeArguments"; | 2970 return "NULL TypeArguments"; |
| 2873 } | 2971 } |
| 2874 const char* format = "%s [%s]"; | 2972 const char* format = "%s [%s]"; |
| 2875 const char* prev_cstr = "TypeArguments:"; | 2973 const char* prev_cstr = "TypeArguments:"; |
| (...skipping 5831 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 8707 } | 8805 } |
| 8708 const Class& cls = Class::Handle(type_class()); | 8806 const Class& cls = Class::Handle(type_class()); |
| 8709 return cls.TypeTest(test_kind, | 8807 return cls.TypeTest(test_kind, |
| 8710 AbstractTypeArguments::Handle(arguments()), | 8808 AbstractTypeArguments::Handle(arguments()), |
| 8711 Class::Handle(other.type_class()), | 8809 Class::Handle(other.type_class()), |
| 8712 AbstractTypeArguments::Handle(other.arguments()), | 8810 AbstractTypeArguments::Handle(other.arguments()), |
| 8713 malformed_error); | 8811 malformed_error); |
| 8714 } | 8812 } |
| 8715 | 8813 |
| 8716 | 8814 |
| 8815 uword AbstractType::Hash() const { | |
| 8816 // AbstractType is an abstract class. | |
| 8817 UNREACHABLE(); | |
| 8818 return 0; | |
| 8819 } | |
| 8820 | |
| 8821 | |
| 8717 const char* AbstractType::ToCString() const { | 8822 const char* AbstractType::ToCString() const { |
| 8718 // AbstractType is an abstract class. | 8823 // AbstractType is an abstract class. |
| 8719 UNREACHABLE(); | 8824 UNREACHABLE(); |
| 8720 return "AbstractType"; | 8825 return "AbstractType"; |
| 8721 } | 8826 } |
| 8722 | 8827 |
| 8723 | 8828 |
| 8724 RawType* Type::NullType() { | 8829 RawType* Type::NullType() { |
| 8725 return Isolate::Current()->object_store()->null_type(); | 8830 return Isolate::Current()->object_store()->null_type(); |
| 8726 } | 8831 } |
| (...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 8981 new_canonical_types.SetAt(index, *this); | 9086 new_canonical_types.SetAt(index, *this); |
| 8982 } else { | 9087 } else { |
| 8983 canonical_types.SetAt(index, *this); | 9088 canonical_types.SetAt(index, *this); |
| 8984 } | 9089 } |
| 8985 ASSERT(IsOld()); | 9090 ASSERT(IsOld()); |
| 8986 SetCanonical(); | 9091 SetCanonical(); |
| 8987 return this->raw(); | 9092 return this->raw(); |
| 8988 } | 9093 } |
| 8989 | 9094 |
| 8990 | 9095 |
| 9096 uword Type::Hash() const { | |
| 9097 ASSERT(IsFinalized()); | |
| 9098 uword result = 1; | |
| 9099 if (IsMalformed()) return result; | |
| 9100 result += Class::Handle(type_class()).id(); | |
| 9101 result += AbstractTypeArguments::Handle(arguments()).Hash(); | |
| 9102 return FinalizeHash(result); | |
| 9103 } | |
| 9104 | |
| 9105 | |
| 8991 void Type::set_type_class(const Object& value) const { | 9106 void Type::set_type_class(const Object& value) const { |
| 8992 ASSERT(!value.IsNull() && (value.IsClass() || value.IsUnresolvedClass())); | 9107 ASSERT(!value.IsNull() && (value.IsClass() || value.IsUnresolvedClass())); |
| 8993 StorePointer(&raw_ptr()->type_class_, value.raw()); | 9108 StorePointer(&raw_ptr()->type_class_, value.raw()); |
| 8994 } | 9109 } |
| 8995 | 9110 |
| 8996 | 9111 |
| 8997 void Type::set_arguments(const AbstractTypeArguments& value) const { | 9112 void Type::set_arguments(const AbstractTypeArguments& value) const { |
| 8998 StorePointer(&raw_ptr()->arguments_, value.raw()); | 9113 StorePointer(&raw_ptr()->arguments_, value.raw()); |
| 8999 } | 9114 } |
| 9000 | 9115 |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 9084 const TypeParameter& other_type_param = TypeParameter::Cast(other); | 9199 const TypeParameter& other_type_param = TypeParameter::Cast(other); |
| 9085 if (IsFinalized() != other_type_param.IsFinalized()) { | 9200 if (IsFinalized() != other_type_param.IsFinalized()) { |
| 9086 return false; | 9201 return false; |
| 9087 } | 9202 } |
| 9088 if (parameterized_class() != other_type_param.parameterized_class()) { | 9203 if (parameterized_class() != other_type_param.parameterized_class()) { |
| 9089 return false; | 9204 return false; |
| 9090 } | 9205 } |
| 9091 if (index() != other_type_param.index()) { | 9206 if (index() != other_type_param.index()) { |
| 9092 return false; | 9207 return false; |
| 9093 } | 9208 } |
| 9094 const String& type_param_name = String::Handle(name()); | 9209 return true; |
|
Ivan Posva
2012/12/12 21:25:10
Thanks for simplifying this.
| |
| 9095 const String& other_type_param_name = String::Handle(other_type_param.name()); | |
| 9096 return type_param_name.Equals(other_type_param_name); | |
| 9097 } | 9210 } |
| 9098 | 9211 |
| 9099 | 9212 |
| 9100 void TypeParameter::set_parameterized_class(const Class& value) const { | 9213 void TypeParameter::set_parameterized_class(const Class& value) const { |
| 9101 // Set value may be null. | 9214 // Set value may be null. |
| 9102 StorePointer(&raw_ptr()->parameterized_class_, value.raw()); | 9215 StorePointer(&raw_ptr()->parameterized_class_, value.raw()); |
| 9103 } | 9216 } |
| 9104 | 9217 |
| 9105 | 9218 |
| 9106 void TypeParameter::set_index(intptr_t value) const { | 9219 void TypeParameter::set_index(intptr_t value) const { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 9122 RawAbstractType* TypeParameter::InstantiateFrom( | 9235 RawAbstractType* TypeParameter::InstantiateFrom( |
| 9123 const AbstractTypeArguments& instantiator_type_arguments) const { | 9236 const AbstractTypeArguments& instantiator_type_arguments) const { |
| 9124 ASSERT(IsFinalized()); | 9237 ASSERT(IsFinalized()); |
| 9125 if (instantiator_type_arguments.IsNull()) { | 9238 if (instantiator_type_arguments.IsNull()) { |
| 9126 return Type::DynamicType(); | 9239 return Type::DynamicType(); |
| 9127 } | 9240 } |
| 9128 return instantiator_type_arguments.TypeAt(index()); | 9241 return instantiator_type_arguments.TypeAt(index()); |
| 9129 } | 9242 } |
| 9130 | 9243 |
| 9131 | 9244 |
| 9245 uword TypeParameter::Hash() const { | |
| 9246 ASSERT(IsFinalized()); | |
| 9247 uword result = 0; | |
| 9248 result += Class::Handle(parameterized_class()).id(); | |
| 9249 result <<= index(); | |
| 9250 return FinalizeHash(result); | |
| 9251 } | |
| 9252 | |
| 9253 | |
| 9132 RawTypeParameter* TypeParameter::New() { | 9254 RawTypeParameter* TypeParameter::New() { |
| 9133 ASSERT(Isolate::Current()->object_store()->type_parameter_class() != | 9255 ASSERT(Isolate::Current()->object_store()->type_parameter_class() != |
| 9134 Class::null()); | 9256 Class::null()); |
| 9135 RawObject* raw = Object::Allocate(TypeParameter::kClassId, | 9257 RawObject* raw = Object::Allocate(TypeParameter::kClassId, |
| 9136 TypeParameter::InstanceSize(), | 9258 TypeParameter::InstanceSize(), |
| 9137 Heap::kOld); | 9259 Heap::kOld); |
| 9138 return reinterpret_cast<RawTypeParameter*>(raw); | 9260 return reinterpret_cast<RawTypeParameter*>(raw); |
| 9139 } | 9261 } |
| 9140 | 9262 |
| 9141 | 9263 |
| (...skipping 3157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 12299 } | 12421 } |
| 12300 return result.raw(); | 12422 return result.raw(); |
| 12301 } | 12423 } |
| 12302 | 12424 |
| 12303 | 12425 |
| 12304 const char* WeakProperty::ToCString() const { | 12426 const char* WeakProperty::ToCString() const { |
| 12305 return "_WeakProperty"; | 12427 return "_WeakProperty"; |
| 12306 } | 12428 } |
| 12307 | 12429 |
| 12308 } // namespace dart | 12430 } // namespace dart |
| OLD | NEW |