From: danno@chromium.org Date: Wed, 23 Mar 2011 09:57:12 +0000 (+0000) Subject: Remember and reuse derived map for external arrays X-Git-Tag: upstream/4.7.83~19838 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=88854cd712d80d886c368dc9a308fe5fe362bead;p=platform%2Fupstream%2Fv8.git Remember and reuse derived map for external arrays Ensure that all objects that had the same map before attaching an external array have the same map once the external array is attached. BUG=75639 TEST=fast/canvas/webgl/uninitialized-test.html Review URL: http://codereview.chromium.org/6685073 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7318 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/api.cc b/src/api.cc index 331f7e7..94162cf 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2981,14 +2981,16 @@ void PrepareExternalArrayElements(i::Handle object, // map if the element type is now changing, because assumptions about // generated code based on the receiver's map will be invalid. i::Handle elements(object->elements()); - bool force_unique_map = + bool cant_reuse_map = elements->map()->IsUndefined() || !elements->map()->has_external_array_elements() || elements->map() != HEAP->MapForExternalArrayType(array_type); - if (force_unique_map) { + if (cant_reuse_map) { i::Handle external_array_map = - FACTORY->NewExternalArrayElementsMap( - i::Handle(object->map())); + FACTORY->GetExternalArrayElementsMap( + i::Handle(object->map()), + array_type, + object->HasFastProperties()); object->set_map(*external_array_map); } object->set_elements(*array); diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 015e702..49699e9 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1936,6 +1936,7 @@ void Genesis::TransferNamedProperties(Handle from, break; } case MAP_TRANSITION: + case EXTERNAL_ARRAY_TRANSITION: case CONSTANT_TRANSITION: case NULL_DESCRIPTOR: // Ignore non-properties. diff --git a/src/factory.cc b/src/factory.cc index d798c3e..4897592 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -392,8 +392,14 @@ Handle Factory::GetSlowElementsMap(Handle src) { } -Handle Factory::NewExternalArrayElementsMap(Handle src) { - CALL_HEAP_FUNCTION(isolate(), src->NewExternalArrayElementsMap(), Map); +Handle Factory::GetExternalArrayElementsMap( + Handle src, + ExternalArrayType array_type, + bool safe_to_add_transition) { + CALL_HEAP_FUNCTION(isolate(), + src->GetExternalArrayElementsMap(array_type, + safe_to_add_transition), + Map); } diff --git a/src/factory.h b/src/factory.h index d9d5ec4..71bfdc4 100644 --- a/src/factory.h +++ b/src/factory.h @@ -192,7 +192,9 @@ class Factory { Handle GetSlowElementsMap(Handle map); - Handle NewExternalArrayElementsMap(Handle map); + Handle GetExternalArrayElementsMap(Handle map, + ExternalArrayType array_type, + bool safe_to_add_transition); Handle CopyFixedArray(Handle array); diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 08cf7d7..8e22fe1 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -1101,11 +1101,11 @@ void MarkCompactCollector::MarkDescriptorArray( ASSERT(contents->IsFixedArray()); ASSERT(contents->length() >= 2); SetMark(contents); - // Contents contains (value, details) pairs. If the details say that - // the type of descriptor is MAP_TRANSITION, CONSTANT_TRANSITION, or - // NULL_DESCRIPTOR, we don't mark the value as live. Only for - // MAP_TRANSITION and CONSTANT_TRANSITION is the value an Object* (a - // Map*). + // Contents contains (value, details) pairs. If the details say that the type + // of descriptor is MAP_TRANSITION, CONSTANT_TRANSITION, + // EXTERNAL_ARRAY_TRANSITION or NULL_DESCRIPTOR, we don't mark the value as + // live. Only for MAP_TRANSITION, EXTERNAL_ARRAY_TRANSITION and + // CONSTANT_TRANSITION is the value an Object* (a Map*). for (int i = 0; i < contents->length(); i += 2) { // If the pair (value, details) at index i, i+1 is not // a transition or null descriptor, mark the value. diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js index 416f887..99e9819 100644 --- a/src/mirror-debugger.js +++ b/src/mirror-debugger.js @@ -170,14 +170,15 @@ PropertyKind.Indexed = 2; // A copy of the PropertyType enum from global.h PropertyType = {}; -PropertyType.Normal = 0; -PropertyType.Field = 1; -PropertyType.ConstantFunction = 2; -PropertyType.Callbacks = 3; -PropertyType.Interceptor = 4; -PropertyType.MapTransition = 5; -PropertyType.ConstantTransition = 6; -PropertyType.NullDescriptor = 7; +PropertyType.Normal = 0; +PropertyType.Field = 1; +PropertyType.ConstantFunction = 2; +PropertyType.Callbacks = 3; +PropertyType.Interceptor = 4; +PropertyType.MapTransition = 5; +PropertyType.ExternalArrayTransition = 6; +PropertyType.ConstantTransition = 7; +PropertyType.NullDescriptor = 8; // Different attributes for a property. diff --git a/src/objects-inl.h b/src/objects-inl.h index 922c054..0ea7ac0 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -1722,7 +1722,8 @@ bool DescriptorArray::IsProperty(int descriptor_number) { bool DescriptorArray::IsTransition(int descriptor_number) { PropertyType t = GetType(descriptor_number); - return t == MAP_TRANSITION || t == CONSTANT_TRANSITION; + return t == MAP_TRANSITION || t == CONSTANT_TRANSITION || + t == EXTERNAL_ARRAY_TRANSITION; } @@ -2871,21 +2872,6 @@ MaybeObject* Map::GetSlowElementsMap() { } -MaybeObject* Map::NewExternalArrayElementsMap() { - // TODO(danno): Special case empty object map (or most common case) - // to return a pre-canned pixel array map. - Object* obj; - { MaybeObject* maybe_obj = CopyDropTransitions(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - Map* new_map = Map::cast(obj); - new_map->set_has_fast_elements(false); - new_map->set_has_external_array_elements(true); - isolate()->counters()->map_to_external_array_elements()->Increment(); - return new_map; -} - - ACCESSORS(Map, instance_descriptors, DescriptorArray, kInstanceDescriptorsOffset) ACCESSORS(Map, code_cache, Object, kCodeCacheOffset) diff --git a/src/objects.cc b/src/objects.cc index a2b35ef..ac5b0d4 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1282,12 +1282,21 @@ MaybeObject* JSObject::AddFastProperty(String* name, } } - // Only allow map transition if the object's map is NOT equal to the - // global object_function's map and there is not a transition for name. + // Only allow map transition if the object isn't the global object and there + // is not a transition for the name, or there's a transition for the name but + // it's unrelated to properties. + int descriptor_index = old_descriptors->Search(name); + + // External array transitions are stored in the descriptor for property "", + // which is not a identifier and should have forced a switch to slow + // properties above. + ASSERT(descriptor_index == DescriptorArray::kNotFound || + old_descriptors->GetType(descriptor_index) != EXTERNAL_ARRAY_TRANSITION); + bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound || + old_descriptors->GetType(descriptor_index) == EXTERNAL_ARRAY_TRANSITION; bool allow_map_transition = - !old_descriptors->Contains(name) && - (isolate->context()->global_context()->object_function()-> - map() != map()); + can_insert_transition && + (isolate->context()->global_context()->object_function()->map() != map()); ASSERT(index < map()->inobject_properties() || (index - map()->inobject_properties()) < properties()->length() || @@ -1819,6 +1828,77 @@ void Map::LookupInDescriptors(JSObject* holder, } +MaybeObject* Map::GetExternalArrayElementsMap(ExternalArrayType array_type, + bool safe_to_add_transition) { + DescriptorArray* descriptors = instance_descriptors(); + String* external_array_sentinel_name = GetIsolate()->heap()->empty_symbol(); + + if (safe_to_add_transition) { + // It's only safe to manipulate the descriptor array if it would be + // safe to add a transition. + + ASSERT(!is_shared()); // no transitions can be added to shared maps. + // Check if the external array transition already exists. + DescriptorLookupCache* cache = heap()->isolate()->descriptor_lookup_cache(); + int index = cache->Lookup(descriptors, external_array_sentinel_name); + if (index == DescriptorLookupCache::kAbsent) { + index = descriptors->Search(external_array_sentinel_name); + cache->Update(descriptors, + external_array_sentinel_name, + index); + } + + // If the transition already exists, check the type. If there is a match, + // return it. + if (index != DescriptorArray::kNotFound) { + PropertyDetails details(PropertyDetails(descriptors->GetDetails(index))); + if (details.type() == EXTERNAL_ARRAY_TRANSITION && + details.array_type() == array_type) { + return descriptors->GetValue(index); + } else { + safe_to_add_transition = false; + } + } + } + + // No transition to an existing external array map. Make a new one. + Object* obj; + { MaybeObject* maybe_map = CopyDropTransitions(); + if (!maybe_map->ToObject(&obj)) return maybe_map; + } + Map* new_map = Map::cast(obj); + + new_map->set_has_fast_elements(false); + new_map->set_has_external_array_elements(true); + GetIsolate()->counters()->map_to_external_array_elements()->Increment(); + + // Only remember the map transition if the object's map is NOT equal to the + // global object_function's map and there is not an already existing + // non-matching external array transition. + bool allow_map_transition = + safe_to_add_transition && + (GetIsolate()->context()->global_context()->object_function()->map() != + map()); + if (allow_map_transition) { + // Allocate new instance descriptors for the old map with map transition. + ExternalArrayTransitionDescriptor desc(external_array_sentinel_name, + Map::cast(new_map), + array_type); + Object* new_descriptors; + MaybeObject* maybe_new_descriptors = descriptors->CopyInsert( + &desc, + KEEP_TRANSITIONS); + if (!maybe_new_descriptors->ToObject(&new_descriptors)) { + return maybe_new_descriptors; + } + descriptors = DescriptorArray::cast(new_descriptors); + set_instance_descriptors(descriptors); + } + + return new_map; +} + + void JSObject::LocalLookupRealNamedProperty(String* name, LookupResult* result) { if (IsJSGlobalProxy()) { @@ -2051,6 +2131,7 @@ MaybeObject* JSObject::SetProperty(LookupResult* result, return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); } case NULL_DESCRIPTOR: + case EXTERNAL_ARRAY_TRANSITION: return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); default: UNREACHABLE(); @@ -2130,6 +2211,7 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( // if the value is a function. return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); case NULL_DESCRIPTOR: + case EXTERNAL_ARRAY_TRANSITION: return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); default: UNREACHABLE(); @@ -5484,6 +5566,7 @@ void Map::CreateBackPointers() { DescriptorArray* descriptors = instance_descriptors(); for (int i = 0; i < descriptors->number_of_descriptors(); i++) { if (descriptors->GetType(i) == MAP_TRANSITION || + descriptors->GetType(i) == EXTERNAL_ARRAY_TRANSITION || descriptors->GetType(i) == CONSTANT_TRANSITION) { // Get target. Map* target = Map::cast(descriptors->GetValue(i)); @@ -5526,6 +5609,7 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { // non-live object. PropertyDetails details(Smi::cast(contents->get(i + 1))); if (details.type() == MAP_TRANSITION || + details.type() == EXTERNAL_ARRAY_TRANSITION || details.type() == CONSTANT_TRANSITION) { Map* target = reinterpret_cast(contents->get(i)); ASSERT(target->IsHeapObject()); @@ -6399,6 +6483,7 @@ const char* Code::PropertyType2String(PropertyType type) { case CALLBACKS: return "CALLBACKS"; case INTERCEPTOR: return "INTERCEPTOR"; case MAP_TRANSITION: return "MAP_TRANSITION"; + case EXTERNAL_ARRAY_TRANSITION: return "EXTERNAL_ARRAY_TRANSITION"; case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION"; case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR"; } diff --git a/src/objects.h b/src/objects.h index e0ea064..2039141 100644 --- a/src/objects.h +++ b/src/objects.h @@ -135,19 +135,37 @@ class PropertyDetails BASE_EMBEDDED { PropertyDetails(PropertyAttributes attributes, PropertyType type, int index = 0) { + ASSERT(type != EXTERNAL_ARRAY_TRANSITION); ASSERT(TypeField::is_valid(type)); ASSERT(AttributesField::is_valid(attributes)); - ASSERT(IndexField::is_valid(index)); + ASSERT(StorageField::is_valid(index)); value_ = TypeField::encode(type) | AttributesField::encode(attributes) - | IndexField::encode(index); + | StorageField::encode(index); ASSERT(type == this->type()); ASSERT(attributes == this->attributes()); ASSERT(index == this->index()); } + PropertyDetails(PropertyAttributes attributes, + PropertyType type, + ExternalArrayType array_type) { + ASSERT(type == EXTERNAL_ARRAY_TRANSITION); + ASSERT(TypeField::is_valid(type)); + ASSERT(AttributesField::is_valid(attributes)); + ASSERT(StorageField::is_valid(static_cast(array_type))); + + value_ = TypeField::encode(type) + | AttributesField::encode(attributes) + | StorageField::encode(static_cast(array_type)); + + ASSERT(type == this->type()); + ASSERT(attributes == this->attributes()); + ASSERT(array_type == this->array_type()); + } + // Conversion for storing details as Object*. inline PropertyDetails(Smi* smi); inline Smi* AsSmi(); @@ -157,7 +175,8 @@ class PropertyDetails BASE_EMBEDDED { bool IsTransition() { PropertyType t = type(); ASSERT(t != INTERCEPTOR); - return t == MAP_TRANSITION || t == CONSTANT_TRANSITION; + return t == MAP_TRANSITION || t == CONSTANT_TRANSITION || + t == EXTERNAL_ARRAY_TRANSITION; } bool IsProperty() { @@ -166,11 +185,18 @@ class PropertyDetails BASE_EMBEDDED { PropertyAttributes attributes() { return AttributesField::decode(value_); } - int index() { return IndexField::decode(value_); } + int index() { return StorageField::decode(value_); } + + ExternalArrayType array_type() { + ASSERT(type() == EXTERNAL_ARRAY_TRANSITION); + return static_cast(StorageField::decode(value_)); + } inline PropertyDetails AsDeleted(); - static bool IsValidIndex(int index) { return IndexField::is_valid(index); } + static bool IsValidIndex(int index) { + return StorageField::is_valid(index); + } bool IsReadOnly() { return (attributes() & READ_ONLY) != 0; } bool IsDontDelete() { return (attributes() & DONT_DELETE) != 0; } @@ -179,10 +205,10 @@ class PropertyDetails BASE_EMBEDDED { // Bit fields in value_ (type, shift, size). Must be public so the // constants can be embedded in generated code. - class TypeField: public BitField {}; - class AttributesField: public BitField {}; - class DeletedField: public BitField {}; - class IndexField: public BitField {}; + class TypeField: public BitField {}; + class AttributesField: public BitField {}; + class DeletedField: public BitField {}; + class StorageField: public BitField {}; static const int kInitialIndex = 1; private: @@ -3502,18 +3528,18 @@ class Code: public HeapObject { static const int kFlagsICStateShift = 0; static const int kFlagsICInLoopShift = 3; static const int kFlagsTypeShift = 4; - static const int kFlagsKindShift = 7; - static const int kFlagsICHolderShift = 11; - static const int kFlagsExtraICStateShift = 12; - static const int kFlagsArgumentsCountShift = 14; + static const int kFlagsKindShift = 8; + static const int kFlagsICHolderShift = 12; + static const int kFlagsExtraICStateShift = 13; + static const int kFlagsArgumentsCountShift = 15; static const int kFlagsICStateMask = 0x00000007; // 00000000111 static const int kFlagsICInLoopMask = 0x00000008; // 00000001000 - static const int kFlagsTypeMask = 0x00000070; // 00001110000 - static const int kFlagsKindMask = 0x00000780; // 11110000000 - static const int kFlagsCacheInPrototypeMapMask = 0x00000800; - static const int kFlagsExtraICStateMask = 0x00003000; - static const int kFlagsArgumentsCountMask = 0xFFFFC000; + static const int kFlagsTypeMask = 0x000000F0; // 00001110000 + static const int kFlagsKindMask = 0x00000F00; // 11110000000 + static const int kFlagsCacheInPrototypeMapMask = 0x00001000; + static const int kFlagsExtraICStateMask = 0x00006000; + static const int kFlagsArgumentsCountMask = 0xFFFF8000; static const int kFlagsNotUsedInLookup = (kFlagsICInLoopMask | kFlagsTypeMask | kFlagsCacheInPrototypeMapMask); @@ -3715,7 +3741,9 @@ class Map: public HeapObject { // Returns a new map with all transitions dropped from the descriptors and the // external array elements bit set. - MUST_USE_RESULT inline MaybeObject* NewExternalArrayElementsMap(); + MUST_USE_RESULT MaybeObject* GetExternalArrayElementsMap( + ExternalArrayType array_type, + bool safe_to_add_transition); // Returns the property index for name (only valid for FAST MODE). int PropertyIndexFor(String* name); diff --git a/src/property.cc b/src/property.cc index 9677433..c35fb83 100644 --- a/src/property.cc +++ b/src/property.cc @@ -52,6 +52,12 @@ void LookupResult::Print(FILE* out) { GetTransitionMap()->Print(out); PrintF(out, "\n"); break; + case EXTERNAL_ARRAY_TRANSITION: + PrintF(out, " -type = external array transition\n"); + PrintF(out, " -map:\n"); + GetTransitionMap()->Print(out); + PrintF(out, "\n"); + break; case CONSTANT_FUNCTION: PrintF(out, " -type = constant function\n"); PrintF(out, " -function:\n"); diff --git a/src/property.h b/src/property.h index 98d1c89..fa3916e 100644 --- a/src/property.h +++ b/src/property.h @@ -110,6 +110,16 @@ class MapTransitionDescriptor: public Descriptor { : Descriptor(key, map, attributes, MAP_TRANSITION) { } }; +class ExternalArrayTransitionDescriptor: public Descriptor { + public: + ExternalArrayTransitionDescriptor(String* key, + Map* map, + ExternalArrayType array_type) + : Descriptor(key, map, PropertyDetails(NONE, + EXTERNAL_ARRAY_TRANSITION, + array_type)) { } +}; + // Marks a field name in a map so that adding the field is guaranteed // to create a FIELD descriptor in the new map. Used after adding // a constant function the first time, creating a CONSTANT_FUNCTION @@ -262,7 +272,8 @@ class LookupResult BASE_EMBEDDED { Map* GetTransitionMap() { ASSERT(lookup_type_ == DESCRIPTOR_TYPE); - ASSERT(type() == MAP_TRANSITION || type() == CONSTANT_TRANSITION); + ASSERT(type() == MAP_TRANSITION || type() == CONSTANT_TRANSITION || + type() == EXTERNAL_ARRAY_TRANSITION); return Map::cast(GetValue()); } diff --git a/src/runtime.cc b/src/runtime.cc index 363d799..2ee41cb 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -9308,6 +9308,7 @@ static MaybeObject* DebugLookupResultValue(Heap* heap, } case INTERCEPTOR: case MAP_TRANSITION: + case EXTERNAL_ARRAY_TRANSITION: case CONSTANT_TRANSITION: case NULL_DESCRIPTOR: return heap->undefined_value(); diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 0e78af4..19ee997 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -649,7 +649,7 @@ MaybeObject* StubCache::ComputeKeyedLoadOrStoreExternalArray( } Object* result; { MaybeObject* maybe_result = - receiver->map()->UpdateCodeCache(name, Code::cast(code)); + receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } diff --git a/src/v8globals.h b/src/v8globals.h index 0933acf..7ff464a 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -318,14 +318,15 @@ enum InlineCacheHolderFlag { // Must fit in the BitField PropertyDetails::TypeField. // A copy of this is in mirror-debugger.js. enum PropertyType { - NORMAL = 0, // only in slow mode - FIELD = 1, // only in fast mode - CONSTANT_FUNCTION = 2, // only in fast mode - CALLBACKS = 3, - INTERCEPTOR = 4, // only in lookup results, not in descriptors. - MAP_TRANSITION = 5, // only in fast mode - CONSTANT_TRANSITION = 6, // only in fast mode - NULL_DESCRIPTOR = 7, // only in fast mode + NORMAL = 0, // only in slow mode + FIELD = 1, // only in fast mode + CONSTANT_FUNCTION = 2, // only in fast mode + CALLBACKS = 3, + INTERCEPTOR = 4, // only in lookup results, not in descriptors. + MAP_TRANSITION = 5, // only in fast mode + EXTERNAL_ARRAY_TRANSITION = 6, + CONSTANT_TRANSITION = 7, // only in fast mode + NULL_DESCRIPTOR = 8, // only in fast mode // All properties before MAP_TRANSITION are real. FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION, // There are no IC stubs for NULL_DESCRIPTORS. Therefore, diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 3f9ed6d..4125b3c 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -11598,6 +11598,95 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, free(large_array_data); } + // The "" property descriptor is overloaded to store information about + // the external array. Ensure that setting and accessing the "" property + // works (it should overwrite the information cached about the external + // array in the DescriptorArray) in various situations. + result = CompileRun("ext_array[''] = 23; ext_array['']"); + CHECK_EQ(23, result->Int32Value()); + + // Property "" set after the external array is associated with the object. + { + v8::Handle obj2 = v8::Object::New(); + obj2->Set(v8_str("ee_test_field"), v8::Int32::New(256)); + obj2->Set(v8_str(""), v8::Int32::New(1503)); + // Set the elements to be the external array. + obj2->SetIndexedPropertiesToExternalArrayData(array_data, + array_type, + kElementCount); + context->Global()->Set(v8_str("ext_array"), obj2); + result = CompileRun("ext_array['']"); + CHECK_EQ(1503, result->Int32Value()); + } + + // Property "" set after the external array is associated with the object. + { + v8::Handle obj2 = v8::Object::New(); + obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); + // Set the elements to be the external array. + obj2->SetIndexedPropertiesToExternalArrayData(array_data, + array_type, + kElementCount); + obj2->Set(v8_str(""), v8::Int32::New(1503)); + context->Global()->Set(v8_str("ext_array"), obj2); + result = CompileRun("ext_array['']"); + CHECK_EQ(1503, result->Int32Value()); + } + + // Should reuse the map from previous test. + { + v8::Handle obj2 = v8::Object::New(); + obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); + // Set the elements to be the external array. Should re-use the map + // from previous test. + obj2->SetIndexedPropertiesToExternalArrayData(array_data, + array_type, + kElementCount); + context->Global()->Set(v8_str("ext_array"), obj2); + result = CompileRun("ext_array['']"); + } + + // Property "" is a constant function that shouldn't not be interfered with + // when an external array is set. + { + v8::Handle obj2 = v8::Object::New(); + // Start + obj2->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); + + // Add a constant function to an object. + context->Global()->Set(v8_str("ext_array"), obj2); + result = CompileRun("ext_array[''] = function() {return 1503;};" + "ext_array['']();"); + + // Add an external array transition to the same map that + // has the constant transition. + v8::Handle obj3 = v8::Object::New(); + obj3->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); + obj3->SetIndexedPropertiesToExternalArrayData(array_data, + array_type, + kElementCount); + context->Global()->Set(v8_str("ext_array"), obj3); + } + + // If a external array transition is in the map, it should get clobbered + // by a constant function. + { + // Add an external array transition. + v8::Handle obj3 = v8::Object::New(); + obj3->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); + obj3->SetIndexedPropertiesToExternalArrayData(array_data, + array_type, + kElementCount); + + // Add a constant function to the same map that just got an external array + // transition. + v8::Handle obj2 = v8::Object::New(); + obj2->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); + context->Global()->Set(v8_str("ext_array"), obj2); + result = CompileRun("ext_array[''] = function() {return 1503;};" + "ext_array['']();"); + } + free(array_data); }