diff options
Diffstat (limited to 'deps/v8/src/objects.cc')
-rw-r--r-- | deps/v8/src/objects.cc | 2135 |
1 files changed, 1475 insertions, 660 deletions
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 254cd26f57..fcc2efad31 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -27,6 +27,7 @@ #include "v8.h" +#include "accessors.h" #include "api.h" #include "arguments.h" #include "bootstrapper.h" @@ -247,6 +248,18 @@ MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw, } +Handle<Object> Object::GetProperty(Handle<Object> object, Handle<String> name) { + // TODO(rossberg): The index test should not be here but in the GetProperty + // method (or somewhere else entirely). Needs more global clean-up. + uint32_t index; + if (name->AsArrayIndex(&index)) return GetElement(object, index); + Isolate* isolate = object->IsHeapObject() + ? Handle<HeapObject>::cast(object)->GetIsolate() + : Isolate::Current(); + CALL_HEAP_FUNCTION(isolate, object->GetProperty(*name), Object); +} + + Handle<Object> Object::GetElement(Handle<Object> object, uint32_t index) { Isolate* isolate = object->IsHeapObject() ? Handle<HeapObject>::cast(object)->GetIsolate() @@ -641,7 +654,8 @@ MaybeObject* Object::GetProperty(Object* receiver, ASSERT(!value->IsTheHole() || result->IsReadOnly()); return value->IsTheHole() ? heap->undefined_value() : value; case FIELD: - value = result->holder()->FastPropertyAt(result->GetFieldIndex()); + value = result->holder()->FastPropertyAt( + result->GetFieldIndex().field_index()); ASSERT(!value->IsTheHole() || result->IsReadOnly()); return value->IsTheHole() ? heap->undefined_value() : value; case CONSTANT_FUNCTION: @@ -881,14 +895,15 @@ MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) { int len = length(); Object* object; String* result; - if (IsAsciiRepresentation()) { - { MaybeObject* maybe_object = heap->AllocateRawAsciiString(len, tenure); + if (IsOneByteRepresentation()) { + { MaybeObject* maybe_object = + heap->AllocateRawOneByteString(len, tenure); if (!maybe_object->ToObject(&object)) return maybe_object; } result = String::cast(object); String* first = cs->first(); int first_length = first->length(); - char* dest = SeqAsciiString::cast(result)->GetChars(); + char* dest = SeqOneByteString::cast(result)->GetChars(); WriteToFlat(first, dest, 0, first_length); String* second = cs->second(); WriteToFlat(second, @@ -941,7 +956,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { if (size < ExternalString::kShortSize) { return false; } - bool is_ascii = this->IsAsciiRepresentation(); + bool is_ascii = this->IsOneByteRepresentation(); bool is_symbol = this->IsSymbol(); // Morph the object to an external string by adjusting the map and @@ -1118,6 +1133,10 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { } break; } + case JS_MODULE_TYPE: { + accumulator->Add("<JS Module>"); + break; + } // All other JSObjects are rather similar to each other (JSObject, // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). default: { @@ -1340,7 +1359,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size, SlicedString::BodyDescriptor::IterateBody(this, v); break; case kExternalStringTag: - if ((type & kStringEncodingMask) == kAsciiStringTag) { + if ((type & kStringEncodingMask) == kOneByteStringTag) { reinterpret_cast<ExternalAsciiString*>(this)-> ExternalAsciiStringIterateBody(v); } else { @@ -1539,8 +1558,9 @@ MaybeObject* JSObject::AddFastProperty(String* name, PropertyAttributes attributes, StoreFromKeyed store_mode) { ASSERT(!IsJSGlobalProxy()); - ASSERT(map()->instance_descriptors()->Search(name) == - DescriptorArray::kNotFound); + ASSERT(DescriptorArray::kNotFound == + map()->instance_descriptors()->Search( + name, map()->NumberOfOwnDescriptors())); // Normalize the object if the name is an actual string (not the // hidden symbols) and is not a real identifier. @@ -1676,39 +1696,88 @@ MaybeObject* JSObject::AddProperty(String* name, ASSERT(!IsJSGlobalProxy()); Map* map_of_this = map(); Heap* heap = GetHeap(); + Isolate* isolate = heap->isolate(); + MaybeObject* result; if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK && !map_of_this->is_extensible()) { if (strict_mode == kNonStrictMode) { return value; } else { Handle<Object> args[1] = {Handle<String>(name)}; - return heap->isolate()->Throw( + return isolate->Throw( *FACTORY->NewTypeError("object_not_extensible", HandleVector(args, 1))); } } + if (HasFastProperties()) { // Ensure the descriptor array does not get too big. - if (map_of_this->instance_descriptors()->number_of_descriptors() < + if (map_of_this->NumberOfOwnDescriptors() < DescriptorArray::kMaxNumberOfDescriptors) { if (value->IsJSFunction()) { - return AddConstantFunctionProperty(name, - JSFunction::cast(value), - attributes); + result = AddConstantFunctionProperty(name, + JSFunction::cast(value), + attributes); } else { - return AddFastProperty(name, value, attributes, store_mode); + result = AddFastProperty(name, value, attributes, store_mode); } } else { // Normalize the object to prevent very large instance descriptors. // This eliminates unwanted N^2 allocation and lookup behavior. Object* obj; - { MaybeObject* maybe_obj = - NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + MaybeObject* maybe = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); + if (!maybe->To(&obj)) return maybe; + result = AddSlowProperty(name, value, attributes); } + } else { + result = AddSlowProperty(name, value, attributes); } - return AddSlowProperty(name, value, attributes); + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (FLAG_harmony_observation && map()->is_observed()) { + EnqueueChangeRecord(handle(this, isolate), + "new", + handle(name, isolate), + handle(heap->the_hole_value(), isolate)); + } + + return *hresult; +} + + +void JSObject::EnqueueChangeRecord(Handle<JSObject> object, + const char* type_str, + Handle<String> name, + Handle<Object> old_value) { + Isolate* isolate = object->GetIsolate(); + HandleScope scope; + Handle<String> type = isolate->factory()->LookupAsciiSymbol(type_str); + if (object->IsJSGlobalObject()) { + object = handle(JSGlobalObject::cast(*object)->global_receiver(), isolate); + } + Handle<Object> args[] = { type, object, name, old_value }; + bool threw; + Execution::Call(Handle<JSFunction>(isolate->observers_notify_change()), + Handle<Object>(isolate->heap()->undefined_value()), + old_value->IsTheHole() ? 3 : 4, args, + &threw); + ASSERT(!threw); +} + + +void JSObject::DeliverChangeRecords(Isolate* isolate) { + ASSERT(isolate->observer_delivery_pending()); + bool threw = false; + Execution::Call( + isolate->observers_deliver_changes(), + isolate->factory()->undefined_value(), + 0, + NULL, + &threw); + ASSERT(!threw); + isolate->set_observer_delivery_pending(false); } @@ -1760,6 +1829,7 @@ MaybeObject* JSObject::ConvertTransitionToMapTransition( Object* new_value, PropertyAttributes attributes) { Map* old_map = map(); + Map* old_target = old_map->GetTransition(transition_index); Object* result; MaybeObject* maybe_result = @@ -1770,13 +1840,35 @@ MaybeObject* JSObject::ConvertTransitionToMapTransition( // This method should only be used to convert existing transitions. Objects // with the map of "new Object()" cannot have transitions in the first place. - ASSERT(map() != GetIsolate()->empty_object_map()); + Map* new_map = map(); + ASSERT(new_map != GetIsolate()->empty_object_map()); // TODO(verwaest): From here on we lose existing map transitions, causing // invalid back pointers. This will change once we can store multiple // transitions with the same key. - old_map->SetTransition(transition_index, map()); - map()->SetBackPointer(old_map); + + bool owned_descriptors = old_map->owns_descriptors(); + if (owned_descriptors || + old_target->instance_descriptors() == old_map->instance_descriptors()) { + // Since the conversion above generated a new fast map with an additional + // property which can be shared as well, install this descriptor pointer + // along the entire chain of smaller maps. + Map* map; + DescriptorArray* new_descriptors = new_map->instance_descriptors(); + DescriptorArray* old_descriptors = old_map->instance_descriptors(); + for (Object* current = old_map; + !current->IsUndefined(); + current = map->GetBackPointer()) { + map = Map::cast(current); + if (map->instance_descriptors() != old_descriptors) break; + map->SetEnumLength(Map::kInvalidEnumCache); + map->set_instance_descriptors(new_descriptors); + } + old_map->set_owns_descriptors(false); + } + + old_map->SetTransition(transition_index, new_map); + new_map->SetBackPointer(old_map); return result; } @@ -1883,7 +1975,7 @@ MaybeObject* JSReceiver::SetProperty(String* name, StrictModeFlag strict_mode, JSReceiver::StoreFromKeyed store_mode) { LookupResult result(GetIsolate()); - LocalLookup(name, &result); + LocalLookup(name, &result, true); if (!result.IsFound()) { map()->LookupTransition(JSObject::cast(this), name, &result); } @@ -2100,11 +2192,13 @@ enum RightTrimMode { FROM_GC, FROM_MUTATOR }; static void ZapEndOfFixedArray(Address new_end, int to_trim) { // If we are doing a big trim in old space then we zap the space. Object** zap = reinterpret_cast<Object**>(new_end); + zap++; // Header of filler must be at least one word so skip that. for (int i = 1; i < to_trim; i++) { *zap++ = Smi::FromInt(0); } } + template<RightTrimMode trim_mode> static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) { ASSERT(elms->map() != HEAP->fixed_cow_array_map()); @@ -2117,12 +2211,8 @@ static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) { Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim); - if (trim_mode == FROM_GC) { -#ifdef DEBUG - ZapEndOfFixedArray(new_end, to_trim); -#endif - } else { - ZapEndOfFixedArray(new_end, to_trim); + if (trim_mode != FROM_GC || Heap::ShouldZapGarbage()) { + ZapEndOfFixedArray(new_end, to_trim); } int size_delta = to_trim * kPointerSize; @@ -2145,13 +2235,31 @@ static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) { } -void Map::CopyAppendCallbackDescriptors(Handle<Map> map, - Handle<Object> descriptors) { +void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) { + Handle<DescriptorArray> descriptors(map->instance_descriptors()); + if (slack <= descriptors->NumberOfSlackDescriptors()) return; + int number_of_descriptors = descriptors->number_of_descriptors(); + Isolate* isolate = map->GetIsolate(); + Handle<DescriptorArray> new_descriptors = + isolate->factory()->NewDescriptorArray(number_of_descriptors, slack); + DescriptorArray::WhitenessWitness witness(*new_descriptors); + + for (int i = 0; i < number_of_descriptors; ++i) { + new_descriptors->CopyFrom(i, *descriptors, i, witness); + } + + map->set_instance_descriptors(*new_descriptors); +} + + +void Map::AppendCallbackDescriptors(Handle<Map> map, + Handle<Object> descriptors) { Isolate* isolate = map->GetIsolate(); Handle<DescriptorArray> array(map->instance_descriptors()); - v8::NeanderArray callbacks(descriptors); + NeanderArray callbacks(descriptors); int nof_callbacks = callbacks.length(); - int descriptor_count = array->number_of_descriptors(); + + ASSERT(array->NumberOfSlackDescriptors() >= nof_callbacks); // Ensure the keys are symbols before writing them into the instance // descriptor. Since it may cause a GC, it has to be done before we @@ -2164,23 +2272,7 @@ void Map::CopyAppendCallbackDescriptors(Handle<Map> map, entry->set_name(*key); } - Handle<DescriptorArray> result = - isolate->factory()->NewDescriptorArray(descriptor_count + nof_callbacks); - - // Ensure that marking will not progress and change color of objects. - DescriptorArray::WhitenessWitness witness(*result); - - // Copy the descriptors from the array. - if (0 < descriptor_count) { - for (int i = 0; i < descriptor_count; i++) { - result->CopyFrom(i, *array, i, witness); - } - } - - // After this point the GC is not allowed to run anymore until the map is in a - // consistent state again, i.e., all the descriptors are appended and the - // descriptor array is trimmed to the right size. - Map::SetDescriptors(map, result); + int nof = map->NumberOfOwnDescriptors(); // Fill in new callback descriptors. Process the callbacks from // back to front so that the last callback with a given name takes @@ -2189,26 +2281,14 @@ void Map::CopyAppendCallbackDescriptors(Handle<Map> map, AccessorInfo* entry = AccessorInfo::cast(callbacks.get(i)); String* key = String::cast(entry->name()); // Check if a descriptor with this name already exists before writing. - if (LinearSearch(*result, key, map->NumberOfOwnDescriptors()) == - DescriptorArray::kNotFound) { + if (array->Search(key, nof) == DescriptorArray::kNotFound) { CallbacksDescriptor desc(key, entry, entry->property_attributes()); - map->AppendDescriptor(&desc, witness); + array->Append(&desc); + nof += 1; } } - int new_number_of_descriptors = map->NumberOfOwnDescriptors(); - // Reinstall the original descriptor array if no new elements were added. - if (new_number_of_descriptors == descriptor_count) { - Map::SetDescriptors(map, array); - return; - } - - // If duplicates were detected, trim the descriptor array to the right size. - int new_array_size = DescriptorArray::LengthFor(new_number_of_descriptors); - if (new_array_size < result->length()) { - RightTrimFixedArray<FROM_MUTATOR>( - isolate->heap(), *result, result->length() - new_array_size); - } + map->SetNumberOfOwnDescriptors(nof); } @@ -2381,7 +2461,7 @@ void JSObject::LocalLookupRealNamedProperty(String* name, // occur as fields. if (result->IsField() && result->IsReadOnly() && - FastPropertyAt(result->GetFieldIndex())->IsTheHole()) { + FastPropertyAt(result->GetFieldIndex().field_index())->IsTheHole()) { result->DisallowCaching(); } return; @@ -2721,12 +2801,14 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler( - JSReceiver* receiver, + JSReceiver* receiver_raw, uint32_t index) { Isolate* isolate = GetIsolate(); HandleScope scope(isolate); + Handle<JSProxy> proxy(this); + Handle<JSReceiver> receiver(receiver_raw); Handle<String> name = isolate->factory()->Uint32ToString(index); - return GetPropertyAttributeWithHandler(receiver, *name); + return proxy->GetPropertyAttributeWithHandler(*receiver, *name); } @@ -2782,13 +2864,22 @@ MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(const char* name, } -MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, +void JSObject::AddFastPropertyUsingMap(Handle<JSObject> object, + Handle<Map> map) { + CALL_HEAP_FUNCTION_VOID( + object->GetIsolate(), + object->AddFastPropertyUsingMap(*map)); +} + + +MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup, String* name_raw, Object* value_raw, PropertyAttributes attributes, StrictModeFlag strict_mode, StoreFromKeyed store_mode) { Heap* heap = GetHeap(); + Isolate* isolate = heap->isolate(); // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc; @@ -2807,9 +2898,9 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, // Check access rights if needed. if (IsAccessCheckNeeded()) { - if (!heap->isolate()->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { + if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { return SetPropertyWithFailedAccessCheck( - result, name_raw, value_raw, true, strict_mode); + lookup, name_raw, value_raw, true, strict_mode); } } @@ -2818,66 +2909,78 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, if (proto->IsNull()) return value_raw; ASSERT(proto->IsJSGlobalObject()); return JSObject::cast(proto)->SetPropertyForResult( - result, name_raw, value_raw, attributes, strict_mode, store_mode); + lookup, name_raw, value_raw, attributes, strict_mode, store_mode); } // From this point on everything needs to be handlified, because // SetPropertyViaPrototypes might call back into JavaScript. - HandleScope scope(GetIsolate()); + HandleScope scope(isolate); Handle<JSObject> self(this); Handle<String> name(name_raw); - Handle<Object> value(value_raw); + Handle<Object> value(value_raw, isolate); - if (!result->IsProperty() && !self->IsJSContextExtensionObject()) { + if (!lookup->IsProperty() && !self->IsJSContextExtensionObject()) { bool done = false; MaybeObject* result_object = self->SetPropertyViaPrototypes( *name, *value, attributes, strict_mode, &done); if (done) return result_object; } - if (!result->IsFound()) { + if (!lookup->IsFound()) { // Neither properties nor transitions found. return self->AddProperty( *name, *value, attributes, strict_mode, store_mode); } - if (result->IsProperty() && result->IsReadOnly()) { + + if (lookup->IsProperty() && lookup->IsReadOnly()) { if (strict_mode == kStrictMode) { Handle<Object> args[] = { name, self }; - return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError( + return isolate->Throw(*isolate->factory()->NewTypeError( "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); } else { return *value; } } + Handle<Object> old_value(heap->the_hole_value(), isolate); + if (FLAG_harmony_observation && map()->is_observed()) { + old_value = handle(lookup->GetLazyValue(), isolate); + } + // This is a real property that is not read-only, or it is a // transition or null descriptor and there are no setters in the prototypes. - switch (result->type()) { + MaybeObject* result = *value; + switch (lookup->type()) { case NORMAL: - return self->SetNormalizedProperty(result, *value); + result = self->SetNormalizedProperty(lookup, *value); + break; case FIELD: - return self->FastPropertyAtPut(result->GetFieldIndex(), *value); + result = self->FastPropertyAtPut( + lookup->GetFieldIndex().field_index(), *value); + break; case CONSTANT_FUNCTION: // Only replace the function if necessary. - if (*value == result->GetConstantFunction()) return *value; + if (*value == lookup->GetConstantFunction()) return *value; // Preserve the attributes of this existing property. - attributes = result->GetAttributes(); - return self->ConvertDescriptorToField(*name, *value, attributes); + attributes = lookup->GetAttributes(); + result = self->ConvertDescriptorToField(*name, *value, attributes); + break; case CALLBACKS: { - Object* callback_object = result->GetCallbackObject(); + Object* callback_object = lookup->GetCallbackObject(); return self->SetPropertyWithCallback(callback_object, *name, *value, - result->holder(), + lookup->holder(), strict_mode); } case INTERCEPTOR: - return self->SetPropertyWithInterceptor(*name, - *value, - attributes, - strict_mode); + result = self->SetPropertyWithInterceptor(*name, + *value, + attributes, + strict_mode); + break; case TRANSITION: { - Map* transition_map = result->GetTransitionTarget(); + Map* transition_map = lookup->GetTransitionTarget(); int descriptor = transition_map->LastAdded(); DescriptorArray* descriptors = transition_map->instance_descriptors(); @@ -2886,37 +2989,55 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, if (details.type() == FIELD) { if (attributes == details.attributes()) { int field_index = descriptors->GetFieldIndex(descriptor); - return self->AddFastPropertyUsingMap(transition_map, - *name, - *value, - field_index); + result = self->AddFastPropertyUsingMap(transition_map, + *name, + *value, + field_index); + } else { + result = self->ConvertDescriptorToField(*name, *value, attributes); } - return self->ConvertDescriptorToField(*name, *value, attributes); } else if (details.type() == CALLBACKS) { - return ConvertDescriptorToField(*name, *value, attributes); - } - - ASSERT(details.type() == CONSTANT_FUNCTION); - - Object* constant_function = descriptors->GetValue(descriptor); - // If the same constant function is being added we can simply - // transition to the target map. - if (constant_function == *value) { - self->set_map(transition_map); - return constant_function; + result = self->ConvertDescriptorToField(*name, *value, attributes); + } else { + ASSERT(details.type() == CONSTANT_FUNCTION); + + Object* constant_function = descriptors->GetValue(descriptor); + if (constant_function == *value) { + // If the same constant function is being added we can simply + // transition to the target map. + self->set_map(transition_map); + result = constant_function; + } else { + // Otherwise, replace with a map transition to a new map with a FIELD, + // even if the value is a constant function. + result = self->ConvertTransitionToMapTransition( + lookup->GetTransitionIndex(), *name, *value, attributes); + } } - // Otherwise, replace with a map transition to a new map with a FIELD, - // even if the value is a constant function. - return ConvertTransitionToMapTransition( - result->GetTransitionIndex(), *name, *value, attributes); + break; } case HANDLER: case NONEXISTENT: UNREACHABLE(); - return *value; } - UNREACHABLE(); // keep the compiler happy - return *value; + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (FLAG_harmony_observation && map()->is_observed()) { + if (lookup->IsTransition()) { + EnqueueChangeRecord(self, "new", name, old_value); + } else { + LookupResult new_lookup(isolate); + self->LocalLookup(*name, &new_lookup, true); + ASSERT(!new_lookup.GetLazyValue()->IsTheHole()); + if (!new_lookup.GetLazyValue()->SameValue(*old_value)) { + EnqueueChangeRecord(self, "updated", name, old_value); + } + } + } + + return *hresult; } @@ -2942,22 +3063,22 @@ Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes( MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( - String* name, - Object* value, + String* name_raw, + Object* value_raw, PropertyAttributes attributes) { // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc; Isolate* isolate = GetIsolate(); - LookupResult result(isolate); - LocalLookup(name, &result); - if (!result.IsFound()) map()->LookupTransition(this, name, &result); + LookupResult lookup(isolate); + LocalLookup(name_raw, &lookup, true); + if (!lookup.IsFound()) map()->LookupTransition(this, name_raw, &lookup); // Check access rights if needed. if (IsAccessCheckNeeded()) { - if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { - return SetPropertyWithFailedAccessCheck(&result, - name, - value, + if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { + return SetPropertyWithFailedAccessCheck(&lookup, + name_raw, + value_raw, false, kNonStrictMode); } @@ -2965,40 +3086,69 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( if (IsJSGlobalProxy()) { Object* proto = GetPrototype(); - if (proto->IsNull()) return value; + if (proto->IsNull()) return value_raw; ASSERT(proto->IsJSGlobalObject()); return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes( - name, - value, + name_raw, + value_raw, attributes); } // Check for accessor in prototype chain removed here in clone. - if (!result.IsFound()) { + if (!lookup.IsFound()) { // Neither properties nor transitions found. - return AddProperty(name, value, attributes, kNonStrictMode); + return AddProperty(name_raw, value_raw, attributes, kNonStrictMode); + } + + // From this point on everything needs to be handlified. + HandleScope scope(isolate); + Handle<JSObject> self(this); + Handle<String> name(name_raw); + Handle<Object> value(value_raw, isolate); + + Handle<Object> old_value(isolate->heap()->the_hole_value(), isolate); + PropertyAttributes old_attributes = ABSENT; + bool is_observed = FLAG_harmony_observation && self->map()->is_observed(); + if (is_observed) { + // Function prototypes are stored specially + if (self->IsJSFunction() && + JSFunction::cast(*self)->should_have_prototype() && + name->Equals(isolate->heap()->prototype_symbol())) { + MaybeObject* maybe = Accessors::FunctionGetPrototype(*self, NULL); + if (!maybe->ToHandle(&old_value, isolate)) return maybe; + } else { + old_value = handle(lookup.GetLazyValue(), isolate); + } + old_attributes = lookup.GetAttributes(); } // Check of IsReadOnly removed from here in clone. - switch (result.type()) { + MaybeObject* result = *value; + switch (lookup.type()) { case NORMAL: { PropertyDetails details = PropertyDetails(attributes, NORMAL); - return SetNormalizedProperty(name, value, details); + result = self->SetNormalizedProperty(*name, *value, details); + break; } case FIELD: - return FastPropertyAtPut(result.GetFieldIndex(), value); + result = self->FastPropertyAtPut( + lookup.GetFieldIndex().field_index(), *value); + break; case CONSTANT_FUNCTION: // Only replace the function if necessary. - if (value == result.GetConstantFunction()) return value; - // Preserve the attributes of this existing property. - attributes = result.GetAttributes(); - return ConvertDescriptorToField(name, value, attributes); + if (*value != lookup.GetConstantFunction()) { + // Preserve the attributes of this existing property. + attributes = lookup.GetAttributes(); + result = self->ConvertDescriptorToField(*name, *value, attributes); + } + break; case CALLBACKS: case INTERCEPTOR: // Override callback in clone - return ConvertDescriptorToField(name, value, attributes); + result = self->ConvertDescriptorToField(*name, *value, attributes); + break; case TRANSITION: { - Map* transition_map = result.GetTransitionTarget(); + Map* transition_map = lookup.GetTransitionTarget(); int descriptor = transition_map->LastAdded(); DescriptorArray* descriptors = transition_map->instance_descriptors(); @@ -3007,29 +3157,48 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( if (details.type() == FIELD) { if (attributes == details.attributes()) { int field_index = descriptors->GetFieldIndex(descriptor); - return AddFastPropertyUsingMap(transition_map, - name, - value, - field_index); + result = self->AddFastPropertyUsingMap( + transition_map, *name, *value, field_index); + } else { + result = self->ConvertDescriptorToField(*name, *value, attributes); } - return ConvertDescriptorToField(name, value, attributes); } else if (details.type() == CALLBACKS) { - return ConvertDescriptorToField(name, value, attributes); - } - - ASSERT(details.type() == CONSTANT_FUNCTION); + result = self->ConvertDescriptorToField(*name, *value, attributes); + } else { + ASSERT(details.type() == CONSTANT_FUNCTION); - // Replace transition to CONSTANT FUNCTION with a map transition to a new - // map with a FIELD, even if the value is a function. - return ConvertTransitionToMapTransition( - result.GetTransitionIndex(), name, value, attributes); + // Replace transition to CONSTANT FUNCTION with a map transition to a + // new map with a FIELD, even if the value is a function. + result = self->ConvertTransitionToMapTransition( + lookup.GetTransitionIndex(), *name, *value, attributes); + } + break; } case HANDLER: case NONEXISTENT: UNREACHABLE(); } - UNREACHABLE(); // keep the compiler happy - return value; + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (is_observed) { + if (lookup.IsTransition()) { + EnqueueChangeRecord(self, "new", name, old_value); + } else { + LookupResult new_lookup(isolate); + self->LocalLookup(*name, &new_lookup, true); + ASSERT(!new_lookup.GetLazyValue()->IsTheHole()); + if (old_value->IsTheHole() || + new_lookup.GetAttributes() != old_attributes) { + EnqueueChangeRecord(self, "reconfigured", name, old_value); + } else if (!new_lookup.GetLazyValue()->SameValue(*old_value)) { + EnqueueChangeRecord(self, "updated", name, old_value); + } + } + } + + return *hresult; } @@ -3110,42 +3279,43 @@ PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver( String* key) { uint32_t index = 0; if (IsJSObject() && key->AsArrayIndex(&index)) { - return JSObject::cast(this)->HasElementWithReceiver(receiver, index) - ? NONE : ABSENT; + return JSObject::cast(this)->GetElementAttributeWithReceiver( + receiver, index, true); } // Named property. - LookupResult result(GetIsolate()); - Lookup(key, &result); - return GetPropertyAttribute(receiver, &result, key, true); + LookupResult lookup(GetIsolate()); + Lookup(key, &lookup); + return GetPropertyAttributeForResult(receiver, &lookup, key, true); } -PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver, - LookupResult* result, - String* name, - bool continue_search) { +PropertyAttributes JSReceiver::GetPropertyAttributeForResult( + JSReceiver* receiver, + LookupResult* lookup, + String* name, + bool continue_search) { // Check access rights if needed. if (IsAccessCheckNeeded()) { JSObject* this_obj = JSObject::cast(this); Heap* heap = GetHeap(); if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) { return this_obj->GetPropertyAttributeWithFailedAccessCheck( - receiver, result, name, continue_search); + receiver, lookup, name, continue_search); } } - if (result->IsFound()) { - switch (result->type()) { + if (lookup->IsFound()) { + switch (lookup->type()) { case NORMAL: // fall through case FIELD: case CONSTANT_FUNCTION: case CALLBACKS: - return result->GetAttributes(); + return lookup->GetAttributes(); case HANDLER: { - return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler( + return JSProxy::cast(lookup->proxy())->GetPropertyAttributeWithHandler( receiver, name); } case INTERCEPTOR: - return result->holder()->GetPropertyAttributeWithInterceptor( + return lookup->holder()->GetPropertyAttributeWithInterceptor( JSObject::cast(receiver), name, continue_search); case TRANSITION: case NONEXISTENT: @@ -3160,13 +3330,118 @@ PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) { // Check whether the name is an array index. uint32_t index = 0; if (IsJSObject() && name->AsArrayIndex(&index)) { - if (JSObject::cast(this)->HasLocalElement(index)) return NONE; - return ABSENT; + return GetLocalElementAttribute(index); } // Named property. - LookupResult result(GetIsolate()); - LocalLookup(name, &result); - return GetPropertyAttribute(this, &result, name, false); + LookupResult lookup(GetIsolate()); + LocalLookup(name, &lookup, true); + return GetPropertyAttributeForResult(this, &lookup, name, false); +} + + +PropertyAttributes JSObject::GetElementAttributeWithReceiver( + JSReceiver* receiver, uint32_t index, bool continue_search) { + Isolate* isolate = GetIsolate(); + + // Check access rights if needed. + if (IsAccessCheckNeeded()) { + if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { + isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS); + return ABSENT; + } + } + + if (IsJSGlobalProxy()) { + Object* proto = GetPrototype(); + if (proto->IsNull()) return ABSENT; + ASSERT(proto->IsJSGlobalObject()); + return JSObject::cast(proto)->GetElementAttributeWithReceiver( + receiver, index, continue_search); + } + + // Check for lookup interceptor except when bootstrapping. + if (HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) { + return GetElementAttributeWithInterceptor(receiver, index, continue_search); + } + + // Handle [] on String objects. + if (this->IsStringObjectWithCharacterAt(index)) { + return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); + } + + return GetElementAttributeWithoutInterceptor( + receiver, index, continue_search); +} + + +PropertyAttributes JSObject::GetElementAttributeWithInterceptor( + JSReceiver* receiver, uint32_t index, bool continue_search) { + Isolate* isolate = GetIsolate(); + // Make sure that the top context does not change when doing + // callbacks or interceptor calls. + AssertNoContextChange ncc; + HandleScope scope(isolate); + Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); + Handle<JSReceiver> hreceiver(receiver); + Handle<JSObject> holder(this); + CustomArguments args(isolate, interceptor->data(), receiver, this); + v8::AccessorInfo info(args.end()); + if (!interceptor->query()->IsUndefined()) { + v8::IndexedPropertyQuery query = + v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); + LOG(isolate, + ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); + v8::Handle<v8::Integer> result; + { + // Leaving JavaScript. + VMState state(isolate, EXTERNAL); + result = query(index, info); + } + if (!result.IsEmpty()) + return static_cast<PropertyAttributes>(result->Int32Value()); + } else if (!interceptor->getter()->IsUndefined()) { + v8::IndexedPropertyGetter getter = + v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); + LOG(isolate, + ApiIndexedPropertyAccess("interceptor-indexed-get-has", this, index)); + v8::Handle<v8::Value> result; + { + // Leaving JavaScript. + VMState state(isolate, EXTERNAL); + result = getter(index, info); + } + if (!result.IsEmpty()) return NONE; + } + + return holder->GetElementAttributeWithoutInterceptor( + *hreceiver, index, continue_search); +} + + +PropertyAttributes JSObject::GetElementAttributeWithoutInterceptor( + JSReceiver* receiver, uint32_t index, bool continue_search) { + Isolate* isolate = GetIsolate(); + HandleScope scope(isolate); + Handle<JSReceiver> hreceiver(receiver); + Handle<JSObject> holder(this); + PropertyAttributes attr = holder->GetElementsAccessor()->GetAttributes( + *hreceiver, *holder, index); + if (attr != ABSENT) return attr; + + if (holder->IsStringObjectWithCharacterAt(index)) { + return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); + } + + if (!continue_search) return ABSENT; + + Object* pt = holder->GetPrototype(); + if (pt->IsJSProxy()) { + // We need to follow the spec and simulate a call to [[GetOwnProperty]]. + return JSProxy::cast(pt)->GetElementAttributeWithHandler(*hreceiver, index); + } + if (pt->IsNull()) return ABSENT; + return JSObject::cast(pt)->GetElementAttributeWithReceiver( + *hreceiver, index, true); } @@ -3178,10 +3453,12 @@ MaybeObject* NormalizedMapCache::Get(JSObject* obj, Object* result = get(index); if (result->IsMap() && Map::cast(result)->EquivalentToForNormalization(fast, mode)) { -#ifdef DEBUG +#ifdef VERIFY_HEAP if (FLAG_verify_heap) { Map::cast(result)->SharedMapVerify(); } +#endif +#ifdef DEBUG if (FLAG_enable_slow_asserts) { // The cached map should match newly created normalized map bit-by-bit, // except for the code cache, which can contain some ics which can be @@ -3271,19 +3548,19 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, Map* map_of_this = map(); // Allocate new content. - int property_count = map_of_this->NumberOfDescribedProperties(); + int real_size = map_of_this->NumberOfOwnDescriptors(); + int property_count = real_size; if (expected_additional_properties > 0) { property_count += expected_additional_properties; } else { property_count += 2; // Make space for two more properties. } StringDictionary* dictionary; - { MaybeObject* maybe_dictionary = StringDictionary::Allocate(property_count); - if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; - } + MaybeObject* maybe_dictionary = StringDictionary::Allocate(property_count); + if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; DescriptorArray* descs = map_of_this->instance_descriptors(); - for (int i = 0; i < descs->number_of_descriptors(); i++) { + for (int i = 0; i < real_size; i++) { PropertyDetails details = descs->GetDetails(i); switch (details.type()) { case CONSTANT_FUNCTION: { @@ -3328,8 +3605,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, Heap* current_heap = GetHeap(); // Copy the next enumeration index from instance descriptor. - int index = map_of_this->instance_descriptors()->NextEnumerationIndex(); - dictionary->SetNextEnumerationIndex(index); + dictionary->SetNextEnumerationIndex(real_size + 1); Map* new_map; MaybeObject* maybe_map = @@ -3557,7 +3833,6 @@ Object* JSObject::GetHiddenProperty(String* key) { ASSERT(!IsJSGlobalProxy()); MaybeObject* hidden_lookup = GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE); - ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. Object* inline_value = hidden_lookup->ToObjectUnchecked(); if (inline_value->IsSmi()) { @@ -3598,13 +3873,11 @@ MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) { return JSObject::cast(proxy_parent)->SetHiddenProperty(key, value); } ASSERT(!IsJSGlobalProxy()); - - // If there is no backing store yet, store the identity hash inline. MaybeObject* hidden_lookup = GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE); - ASSERT(!hidden_lookup->IsFailure()); Object* inline_value = hidden_lookup->ToObjectUnchecked(); + // If there is no backing store yet, store the identity hash inline. if (value->IsSmi() && key == GetHeap()->identity_hash_symbol() && (inline_value->IsUndefined() || inline_value->IsSmi())) { @@ -3641,15 +3914,16 @@ void JSObject::DeleteHiddenProperty(String* key) { JSObject::cast(proxy_parent)->DeleteHiddenProperty(key); return; } + ASSERT(!IsJSGlobalProxy()); MaybeObject* hidden_lookup = GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE); - ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. - if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) return; + Object* inline_value = hidden_lookup->ToObjectUnchecked(); + // We never delete (inline-stored) identity hashes. - ASSERT(!hidden_lookup->ToObjectUnchecked()->IsSmi()); + ASSERT(key != GetHeap()->identity_hash_symbol()); + if (inline_value->IsUndefined() || inline_value->IsSmi()) return; - ObjectHashTable* hashtable = - ObjectHashTable::cast(hidden_lookup->ToObjectUnchecked()); + ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value); MaybeObject* delete_result = hashtable->Put(key, GetHeap()->the_hole_value()); USE(delete_result); ASSERT(!delete_result->IsFailure()); // Delete does not cause GC. @@ -3675,10 +3949,11 @@ MaybeObject* JSObject::GetHiddenPropertiesHashTable( DescriptorArray* descriptors = this->map()->instance_descriptors(); if (descriptors->number_of_descriptors() > 0) { int sorted_index = descriptors->GetSortedKeyIndex(0); - if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol()) { + if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol() && + sorted_index < map()->NumberOfOwnDescriptors()) { ASSERT(descriptors->GetType(sorted_index) == FIELD); - inline_value = this->FastPropertyAt( - descriptors->GetFieldIndex(sorted_index)); + inline_value = + this->FastPropertyAt(descriptors->GetFieldIndex(sorted_index)); } else { inline_value = GetHeap()->undefined_value(); } @@ -3743,7 +4018,8 @@ MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) { DescriptorArray* descriptors = this->map()->instance_descriptors(); if (descriptors->number_of_descriptors() > 0) { int sorted_index = descriptors->GetSortedKeyIndex(0); - if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol()) { + if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol() && + sorted_index < map()->NumberOfOwnDescriptors()) { ASSERT(descriptors->GetType(sorted_index) == FIELD); this->FastPropertyAtPut(descriptors->GetFieldIndex(sorted_index), value); @@ -3868,6 +4144,21 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { return isolate->heap()->false_value(); } + if (IsStringObjectWithCharacterAt(index)) { + if (mode == STRICT_DELETION) { + // Deleting a non-configurable property in strict mode. + HandleScope scope(isolate); + Handle<Object> holder(this); + Handle<Object> name = isolate->factory()->NewNumberFromUint(index); + Handle<Object> args[2] = { name, holder }; + Handle<Object> error = + isolate->factory()->NewTypeError("strict_delete_property", + HandleVector(args, 2)); + return isolate->Throw(*error); + } + return isolate->heap()->false_value(); + } + if (IsJSGlobalProxy()) { Object* proto = GetPrototype(); if (proto->IsNull()) return isolate->heap()->false_value(); @@ -3875,15 +4166,38 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { return JSGlobalObject::cast(proto)->DeleteElement(index, mode); } - if (HasIndexedInterceptor()) { - // Skip interceptor if forcing deletion. - if (mode != FORCE_DELETION) { - return DeleteElementWithInterceptor(index); + // From this point on everything needs to be handlified. + HandleScope scope(isolate); + Handle<JSObject> self(this); + + Handle<Object> old_value; + bool should_enqueue_change_record = false; + if (FLAG_harmony_observation && self->map()->is_observed()) { + should_enqueue_change_record = self->HasLocalElement(index); + if (should_enqueue_change_record) { + old_value = self->GetLocalElementAccessorPair(index) != NULL + ? Handle<Object>::cast(isolate->factory()->the_hole_value()) + : Object::GetElement(self, index); } - mode = JSReceiver::FORCE_DELETION; } - return GetElementsAccessor()->Delete(this, index, mode); + MaybeObject* result; + // Skip interceptor if forcing deletion. + if (self->HasIndexedInterceptor() && mode != FORCE_DELETION) { + result = self->DeleteElementWithInterceptor(index); + } else { + result = self->GetElementsAccessor()->Delete(*self, index, mode); + } + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (should_enqueue_change_record && !self->HasLocalElement(index)) { + Handle<String> name = isolate->factory()->Uint32ToString(index); + EnqueueChangeRecord(self, "deleted", name, old_value); + } + + return *hresult; } @@ -3917,38 +4231,60 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { uint32_t index = 0; if (name->AsArrayIndex(&index)) { return DeleteElement(index, mode); - } else { - LookupResult result(isolate); - LocalLookup(name, &result); - if (!result.IsFound()) return isolate->heap()->true_value(); - // Ignore attributes if forcing a deletion. - if (result.IsDontDelete() && mode != FORCE_DELETION) { - if (mode == STRICT_DELETION) { - // Deleting a non-configurable property in strict mode. - HandleScope scope(isolate); - Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) }; - return isolate->Throw(*isolate->factory()->NewTypeError( - "strict_delete_property", HandleVector(args, 2))); - } - return isolate->heap()->false_value(); - } - // Check for interceptor. - if (result.IsInterceptor()) { - // Skip interceptor if forcing a deletion. - if (mode == FORCE_DELETION) { - return DeletePropertyPostInterceptor(name, mode); - } - return DeletePropertyWithInterceptor(name); + } + + LookupResult lookup(isolate); + LocalLookup(name, &lookup, true); + if (!lookup.IsFound()) return isolate->heap()->true_value(); + // Ignore attributes if forcing a deletion. + if (lookup.IsDontDelete() && mode != FORCE_DELETION) { + if (mode == STRICT_DELETION) { + // Deleting a non-configurable property in strict mode. + HandleScope scope(isolate); + Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) }; + return isolate->Throw(*isolate->factory()->NewTypeError( + "strict_delete_property", HandleVector(args, 2))); } + return isolate->heap()->false_value(); + } + + // From this point on everything needs to be handlified. + HandleScope scope(isolate); + Handle<JSObject> self(this); + Handle<String> hname(name); + + Handle<Object> old_value(isolate->heap()->the_hole_value()); + bool is_observed = FLAG_harmony_observation && self->map()->is_observed(); + if (is_observed) { + old_value = handle(lookup.GetLazyValue(), isolate); + } + MaybeObject* result; + + // Check for interceptor. + if (lookup.IsInterceptor()) { + // Skip interceptor if forcing a deletion. + if (mode == FORCE_DELETION) { + result = self->DeletePropertyPostInterceptor(*hname, mode); + } else { + result = self->DeletePropertyWithInterceptor(*hname); + } + } else { // Normalize object if needed. Object* obj; - { MaybeObject* maybe_obj = - NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + result = self->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); + if (!result->To(&obj)) return result; // Make sure the properties are normalized before removing the entry. - return DeleteNormalizedProperty(name, mode); + result = self->DeleteNormalizedProperty(*hname, mode); } + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (is_observed && !self->HasLocalProperty(*hname)) { + EnqueueChangeRecord(self, "deleted", hname, old_value); + } + + return *hresult; } @@ -4178,21 +4514,15 @@ bool JSReceiver::IsSimpleEnum() { } -void Map::SetDescriptors(Handle<Map> map, - Handle<DescriptorArray> descriptors) { - Isolate* isolate = map->GetIsolate(); - CALL_HEAP_FUNCTION_VOID(isolate, map->SetDescriptors(*descriptors)); -} - - -int Map::NumberOfDescribedProperties(PropertyAttributes filter) { +int Map::NumberOfDescribedProperties(DescriptorFlag which, + PropertyAttributes filter) { int result = 0; DescriptorArray* descs = instance_descriptors(); - for (int i = 0; i < descs->number_of_descriptors(); i++) { - PropertyDetails details = descs->GetDetails(i); - if ((details.attributes() & filter) == 0) { - result++; - } + int limit = which == ALL_DESCRIPTORS + ? descs->number_of_descriptors() + : NumberOfOwnDescriptors(); + for (int i = 0; i < limit; i++) { + if ((descs->GetDetails(i).attributes() & filter) == 0) result++; } return result; } @@ -4200,7 +4530,8 @@ int Map::NumberOfDescribedProperties(PropertyAttributes filter) { int Map::PropertyIndexFor(String* name) { DescriptorArray* descs = instance_descriptors(); - for (int i = 0; i < descs->number_of_descriptors(); i++) { + int limit = NumberOfOwnDescriptors(); + for (int i = 0; i < limit; i++) { if (name->Equals(descs->GetKey(i))) return descs->GetFieldIndex(i); } return -1; @@ -4209,8 +4540,9 @@ int Map::PropertyIndexFor(String* name) { int Map::NextFreePropertyIndex() { int max_index = -1; + int number_of_own_descriptors = NumberOfOwnDescriptors(); DescriptorArray* descs = instance_descriptors(); - for (int i = 0; i < descs->number_of_descriptors(); i++) { + for (int i = 0; i < number_of_own_descriptors; i++) { if (descs->GetType(i) == FIELD) { int current_index = descs->GetFieldIndex(i); if (current_index > max_index) max_index = current_index; @@ -4222,8 +4554,9 @@ int Map::NextFreePropertyIndex() { AccessorDescriptor* Map::FindAccessor(String* name) { DescriptorArray* descs = instance_descriptors(); - for (int i = 0; i < descs->number_of_descriptors(); i++) { - if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) { + int number_of_own_descriptors = NumberOfOwnDescriptors(); + for (int i = 0; i < number_of_own_descriptors; i++) { + if (descs->GetType(i) == CALLBACKS && name->Equals(descs->GetKey(i))) { return descs->GetCallbacks(i); } } @@ -4231,7 +4564,8 @@ AccessorDescriptor* Map::FindAccessor(String* name) { } -void JSReceiver::LocalLookup(String* name, LookupResult* result) { +void JSReceiver::LocalLookup( + String* name, LookupResult* result, bool search_hidden_prototypes) { ASSERT(name->IsString()); Heap* heap = GetHeap(); @@ -4240,7 +4574,8 @@ void JSReceiver::LocalLookup(String* name, LookupResult* result) { Object* proto = GetPrototype(); if (proto->IsNull()) return result->NotFound(); ASSERT(proto->IsJSGlobalObject()); - return JSReceiver::cast(proto)->LocalLookup(name, result); + return JSReceiver::cast(proto)->LocalLookup( + name, result, search_hidden_prototypes); } if (IsJSProxy()) { @@ -4270,6 +4605,14 @@ void JSReceiver::LocalLookup(String* name, LookupResult* result) { } js_object->LocalLookupRealNamedProperty(name, result); + if (result->IsFound() || !search_hidden_prototypes) return; + + Object* proto = js_object->GetPrototype(); + if (!proto->IsJSReceiver()) return; + JSReceiver* receiver = JSReceiver::cast(proto); + if (receiver->map()->is_hidden_prototype()) { + receiver->LocalLookup(name, result, search_hidden_prototypes); + } } @@ -4279,7 +4622,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) { for (Object* current = this; current != heap->null_value(); current = JSObject::cast(current)->GetPrototype()) { - JSReceiver::cast(current)->LocalLookup(name, result); + JSReceiver::cast(current)->LocalLookup(name, result, false); if (result->IsFound()) return; } result->NotFound(); @@ -4420,7 +4763,9 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name, // to do a lookup, which seems to be a bit of overkill. Heap* heap = GetHeap(); bool only_attribute_changes = getter->IsNull() && setter->IsNull(); - if (HasFastProperties() && !only_attribute_changes) { + if (HasFastProperties() && !only_attribute_changes && + (map()->NumberOfOwnDescriptors() < + DescriptorArray::kMaxNumberOfDescriptors)) { MaybeObject* getterOk = heap->undefined_value(); if (!getter->IsNull()) { getterOk = DefineFastAccessor(name, ACCESSOR_GETTER, getter, attributes); @@ -4550,14 +4895,14 @@ void JSObject::DefineAccessor(Handle<JSObject> object, object->DefineAccessor(*name, *getter, *setter, attributes)); } -MaybeObject* JSObject::DefineAccessor(String* name, - Object* getter, - Object* setter, +MaybeObject* JSObject::DefineAccessor(String* name_raw, + Object* getter_raw, + Object* setter_raw, PropertyAttributes attributes) { Isolate* isolate = GetIsolate(); // Check access rights if needed. if (IsAccessCheckNeeded() && - !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { + !isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); return isolate->heap()->undefined_value(); } @@ -4567,7 +4912,7 @@ MaybeObject* JSObject::DefineAccessor(String* name, if (proto->IsNull()) return this; ASSERT(proto->IsJSGlobalObject()); return JSObject::cast(proto)->DefineAccessor( - name, getter, setter, attributes); + name_raw, getter_raw, setter_raw, attributes); } // Make sure that the top context does not change when doing callbacks or @@ -4575,14 +4920,50 @@ MaybeObject* JSObject::DefineAccessor(String* name, AssertNoContextChange ncc; // Try to flatten before operating on the string. - name->TryFlatten(); + name_raw->TryFlatten(); - if (!CanSetCallback(name)) return isolate->heap()->undefined_value(); + if (!CanSetCallback(name_raw)) return isolate->heap()->undefined_value(); + + // From this point on everything needs to be handlified. + HandleScope scope(isolate); + Handle<JSObject> self(this); + Handle<String> name(name_raw); + Handle<Object> getter(getter_raw); + Handle<Object> setter(setter_raw); uint32_t index = 0; - return name->AsArrayIndex(&index) ? - DefineElementAccessor(index, getter, setter, attributes) : - DefinePropertyAccessor(name, getter, setter, attributes); + bool is_element = name->AsArrayIndex(&index); + + Handle<Object> old_value = isolate->factory()->the_hole_value(); + bool is_observed = FLAG_harmony_observation && self->map()->is_observed(); + bool preexists = false; + if (is_observed) { + if (is_element) { + preexists = HasLocalElement(index); + if (preexists && self->GetLocalElementAccessorPair(index) == NULL) { + old_value = Object::GetElement(self, index); + } + } else { + LookupResult lookup(isolate); + LocalLookup(*name, &lookup, true); + preexists = lookup.IsProperty(); + if (preexists) old_value = handle(lookup.GetLazyValue(), isolate); + } + } + + MaybeObject* result = is_element ? + self->DefineElementAccessor(index, *getter, *setter, attributes) : + self->DefinePropertyAccessor(*name, *getter, *setter, attributes); + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (is_observed) { + const char* type = preexists ? "reconfigured" : "new"; + EnqueueChangeRecord(self, type, name, old_value); + } + + return *hresult; } @@ -4647,9 +5028,10 @@ MaybeObject* JSObject::DefineFastAccessor(String* name, if (result.IsFound()) { Map* target = result.GetTransitionTarget(); - ASSERT(target->instance_descriptors()->number_of_descriptors() == - map()->instance_descriptors()->number_of_descriptors()); - ASSERT(target->instance_descriptors()->GetKey(descriptor_number) == name); + ASSERT(target->NumberOfOwnDescriptors() == + map()->NumberOfOwnDescriptors()); + // This works since descriptors are sorted in order of addition. + ASSERT(map()->instance_descriptors()->GetKey(descriptor_number) == name); return TryAccessorTransition( this, target, descriptor_number, component, accessor, attributes); } @@ -4661,7 +5043,8 @@ MaybeObject* JSObject::DefineFastAccessor(String* name, if (result.IsFound()) { Map* target = result.GetTransitionTarget(); int descriptor_number = target->LastAdded(); - ASSERT(target->instance_descriptors()->GetKey(descriptor_number) == name); + ASSERT(target->instance_descriptors()->GetKey(descriptor_number) + ->Equals(name)); return TryAccessorTransition( this, target, descriptor_number, component, accessor, attributes); } @@ -4760,7 +5143,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { } else { // Lookup the name. LookupResult result(isolate); - LocalLookup(name, &result); + LocalLookup(name, &result, true); // ES5 forbids turning a property into an accessor if it's not // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5). if (result.IsFound() && (result.IsReadOnly() || result.IsDontDelete())) { @@ -4832,8 +5215,9 @@ Object* JSObject::LookupAccessor(String* name, AccessorComponent component) { Object* JSObject::SlowReverseLookup(Object* value) { if (HasFastProperties()) { + int number_of_own_descriptors = map()->NumberOfOwnDescriptors(); DescriptorArray* descs = map()->instance_descriptors(); - for (int i = 0; i < descs->number_of_descriptors(); i++) { + for (int i = 0; i < number_of_own_descriptors; i++) { if (descs->GetType(i) == FIELD) { if (FastPropertyAt(descs->GetFieldIndex(i)) == value) { return descs->GetKey(i); @@ -4861,9 +5245,11 @@ MaybeObject* Map::RawCopy(int instance_size) { result->set_constructor(constructor()); result->set_bit_field(bit_field()); result->set_bit_field2(bit_field2()); - result->set_bit_field3(bit_field3()); - result->SetNumberOfOwnDescriptors(0); - result->SetEnumLength(kInvalidEnumCache); + int new_bit_field3 = bit_field3(); + new_bit_field3 = OwnsDescriptors::update(new_bit_field3, true); + new_bit_field3 = NumberOfOwnDescriptorsBits::update(new_bit_field3, 0); + new_bit_field3 = EnumLengthBits::update(new_bit_field3, kInvalidEnumCache); + result->set_bit_field3(new_bit_field3); return result; } @@ -4887,7 +5273,7 @@ MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode, result->set_is_shared(sharing == SHARED_NORMALIZED_MAP); result->set_dictionary_map(true); -#ifdef DEBUG +#ifdef VERIFY_HEAP if (FLAG_verify_heap && result->is_shared()) { result->SharedMapVerify(); } @@ -4913,22 +5299,100 @@ MaybeObject* Map::CopyDropDescriptors() { } +MaybeObject* Map::ShareDescriptor(DescriptorArray* descriptors, + Descriptor* descriptor) { + // Sanity check. This path is only to be taken if the map owns its descriptor + // array, implying that its NumberOfOwnDescriptors equals the number of + // descriptors in the descriptor array. + ASSERT(NumberOfOwnDescriptors() == + instance_descriptors()->number_of_descriptors()); + Map* result; + MaybeObject* maybe_result = CopyDropDescriptors(); + if (!maybe_result->To(&result)) return maybe_result; + + String* name = descriptor->GetKey(); + + TransitionArray* transitions; + MaybeObject* maybe_transitions = + AddTransition(name, result, SIMPLE_TRANSITION); + if (!maybe_transitions->To(&transitions)) return maybe_transitions; + + int old_size = descriptors->number_of_descriptors(); + + DescriptorArray* new_descriptors; + + if (descriptors->NumberOfSlackDescriptors() > 0) { + new_descriptors = descriptors; + new_descriptors->Append(descriptor); + } else { + // Descriptor arrays grow by 50%. + MaybeObject* maybe_descriptors = DescriptorArray::Allocate( + old_size, old_size < 4 ? 1 : old_size / 2); + if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; + + DescriptorArray::WhitenessWitness witness(new_descriptors); + + // Copy the descriptors, inserting a descriptor. + for (int i = 0; i < old_size; ++i) { + new_descriptors->CopyFrom(i, descriptors, i, witness); + } + + new_descriptors->Append(descriptor, witness); + + if (old_size > 0) { + // If the source descriptors had an enum cache we copy it. This ensures + // that the maps to which we push the new descriptor array back can rely + // on a cache always being available once it is set. If the map has more + // enumerated descriptors than available in the original cache, the cache + // will be lazily replaced by the extended cache when needed. + if (descriptors->HasEnumCache()) { + new_descriptors->CopyEnumCacheFrom(descriptors); + } + + Map* map; + // Replace descriptors by new_descriptors in all maps that share it. + for (Object* current = GetBackPointer(); + !current->IsUndefined(); + current = map->GetBackPointer()) { + map = Map::cast(current); + if (map->instance_descriptors() != descriptors) break; + map->set_instance_descriptors(new_descriptors); + } + + set_instance_descriptors(new_descriptors); + } + } + + result->SetBackPointer(this); + result->InitializeDescriptors(new_descriptors); + ASSERT(result->NumberOfOwnDescriptors() == NumberOfOwnDescriptors() + 1); + + set_transitions(transitions); + set_owns_descriptors(false); + + return result; +} + + MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, String* name, - TransitionFlag flag) { + TransitionFlag flag, + int descriptor_index) { + ASSERT(descriptors->IsSortedNoDuplicates()); + Map* result; MaybeObject* maybe_result = CopyDropDescriptors(); if (!maybe_result->To(&result)) return maybe_result; - if (descriptors->number_of_descriptors() != 0) { - MaybeObject* maybe_failure = result->SetDescriptors(descriptors); - if (maybe_failure->IsFailure()) return maybe_failure; - result->SetNumberOfOwnDescriptors(descriptors->number_of_descriptors()); - } + result->InitializeDescriptors(descriptors); if (flag == INSERT_TRANSITION && CanHaveMoreTransitions()) { TransitionArray* transitions; - MaybeObject* maybe_transitions = AddTransition(name, result); + SimpleTransitionFlag simple_flag = + (descriptor_index == descriptors->number_of_descriptors() - 1) + ? SIMPLE_TRANSITION + : FULL_TRANSITION; + MaybeObject* maybe_transitions = AddTransition(name, result, simple_flag); if (!maybe_transitions->To(&transitions)) return maybe_transitions; set_transitions(transitions); @@ -4940,12 +5404,6 @@ MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) { - // Create a new free-floating map only if we are not allowed to store it. - Map* new_map = NULL; - MaybeObject* maybe_new_map = Copy(); - if (!maybe_new_map->To(&new_map)) return maybe_new_map; - new_map->set_elements_kind(kind); - if (flag == INSERT_TRANSITION) { ASSERT(!HasElementsTransition() || ((elements_transition_map()->elements_kind() == DICTIONARY_ELEMENTS || @@ -4956,10 +5414,40 @@ MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) { ASSERT(!IsFastElementsKind(kind) || IsMoreGeneralElementsKindTransition(elements_kind(), kind)); ASSERT(kind != elements_kind()); + } + + bool insert_transition = + flag == INSERT_TRANSITION && !HasElementsTransition(); + + if (insert_transition && owns_descriptors()) { + // In case the map owned its own descriptors, share the descriptors and + // transfer ownership to the new map. + Map* new_map; + MaybeObject* maybe_new_map = CopyDropDescriptors(); + if (!maybe_new_map->To(&new_map)) return maybe_new_map; MaybeObject* added_elements = set_elements_transition_map(new_map); if (added_elements->IsFailure()) return added_elements; + new_map->set_elements_kind(kind); + new_map->InitializeDescriptors(instance_descriptors()); + new_map->SetBackPointer(this); + set_owns_descriptors(false); + return new_map; + } + + // In case the map did not own its own descriptors, a split is forced by + // copying the map; creating a new descriptor array cell. + // Create a new free-floating map only if we are not allowed to store it. + Map* new_map; + MaybeObject* maybe_new_map = Copy(); + if (!maybe_new_map->To(&new_map)) return maybe_new_map; + + new_map->set_elements_kind(kind); + + if (insert_transition) { + MaybeObject* added_elements = set_elements_transition_map(new_map); + if (added_elements->IsFailure()) return added_elements; new_map->SetBackPointer(this); } @@ -4977,13 +5465,25 @@ MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() { Map* map = ctor->initial_map(); DescriptorArray* descriptors = map->instance_descriptors(); - return CopyReplaceDescriptors(descriptors, NULL, OMIT_TRANSITION); + int number_of_own_descriptors = map->NumberOfOwnDescriptors(); + DescriptorArray* new_descriptors; + MaybeObject* maybe_descriptors = + descriptors->CopyUpTo(number_of_own_descriptors); + if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; + + return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0); } MaybeObject* Map::Copy() { DescriptorArray* descriptors = instance_descriptors(); - return CopyReplaceDescriptors(descriptors, NULL, OMIT_TRANSITION); + DescriptorArray* new_descriptors; + int number_of_own_descriptors = NumberOfOwnDescriptors(); + MaybeObject* maybe_descriptors = + descriptors->CopyUpTo(number_of_own_descriptors); + if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; + + return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0); } @@ -4995,28 +5495,39 @@ MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor, MaybeObject* maybe_failure = descriptor->KeyToSymbol(); if (maybe_failure->IsFailure()) return maybe_failure; - String* key = descriptor->GetKey(); - ASSERT(descriptors->Search(key) == DescriptorArray::kNotFound); - - int old_size = descriptors->number_of_descriptors(); + int old_size = NumberOfOwnDescriptors(); int new_size = old_size + 1; + descriptor->SetEnumerationIndex(new_size); + + if (flag == INSERT_TRANSITION && + owns_descriptors() && + CanHaveMoreTransitions()) { + return ShareDescriptor(descriptors, descriptor); + } DescriptorArray* new_descriptors; - MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size); + MaybeObject* maybe_descriptors = DescriptorArray::Allocate(old_size, 1); if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; - FixedArray::WhitenessWitness witness(new_descriptors); + DescriptorArray::WhitenessWitness witness(new_descriptors); // Copy the descriptors, inserting a descriptor. for (int i = 0; i < old_size; ++i) { new_descriptors->CopyFrom(i, descriptors, i, witness); } - new_descriptors->Append(descriptor, witness, old_size); + if (old_size != descriptors->number_of_descriptors()) { + new_descriptors->SetNumberOfDescriptors(new_size); + new_descriptors->Set(old_size, descriptor, witness); + new_descriptors->Sort(); + } else { + new_descriptors->Append(descriptor, witness); + } - SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates()); + String* key = descriptor->GetKey(); + int insertion_index = new_descriptors->number_of_descriptors() - 1; - return CopyReplaceDescriptors(new_descriptors, key, flag); + return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index); } @@ -5029,21 +5540,38 @@ MaybeObject* Map::CopyInsertDescriptor(Descriptor* descriptor, if (maybe_result->IsFailure()) return maybe_result; // We replace the key if it is already present. - int index = old_descriptors->SearchWithCache(descriptor->GetKey()); + int index = old_descriptors->SearchWithCache(descriptor->GetKey(), this); if (index != DescriptorArray::kNotFound) { - return CopyReplaceDescriptor(descriptor, index, flag); + return CopyReplaceDescriptor(old_descriptors, descriptor, index, flag); } return CopyAddDescriptor(descriptor, flag); } -MaybeObject* Map::CopyReplaceDescriptor(Descriptor* descriptor, +MaybeObject* DescriptorArray::CopyUpTo(int enumeration_index) { + if (enumeration_index == 0) return GetHeap()->empty_descriptor_array(); + + int size = enumeration_index; + + DescriptorArray* descriptors; + MaybeObject* maybe_descriptors = Allocate(size); + if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors; + DescriptorArray::WhitenessWitness witness(descriptors); + + for (int i = 0; i < size; ++i) { + descriptors->CopyFrom(i, this, i, witness); + } + + if (number_of_descriptors() != enumeration_index) descriptors->Sort(); + + return descriptors; +} + + +MaybeObject* Map::CopyReplaceDescriptor(DescriptorArray* descriptors, + Descriptor* descriptor, int insertion_index, TransitionFlag flag) { - DescriptorArray* descriptors = instance_descriptors(); - int size = descriptors->number_of_descriptors(); - ASSERT(0 <= insertion_index && insertion_index < size); - // Ensure the key is a symbol. MaybeObject* maybe_failure = descriptor->KeyToSymbol(); if (maybe_failure->IsFailure()) return maybe_failure; @@ -5051,27 +5579,30 @@ MaybeObject* Map::CopyReplaceDescriptor(Descriptor* descriptor, String* key = descriptor->GetKey(); ASSERT(key == descriptors->GetKey(insertion_index)); + int new_size = NumberOfOwnDescriptors(); + ASSERT(0 <= insertion_index && insertion_index < new_size); + + PropertyDetails details = descriptors->GetDetails(insertion_index); + ASSERT_LE(details.descriptor_index(), new_size); + descriptor->SetEnumerationIndex(details.descriptor_index()); + DescriptorArray* new_descriptors; - MaybeObject* maybe_descriptors = DescriptorArray::Allocate(size); + MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size); if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; + DescriptorArray::WhitenessWitness witness(new_descriptors); - FixedArray::WhitenessWitness witness(new_descriptors); - - // Copy the descriptors, replacing a descriptor. - for (int index = 0; index < size; ++index) { - if (index == insertion_index) continue; - new_descriptors->CopyFrom(index, descriptors, index, witness); + for (int i = 0; i < new_size; ++i) { + if (i == insertion_index) { + new_descriptors->Set(i, descriptor, witness); + } else { + new_descriptors->CopyFrom(i, descriptors, i, witness); + } } - PropertyDetails original_details = descriptors->GetDetails(insertion_index); - descriptor->SetEnumerationIndex(original_details.descriptor_index()); - descriptor->SetSortedKey(original_details.pointer()); - - new_descriptors->Set(insertion_index, descriptor, witness); - - SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates()); + // Re-sort if descriptors were removed. + if (new_size != descriptors->length()) new_descriptors->Sort(); - return CopyReplaceDescriptors(new_descriptors, key, flag); + return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index); } @@ -5846,39 +6377,39 @@ bool FixedArray::IsEqualTo(FixedArray* other) { #endif -MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) { +MaybeObject* DescriptorArray::Allocate(int number_of_descriptors, int slack) { Heap* heap = Isolate::Current()->heap(); // Do not use DescriptorArray::cast on incomplete object. + int size = number_of_descriptors + slack; + if (size == 0) return heap->empty_descriptor_array(); FixedArray* result; - if (number_of_descriptors == 0) return heap->empty_descriptor_array(); // Allocate the array of keys. - MaybeObject* maybe_array = - heap->AllocateFixedArray(LengthFor(number_of_descriptors)); + MaybeObject* maybe_array = heap->AllocateFixedArray(LengthFor(size)); if (!maybe_array->To(&result)) return maybe_array; + result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors)); result->set(kEnumCacheIndex, Smi::FromInt(0)); return result; } +void DescriptorArray::ClearEnumCache() { + set(kEnumCacheIndex, Smi::FromInt(0)); +} + + void DescriptorArray::SetEnumCache(FixedArray* bridge_storage, FixedArray* new_cache, Object* new_index_cache) { ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength); ASSERT(new_index_cache->IsSmi() || new_index_cache->IsFixedArray()); - if (HasEnumCache()) { - FixedArray::cast(get(kEnumCacheIndex))-> - set(kEnumCacheBridgeCacheIndex, new_cache); - FixedArray::cast(get(kEnumCacheIndex))-> - set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache); - } else { - if (IsEmpty()) return; // Do nothing for empty descriptor array. - FixedArray::cast(bridge_storage)-> - set(kEnumCacheBridgeCacheIndex, new_cache); - FixedArray::cast(bridge_storage)-> - set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache); - set(kEnumCacheIndex, bridge_storage); - } + ASSERT(!IsEmpty()); + ASSERT(!HasEnumCache() || new_cache->length() > GetEnumCache()->length()); + FixedArray::cast(bridge_storage)-> + set(kEnumCacheBridgeCacheIndex, new_cache); + FixedArray::cast(bridge_storage)-> + set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache); + set(kEnumCacheIndex, bridge_storage); } @@ -5948,6 +6479,7 @@ void DescriptorArray::Sort() { parent_index = child_index; } } + ASSERT(IsSortedNoDuplicates()); } @@ -6025,10 +6557,10 @@ String::FlatContent String::GetFlatContent() { ASSERT(shape.representation_tag() != kConsStringTag && shape.representation_tag() != kSlicedStringTag); } - if (shape.encoding_tag() == kAsciiStringTag) { + if (shape.encoding_tag() == kOneByteStringTag) { const char* start; if (shape.representation_tag() == kSeqStringTag) { - start = SeqAsciiString::cast(string)->GetChars(); + start = SeqOneByteString::cast(string)->GetChars(); } else { start = ExternalAsciiString::cast(string)->GetChars(); } @@ -6111,7 +6643,7 @@ const uc16* String::GetTwoByteData() { const uc16* String::GetTwoByteData(unsigned start) { - ASSERT(!IsAsciiRepresentationUnderneath()); + ASSERT(!IsOneByteRepresentationUnderneath()); switch (StringShape(this).representation_tag()) { case kSeqStringTag: return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start); @@ -6192,7 +6724,7 @@ void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, } -const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock( +const unibrow::byte* SeqOneByteString::SeqOneByteStringReadBlock( unsigned* remaining, unsigned* offset_ptr, unsigned max_chars) { @@ -6320,7 +6852,7 @@ void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer( } -void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, +void SeqOneByteString::SeqOneByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, unsigned* offset_ptr, unsigned max_chars) { unsigned capacity = rbb->capacity - rbb->cursor; @@ -6363,9 +6895,9 @@ const unibrow::byte* String::ReadBlock(String* input, } switch (StringShape(input).representation_tag()) { case kSeqStringTag: - if (input->IsAsciiRepresentation()) { - SeqAsciiString* str = SeqAsciiString::cast(input); - return str->SeqAsciiStringReadBlock(&rbb->remaining, + if (input->IsOneByteRepresentation()) { + SeqOneByteString* str = SeqOneByteString::cast(input); + return str->SeqOneByteStringReadBlock(&rbb->remaining, offset_ptr, max_chars); } else { @@ -6380,7 +6912,7 @@ const unibrow::byte* String::ReadBlock(String* input, offset_ptr, max_chars); case kExternalStringTag: - if (input->IsAsciiRepresentation()) { + if (input->IsOneByteRepresentation()) { return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock( &rbb->remaining, offset_ptr, @@ -6494,8 +7026,128 @@ void StringInputBuffer::Seek(unsigned pos) { } -void SafeStringInputBuffer::Seek(unsigned pos) { - Reset(pos, input_); +String* ConsStringIteratorOp::Operate(ConsString* consString, + unsigned* outerOffset, int32_t* typeOut, unsigned* lengthOut) { + ASSERT(*lengthOut == (unsigned)consString->length()); + // Push the root string. + PushLeft(consString); + root_ = consString; + root_type_ = *typeOut; + root_length_ = *lengthOut; + unsigned targetOffset = *outerOffset; + unsigned offset = 0; + while (true) { + // Loop until the string is found which contains the target offset. + String* string = consString->first(); + unsigned length = string->length(); + int32_t type; + if (targetOffset < offset + length) { + // Target offset is in the left branch. + // Mark the descent. + ClearRightDescent(); + // Keep going if we're still in a ConString. + type = string->map()->instance_type(); + if ((type & kStringRepresentationMask) == kConsStringTag) { + consString = ConsString::cast(string); + PushLeft(consString); + continue; + } + } else { + // Descend right. + // Update progress through the string. + offset += length; + // Keep going if we're still in a ConString. + string = consString->second(); + type = string->map()->instance_type(); + if ((type & kStringRepresentationMask) == kConsStringTag) { + consString = ConsString::cast(string); + PushRight(consString, type); + continue; + } + // Mark the descent. + SetRightDescent(); + // Need this to be updated for the current string. + length = string->length(); + // Account for the possibility of an empty right leaf. + while (length == 0) { + bool blewStack; + // Need to adjust maximum depth for NextLeaf to work. + AdjustMaximumDepth(); + string = NextLeaf(&blewStack, &type); + if (string == NULL) { + // Luckily, this case is impossible. + ASSERT(!blewStack); + return NULL; + } + length = string->length(); + } + } + // Tell the stack we're done decending. + AdjustMaximumDepth(); + ASSERT(length != 0); + // Adjust return values and exit. + unsigned innerOffset = targetOffset - offset; + consumed_ += length - innerOffset; + *outerOffset = innerOffset; + *typeOut = type; + *lengthOut = length; + return string; + } + UNREACHABLE(); + return NULL; +} + + +String* ConsStringIteratorOp::NextLeaf(bool* blewStack, int32_t* typeOut) { + while (true) { + // Tree traversal complete. + if (depth_ == 0) { + *blewStack = false; + return NULL; + } + // We've lost track of higher nodes. + if (maximum_depth_ - depth_ == kStackSize) { + *blewStack = true; + return NULL; + } + // Check if we're done with this level. + bool haveAlreadyReadRight = trace_ & MaskForDepth(depth_ - 1); + if (haveAlreadyReadRight) { + Pop(); + continue; + } + // Go right. + ConsString* consString = frames_[OffsetForDepth(depth_ - 1)]; + String* string = consString->second(); + int32_t type = string->map()->instance_type(); + if ((type & kStringRepresentationMask) != kConsStringTag) { + // Don't need to mark the descent here. + // Pop stack so next iteration is in correct place. + Pop(); + *typeOut = type; + return string; + } + // No need to mark the descent. + consString = ConsString::cast(string); + PushRight(consString, type); + // Need to traverse all the way left. + while (true) { + // Continue left. + // Update marker. + ClearRightDescent(); + string = consString->first(); + type = string->map()->instance_type(); + if ((type & kStringRepresentationMask) != kConsStringTag) { + AdjustMaximumDepth(); + *typeOut = type; + return string; + } + consString = ConsString::cast(string); + PushLeft(consString); + } + } + UNREACHABLE(); + return NULL; } @@ -6512,8 +7164,8 @@ void String::ReadBlockIntoBuffer(String* input, switch (StringShape(input).representation_tag()) { case kSeqStringTag: - if (input->IsAsciiRepresentation()) { - SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb, + if (input->IsOneByteRepresentation()) { + SeqOneByteString::cast(input)->SeqOneByteStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars); return; @@ -6529,7 +7181,7 @@ void String::ReadBlockIntoBuffer(String* input, max_chars); return; case kExternalStringTag: - if (input->IsAsciiRepresentation()) { + if (input->IsOneByteRepresentation()) { ExternalAsciiString::cast(input)-> ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars); } else { @@ -6714,7 +7366,7 @@ void String::WriteToFlat(String* src, while (true) { ASSERT(0 <= from && from <= to && to <= source->length()); switch (StringShape(source).full_representation_tag()) { - case kAsciiStringTag | kExternalStringTag: { + case kOneByteStringTag | kExternalStringTag: { CopyChars(sink, ExternalAsciiString::cast(source)->GetChars() + from, to - from); @@ -6728,9 +7380,9 @@ void String::WriteToFlat(String* src, to - from); return; } - case kAsciiStringTag | kSeqStringTag: { + case kOneByteStringTag | kSeqStringTag: { CopyChars(sink, - SeqAsciiString::cast(source)->GetChars() + from, + SeqOneByteString::cast(source)->GetChars() + from, to - from); return; } @@ -6740,7 +7392,7 @@ void String::WriteToFlat(String* src, to - from); return; } - case kAsciiStringTag | kConsStringTag: + case kOneByteStringTag | kConsStringTag: case kTwoByteStringTag | kConsStringTag: { ConsString* cons_string = ConsString::cast(source); String* first = cons_string->first(); @@ -6765,9 +7417,9 @@ void String::WriteToFlat(String* src, // common case of sequential ascii right child. if (to - boundary == 1) { sink[boundary - from] = static_cast<sinkchar>(second->Get(0)); - } else if (second->IsSeqAsciiString()) { + } else if (second->IsSeqOneByteString()) { CopyChars(sink + boundary - from, - SeqAsciiString::cast(second)->GetChars(), + SeqOneByteString::cast(second)->GetChars(), to - boundary); } else { WriteToFlat(second, @@ -6781,7 +7433,7 @@ void String::WriteToFlat(String* src, } break; } - case kAsciiStringTag | kSlicedStringTag: + case kOneByteStringTag | kSlicedStringTag: case kTwoByteStringTag | kSlicedStringTag: { SlicedString* slice = SlicedString::cast(source); unsigned offset = slice->offset(); @@ -6906,8 +7558,8 @@ bool String::SlowEquals(String* other) { if (StringShape(lhs).IsSequentialAscii() && StringShape(rhs).IsSequentialAscii()) { - const char* str1 = SeqAsciiString::cast(lhs)->GetChars(); - const char* str2 = SeqAsciiString::cast(rhs)->GetChars(); + const char* str1 = SeqOneByteString::cast(lhs)->GetChars(); + const char* str2 = SeqOneByteString::cast(rhs)->GetChars(); return CompareRawStringContents(Vector<const char>(str1, len), Vector<const char>(str2, len)); } @@ -7035,7 +7687,7 @@ uint32_t String::ComputeAndSetHash() { // Compute the hash code. uint32_t field = 0; if (StringShape(this).IsSequentialAscii()) { - field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), + field = HashSequentialString(SeqOneByteString::cast(this)->GetChars(), len, GetHeap()->HashSeed()); } else if (StringShape(this).IsSequentialTwoByte()) { @@ -7103,6 +7755,36 @@ bool String::SlowAsArrayIndex(uint32_t* index) { } +String* SeqString::Truncate(int new_length) { + Heap* heap = GetHeap(); + if (new_length <= 0) return heap->empty_string(); + + int string_size, allocated_string_size; + int old_length = length(); + if (old_length <= new_length) return this; + + if (IsSeqOneByteString()) { + allocated_string_size = SeqOneByteString::SizeFor(old_length); + string_size = SeqOneByteString::SizeFor(new_length); + } else { + allocated_string_size = SeqTwoByteString::SizeFor(old_length); + string_size = SeqTwoByteString::SizeFor(new_length); + } + + int delta = allocated_string_size - string_size; + set_length(new_length); + + // String sizes are pointer size aligned, so that we can use filler objects + // that are a multiple of pointer size. + Address end_of_string = address() + string_size; + heap->CreateFillerObjectAt(end_of_string, delta); + if (Marking::IsBlack(Marking::MarkBitFrom(this))) { + MemoryChunk::IncrementLiveBytesFromMutator(address(), -delta); + } + return this; +} + + uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { // For array indexes mix the length into the hash as an array index could // be zero. @@ -7138,7 +7820,6 @@ void StringHasher::AddSurrogatePairNoIndex(uc32 c) { uint32_t StringHasher::GetHashField() { - ASSERT(is_valid()); if (length_ <= String::kMaxHashCalcLength) { if (is_array_index()) { return MakeArrayIndexHash(array_index(), length_); @@ -7193,13 +7874,46 @@ void String::PrintOn(FILE* file) { } +static void TrimEnumCache(Heap* heap, Map* map, DescriptorArray* descriptors) { + int live_enum = map->EnumLength(); + if (live_enum == Map::kInvalidEnumCache) { + live_enum = map->NumberOfDescribedProperties(OWN_DESCRIPTORS, DONT_ENUM); + } + if (live_enum == 0) return descriptors->ClearEnumCache(); + + FixedArray* enum_cache = descriptors->GetEnumCache(); + + int to_trim = enum_cache->length() - live_enum; + if (to_trim <= 0) return; + RightTrimFixedArray<FROM_GC>(heap, descriptors->GetEnumCache(), to_trim); + + if (!descriptors->HasEnumIndicesCache()) return; + FixedArray* enum_indices_cache = descriptors->GetEnumIndicesCache(); + RightTrimFixedArray<FROM_GC>(heap, enum_indices_cache, to_trim); +} + + +static void TrimDescriptorArray(Heap* heap, + Map* map, + DescriptorArray* descriptors, + int number_of_own_descriptors) { + int number_of_descriptors = descriptors->number_of_descriptors(); + int to_trim = number_of_descriptors - number_of_own_descriptors; + if (to_trim <= 0) return; + + RightTrimFixedArray<FROM_GC>(heap, descriptors, to_trim); + descriptors->SetNumberOfDescriptors(number_of_own_descriptors); + + if (descriptors->HasEnumCache()) TrimEnumCache(heap, map, descriptors); + descriptors->Sort(); +} + + // Clear a possible back pointer in case the transition leads to a dead map. // Return true in case a back pointer has been cleared and false otherwise. -static bool ClearBackPointer(Heap* heap, Object* target) { - ASSERT(target->IsMap()); - Map* map = Map::cast(target); - if (Marking::MarkBitFrom(map).Get()) return false; - map->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER); +static bool ClearBackPointer(Heap* heap, Map* target) { + if (Marking::MarkBitFrom(target).Get()) return false; + target->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER); return true; } @@ -7218,9 +7932,17 @@ void Map::ClearNonLiveTransitions(Heap* heap) { int transition_index = 0; + DescriptorArray* descriptors = instance_descriptors(); + bool descriptors_owner_died = false; + // Compact all live descriptors to the left. for (int i = 0; i < t->number_of_transitions(); ++i) { - if (!ClearBackPointer(heap, t->GetTarget(i))) { + Map* target = t->GetTarget(i); + if (ClearBackPointer(heap, target)) { + if (target->instance_descriptors() == descriptors) { + descriptors_owner_died = true; + } + } else { if (i != transition_index) { String* key = t->GetKey(i); t->SetKey(transition_index, key); @@ -7235,6 +7957,9 @@ void Map::ClearNonLiveTransitions(Heap* heap) { if (t->HasElementsTransition() && ClearBackPointer(heap, t->elements_transition())) { + if (t->elements_transition()->instance_descriptors() == descriptors) { + descriptors_owner_died = true; + } t->ClearElementsTransition(); } else { // If there are no transitions to be cleared, return. @@ -7243,19 +7968,21 @@ void Map::ClearNonLiveTransitions(Heap* heap) { if (transition_index == t->number_of_transitions()) return; } - // If the final transition array does not contain any live transitions, remove - // the transition array from the map. - if (transition_index == 0 && - !t->HasElementsTransition() && - !t->HasPrototypeTransitions() && - t->descriptors()->IsEmpty()) { - return ClearTransitions(heap); + int number_of_own_descriptors = NumberOfOwnDescriptors(); + + if (descriptors_owner_died) { + if (number_of_own_descriptors > 0) { + TrimDescriptorArray(heap, this, descriptors, number_of_own_descriptors); + ASSERT(descriptors->number_of_descriptors() == number_of_own_descriptors); + } else { + ASSERT(descriptors == GetHeap()->empty_descriptor_array()); + } } int trim = t->number_of_transitions() - transition_index; if (trim > 0) { - RightTrimFixedArray<FROM_GC>( - heap, t, trim * TransitionArray::kTransitionSize); + RightTrimFixedArray<FROM_GC>(heap, t, t->IsSimpleTransition() + ? trim : trim * TransitionArray::kTransitionSize); } } @@ -7289,6 +8016,7 @@ bool Map::EquivalentToForNormalization(Map* other, instance_type() == other->instance_type() && bit_field() == other->bit_field() && bit_field2() == other->bit_field2() && + is_observed() == other->is_observed() && function_with_prototype() == other->function_with_prototype(); } @@ -7469,6 +8197,35 @@ MaybeObject* JSObject::OptimizeAsPrototype() { } +MUST_USE_RESULT static MaybeObject* CacheInitialJSArrayMaps( + Context* native_context, Map* initial_map) { + // Replace all of the cached initial array maps in the native context with + // the appropriate transitioned elements kind maps. + Heap* heap = native_context->GetHeap(); + MaybeObject* maybe_maps = + heap->AllocateFixedArrayWithHoles(kElementsKindCount); + FixedArray* maps; + if (!maybe_maps->To(&maps)) return maybe_maps; + + Map* current_map = initial_map; + ElementsKind kind = current_map->elements_kind(); + ASSERT(kind == GetInitialFastElementsKind()); + maps->set(kind, current_map); + for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1; + i < kFastElementsKindCount; ++i) { + Map* new_map; + ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i); + MaybeObject* maybe_new_map = + current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION); + if (!maybe_new_map->To(&new_map)) return maybe_new_map; + maps->set(next_kind, new_map); + current_map = new_map; + } + native_context->set_js_array_maps(maps); + return initial_map; +} + + MaybeObject* JSFunction::SetInstancePrototype(Object* value) { ASSERT(value->IsJSReceiver()); Heap* heap = GetHeap(); @@ -7483,14 +8240,29 @@ MaybeObject* JSFunction::SetInstancePrototype(Object* value) { // Now some logic for the maps of the objects that are created by using this // function as a constructor. if (has_initial_map()) { - // If the function has allocated the initial map - // replace it with a copy containing the new prototype. + // If the function has allocated the initial map replace it with a + // copy containing the new prototype. Also complete any in-object + // slack tracking that is in progress at this point because it is + // still tracking the old copy. + if (shared()->IsInobjectSlackTrackingInProgress()) { + shared()->CompleteInobjectSlackTracking(); + } Map* new_map; - MaybeObject* maybe_new_map = initial_map()->Copy(); - if (!maybe_new_map->To(&new_map)) return maybe_new_map; + MaybeObject* maybe_object = initial_map()->Copy(); + if (!maybe_object->To(&new_map)) return maybe_object; new_map->set_prototype(value); - MaybeObject* maybe_object = set_initial_map_and_cache_transitions(new_map); - if (maybe_object->IsFailure()) return maybe_object; + + // If the function is used as the global Array function, cache the + // initial map (and transitioned versions) in the native context. + Context* native_context = context()->native_context(); + Object* array_function = native_context->get(Context::ARRAY_FUNCTION_INDEX); + if (array_function->IsJSFunction() && + this == JSFunction::cast(array_function)) { + MaybeObject* ok = CacheInitialJSArrayMaps(native_context, new_map); + if (ok->IsFailure()) return ok; + } + + set_initial_map(new_map); } else { // Put the value in the initial map field until an initial map is // needed. At that point, a new initial map is created and the @@ -7875,7 +8647,7 @@ void SharedFunctionInfo::DetachInitialMap() { // constructor is called. The countdown will continue and (possibly after // several more GCs) CompleteInobjectSlackTracking will eventually be called. Heap* heap = map->GetHeap(); - set_initial_map(heap->raw_unchecked_undefined_value()); + set_initial_map(heap->undefined_value()); Builtins* builtins = heap->isolate()->builtins(); ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), *RawField(this, kConstructStubOffset)); @@ -8003,6 +8775,15 @@ void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { } +void ObjectVisitor::VisitCodeAgeSequence(RelocInfo* rinfo) { + ASSERT(RelocInfo::IsCodeAgeSequence(rinfo->rmode())); + Object* stub = rinfo->code_age_stub(); + if (stub) { + VisitPointer(&stub); + } +} + + void ObjectVisitor::VisitCodeEntry(Address entry_address) { Object* code = Code::GetObjectFromEntryAddress(entry_address); Object* old_code = code; @@ -8197,6 +8978,7 @@ void Code::ClearInlineCaches() { void Code::ClearTypeFeedbackCells(Heap* heap) { + if (kind() != FUNCTION) return; Object* raw_info = type_feedback_info(); if (raw_info->IsTypeFeedbackInfo()) { TypeFeedbackCells* type_feedback_cells = @@ -8211,7 +8993,92 @@ void Code::ClearTypeFeedbackCells(Heap* heap) { bool Code::allowed_in_shared_map_code_cache() { return is_keyed_load_stub() || is_keyed_store_stub() || - (is_compare_ic_stub() && compare_state() == CompareIC::KNOWN_OBJECTS); + (is_compare_ic_stub() && + ICCompareStub::CompareState(stub_info()) == CompareIC::KNOWN_OBJECTS); +} + + +void Code::MakeCodeAgeSequenceYoung(byte* sequence) { + PatchPlatformCodeAge(sequence, kNoAge, NO_MARKING_PARITY); +} + + +void Code::MakeOlder(MarkingParity current_parity) { + byte* sequence = FindCodeAgeSequence(); + if (sequence != NULL) { + Age age; + MarkingParity code_parity; + GetCodeAgeAndParity(sequence, &age, &code_parity); + if (age != kLastCodeAge && code_parity != current_parity) { + PatchPlatformCodeAge(sequence, static_cast<Age>(age + 1), + current_parity); + } + } +} + + +bool Code::IsOld() { + byte* sequence = FindCodeAgeSequence(); + if (sequence == NULL) return false; + Age age; + MarkingParity parity; + GetCodeAgeAndParity(sequence, &age, &parity); + return age >= kSexagenarianCodeAge; +} + + +byte* Code::FindCodeAgeSequence() { + return FLAG_age_code && + prologue_offset() != kPrologueOffsetNotSet && + (kind() == OPTIMIZED_FUNCTION || + (kind() == FUNCTION && !has_debug_break_slots())) + ? instruction_start() + prologue_offset() + : NULL; +} + + +void Code::GetCodeAgeAndParity(Code* code, Age* age, + MarkingParity* parity) { + Isolate* isolate = Isolate::Current(); + Builtins* builtins = isolate->builtins(); + Code* stub = NULL; +#define HANDLE_CODE_AGE(AGE) \ + stub = *builtins->Make##AGE##CodeYoungAgainEvenMarking(); \ + if (code == stub) { \ + *age = k##AGE##CodeAge; \ + *parity = EVEN_MARKING_PARITY; \ + return; \ + } \ + stub = *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ + if (code == stub) { \ + *age = k##AGE##CodeAge; \ + *parity = ODD_MARKING_PARITY; \ + return; \ + } + CODE_AGE_LIST(HANDLE_CODE_AGE) +#undef HANDLE_CODE_AGE + UNREACHABLE(); +} + + +Code* Code::GetCodeAgeStub(Age age, MarkingParity parity) { + Isolate* isolate = Isolate::Current(); + Builtins* builtins = isolate->builtins(); + switch (age) { +#define HANDLE_CODE_AGE(AGE) \ + case k##AGE##CodeAge: { \ + Code* stub = parity == EVEN_MARKING_PARITY \ + ? *builtins->Make##AGE##CodeYoungAgainEvenMarking() \ + : *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ + return stub; \ + } + CODE_AGE_LIST(HANDLE_CODE_AGE) +#undef HANDLE_CODE_AGE + default: + UNREACHABLE(); + break; + } + return NULL; } @@ -8473,11 +9340,15 @@ void Code::Disassemble(const char* name, FILE* out) { PrintF(out, "argc = %d\n", arguments_count()); } if (is_compare_ic_stub()) { - CompareIC::State state = CompareIC::ComputeState(this); - PrintF(out, "compare_state = %s\n", CompareIC::GetStateName(state)); - } - if (is_compare_ic_stub() && major_key() == CodeStub::CompareIC) { - Token::Value op = CompareIC::ComputeOperation(this); + ASSERT(major_key() == CodeStub::CompareIC); + CompareIC::State left_state, right_state, handler_state; + Token::Value op; + ICCompareStub::DecodeMinorKey(stub_info(), &left_state, &right_state, + &handler_state, &op); + PrintF(out, "compare_state = %s*%s -> %s\n", + CompareIC::GetStateName(left_state), + CompareIC::GetStateName(right_state), + CompareIC::GetStateName(handler_state)); PrintF(out, "compare_operation = %s\n", Token::Name(op)); } } @@ -8523,8 +9394,6 @@ void Code::Disassemble(const char* name, FILE* out) { PrintF(out, "\n"); } PrintF(out, "\n"); - // Just print if type feedback info is ever used for optimized code. - ASSERT(type_feedback_info()->IsUndefined()); } else if (kind() == FUNCTION) { unsigned offset = stack_check_table_offset(); // If there is no stack check table, the "table start" will at or after @@ -8566,9 +9435,8 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength( // Allocate a new fast elements backing store. FixedArray* new_elements; - { MaybeObject* maybe = heap->AllocateFixedArrayWithHoles(capacity); - if (!maybe->To(&new_elements)) return maybe; - } + MaybeObject* maybe = heap->AllocateUninitializedFixedArray(capacity); + if (!maybe->To(&new_elements)) return maybe; ElementsKind elements_kind = GetElementsKind(); ElementsKind new_elements_kind; @@ -8592,10 +9460,10 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength( } FixedArrayBase* old_elements = elements(); ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); - { MaybeObject* maybe_obj = - accessor->CopyElements(this, new_elements, new_elements_kind); - if (maybe_obj->IsFailure()) return maybe_obj; - } + MaybeObject* maybe_obj = + accessor->CopyElements(this, new_elements, new_elements_kind); + if (maybe_obj->IsFailure()) return maybe_obj; + if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { Map* new_map = map(); if (new_elements_kind != elements_kind) { @@ -8701,7 +9569,53 @@ void JSArray::Expand(int required_size) { MaybeObject* JSArray::SetElementsLength(Object* len) { // We should never end in here with a pixel or external array. ASSERT(AllowsSetElementsLength()); - return GetElementsAccessor()->SetLength(this, len); + if (!(FLAG_harmony_observation && map()->is_observed())) + return GetElementsAccessor()->SetLength(this, len); + + Isolate* isolate = GetIsolate(); + HandleScope scope(isolate); + Handle<JSArray> self(this); + List<Handle<String> > indices; + List<Handle<Object> > old_values; + Handle<Object> old_length_handle(self->length()); + Handle<Object> new_length_handle(len); + uint32_t old_length = 0; + CHECK(old_length_handle->ToArrayIndex(&old_length)); + uint32_t new_length = 0; + if (!new_length_handle->ToArrayIndex(&new_length)) + return Failure::InternalError(); + + // TODO(adamk): This loop can be very slow for arrays in dictionary mode. + // Find another way to iterate over arrays with dictionary elements. + for (uint32_t i = old_length - 1; i + 1 > new_length; --i) { + PropertyAttributes attributes = self->GetLocalElementAttribute(i); + if (attributes == ABSENT) continue; + // A non-configurable property will cause the truncation operation to + // stop at this index. + if (attributes == DONT_DELETE) break; + old_values.Add( + self->GetLocalElementAccessorPair(i) == NULL + ? Object::GetElement(self, i) + : Handle<Object>::cast(isolate->factory()->the_hole_value())); + indices.Add(isolate->factory()->Uint32ToString(i)); + } + + MaybeObject* result = + self->GetElementsAccessor()->SetLength(*self, *new_length_handle); + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + CHECK(self->length()->ToArrayIndex(&new_length)); + if (old_length != new_length) { + for (int i = 0; i < indices.length(); ++i) { + JSObject::EnqueueChangeRecord( + self, "deleted", indices[i], old_values[i]); + } + JSObject::EnqueueChangeRecord( + self, "updated", isolate->factory()->length_symbol(), + old_length_handle); + } + return *hresult; } @@ -8765,6 +9679,22 @@ MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) { } +void Map::ZapTransitions() { + TransitionArray* transition_array = transitions(); + MemsetPointer(transition_array->data_start(), + GetHeap()->the_hole_value(), + transition_array->length()); +} + + +void Map::ZapPrototypeTransitions() { + FixedArray* proto_transitions = GetPrototypeTransitions(); + MemsetPointer(proto_transitions->data_start(), + GetHeap()->the_hole_value(), + proto_transitions->length()); +} + + MaybeObject* JSReceiver::SetPrototype(Object* value, bool skip_hidden_prototypes) { #ifdef DEBUG @@ -8862,203 +9792,51 @@ MaybeObject* JSObject::EnsureCanContainElements(Arguments* args, } -bool JSObject::HasElementWithInterceptor(JSReceiver* receiver, uint32_t index) { - Isolate* isolate = GetIsolate(); - // Make sure that the top context does not change when doing - // callbacks or interceptor calls. - AssertNoContextChange ncc; - HandleScope scope(isolate); - Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); - Handle<JSReceiver> receiver_handle(receiver); - Handle<JSObject> holder_handle(this); - CustomArguments args(isolate, interceptor->data(), receiver, this); - v8::AccessorInfo info(args.end()); - if (!interceptor->query()->IsUndefined()) { - v8::IndexedPropertyQuery query = - v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); - LOG(isolate, - ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); - v8::Handle<v8::Integer> result; - { - // Leaving JavaScript. - VMState state(isolate, EXTERNAL); - result = query(index, info); - } - if (!result.IsEmpty()) { - ASSERT(result->IsInt32()); - return true; // absence of property is signaled by empty handle. - } - } else if (!interceptor->getter()->IsUndefined()) { - v8::IndexedPropertyGetter getter = - v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); - LOG(isolate, - ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index)); - v8::Handle<v8::Value> result; - { - // Leaving JavaScript. - VMState state(isolate, EXTERNAL); - result = getter(index, info); - } - if (!result.IsEmpty()) return true; +PropertyType JSObject::GetLocalPropertyType(String* name) { + uint32_t index = 0; + if (name->AsArrayIndex(&index)) { + return GetLocalElementType(index); } + LookupResult lookup(GetIsolate()); + LocalLookup(name, &lookup, true); + return lookup.type(); +} - if (holder_handle->GetElementsAccessor()->HasElement( - *receiver_handle, *holder_handle, index)) { - return true; - } - if (holder_handle->IsStringObjectWithCharacterAt(index)) return true; - Object* pt = holder_handle->GetPrototype(); - if (pt->IsJSProxy()) { - // We need to follow the spec and simulate a call to [[GetOwnProperty]]. - return JSProxy::cast(pt)->GetElementAttributeWithHandler( - receiver, index) != ABSENT; - } - if (pt->IsNull()) return false; - return JSObject::cast(pt)->HasElementWithReceiver(*receiver_handle, index); +PropertyType JSObject::GetLocalElementType(uint32_t index) { + return GetElementsAccessor()->GetType(this, this, index); } -JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { - // Check access rights if needed. - if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); - return UNDEFINED_ELEMENT; - } - } - - if (IsJSGlobalProxy()) { - Object* proto = GetPrototype(); - if (proto->IsNull()) return UNDEFINED_ELEMENT; - ASSERT(proto->IsJSGlobalObject()); - return JSObject::cast(proto)->HasLocalElement(index); +AccessorPair* JSObject::GetLocalPropertyAccessorPair(String* name) { + uint32_t index = 0; + if (name->AsArrayIndex(&index)) { + return GetLocalElementAccessorPair(index); } - // Check for lookup interceptor - if (HasIndexedInterceptor()) { - return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT - : UNDEFINED_ELEMENT; - } + LookupResult lookup(GetIsolate()); + LocalLookupRealNamedProperty(name, &lookup); - // Handle [] on String objects. - if (this->IsStringObjectWithCharacterAt(index)) { - return STRING_CHARACTER_ELEMENT; - } - - switch (GetElementsKind()) { - case FAST_SMI_ELEMENTS: - case FAST_ELEMENTS: - case FAST_HOLEY_SMI_ELEMENTS: - case FAST_HOLEY_ELEMENTS: { - uint32_t length = IsJSArray() ? - static_cast<uint32_t> - (Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast<uint32_t>(FixedArray::cast(elements())->length()); - if ((index < length) && - !FixedArray::cast(elements())->get(index)->IsTheHole()) { - return FAST_ELEMENT; - } - break; - } - case FAST_DOUBLE_ELEMENTS: - case FAST_HOLEY_DOUBLE_ELEMENTS: { - uint32_t length = IsJSArray() ? - static_cast<uint32_t> - (Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length()); - if ((index < length) && - !FixedDoubleArray::cast(elements())->is_the_hole(index)) { - return FAST_ELEMENT; - } - break; - } - case EXTERNAL_PIXEL_ELEMENTS: { - ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); - if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT; - break; - } - case EXTERNAL_BYTE_ELEMENTS: - case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case EXTERNAL_SHORT_ELEMENTS: - case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case EXTERNAL_INT_ELEMENTS: - case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: - case EXTERNAL_DOUBLE_ELEMENTS: { - ExternalArray* array = ExternalArray::cast(elements()); - if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT; - break; - } - case DICTIONARY_ELEMENTS: { - if (element_dictionary()->FindEntry(index) != - SeededNumberDictionary::kNotFound) { - return DICTIONARY_ELEMENT; - } - break; - } - case NON_STRICT_ARGUMENTS_ELEMENTS: { - // Aliased parameters and non-aliased elements in a fast backing store - // behave as FAST_ELEMENT. Non-aliased elements in a dictionary - // backing store behave as DICTIONARY_ELEMENT. - FixedArray* parameter_map = FixedArray::cast(elements()); - uint32_t length = parameter_map->length(); - Object* probe = - index < (length - 2) ? parameter_map->get(index + 2) : NULL; - if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT; - // If not aliased, check the arguments. - FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); - if (arguments->IsDictionary()) { - SeededNumberDictionary* dictionary = - SeededNumberDictionary::cast(arguments); - if (dictionary->FindEntry(index) != SeededNumberDictionary::kNotFound) { - return DICTIONARY_ELEMENT; - } - } else { - length = arguments->length(); - probe = (index < length) ? arguments->get(index) : NULL; - if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT; - } - break; - } + if (lookup.IsPropertyCallbacks() && + lookup.GetCallbackObject()->IsAccessorPair()) { + return AccessorPair::cast(lookup.GetCallbackObject()); } - - return UNDEFINED_ELEMENT; + return NULL; } -bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { - // Check access rights if needed. - if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); - return false; - } - } - - // Check for lookup interceptor - if (HasIndexedInterceptor()) { - return HasElementWithInterceptor(receiver, index); - } - - ElementsAccessor* accessor = GetElementsAccessor(); - if (accessor->HasElement(receiver, this, index)) { - return true; +AccessorPair* JSObject::GetLocalElementAccessorPair(uint32_t index) { + if (IsJSGlobalProxy()) { + Object* proto = GetPrototype(); + if (proto->IsNull()) return NULL; + ASSERT(proto->IsJSGlobalObject()); + return JSObject::cast(proto)->GetLocalElementAccessorPair(index); } - // Handle [] on String objects. - if (this->IsStringObjectWithCharacterAt(index)) return true; + // Check for lookup interceptor. + if (HasIndexedInterceptor()) return NULL; - Object* pt = GetPrototype(); - if (pt->IsNull()) return false; - if (pt->IsJSProxy()) { - // We need to follow the spec and simulate a call to [[GetOwnProperty]]. - return JSProxy::cast(pt)->GetElementAttributeWithHandler( - receiver, index) != ABSENT; - } - return JSObject::cast(pt)->HasElementWithReceiver(receiver, index); + return GetElementsAccessor()->GetAccessorPair(this, this, index); } @@ -9367,7 +10145,7 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, MaybeObject* JSObject::SetDictionaryElement(uint32_t index, - Object* value, + Object* value_raw, PropertyAttributes attributes, StrictModeFlag strict_mode, bool check_prototype, @@ -9375,24 +10153,23 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); Isolate* isolate = GetIsolate(); Heap* heap = isolate->heap(); + Handle<JSObject> self(this); + Handle<Object> value(value_raw); // Insert element in the dictionary. - FixedArray* elements = FixedArray::cast(this->elements()); + Handle<FixedArray> elements(FixedArray::cast(this->elements())); bool is_arguments = (elements->map() == heap->non_strict_arguments_elements_map()); - SeededNumberDictionary* dictionary = NULL; - if (is_arguments) { - dictionary = SeededNumberDictionary::cast(elements->get(1)); - } else { - dictionary = SeededNumberDictionary::cast(elements); - } + Handle<SeededNumberDictionary> dictionary(is_arguments + ? SeededNumberDictionary::cast(elements->get(1)) + : SeededNumberDictionary::cast(*elements)); int entry = dictionary->FindEntry(index); if (entry != SeededNumberDictionary::kNotFound) { Object* element = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) { - return SetElementWithCallback(element, index, value, this, strict_mode); + return SetElementWithCallback(element, index, *value, this, strict_mode); } else { dictionary->UpdateMaxNumberKey(index); // If a value has not been initialized we allow writing to it even if it @@ -9421,24 +10198,24 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, Context* context = Context::cast(elements->get(0)); int context_index = entry->aliased_context_slot(); ASSERT(!context->get(context_index)->IsTheHole()); - context->set(context_index, value); + context->set(context_index, *value); // For elements that are still writable we keep slow aliasing. - if (!details.IsReadOnly()) value = element; + if (!details.IsReadOnly()) value = handle(element, isolate); } - dictionary->ValueAtPut(entry, value); + dictionary->ValueAtPut(entry, *value); } } else { // Index not already used. Look for an accessor in the prototype chain. + // Can cause GC! if (check_prototype) { bool found; - MaybeObject* result = - SetElementWithCallbackSetterInPrototypes( - index, value, &found, strict_mode); + MaybeObject* result = SetElementWithCallbackSetterInPrototypes( + index, *value, &found, strict_mode); if (found) return result; } // When we set the is_extensible flag to false we always force the // element into dictionary mode (and force them to stay there). - if (!map()->is_extensible()) { + if (!self->map()->is_extensible()) { if (strict_mode == kNonStrictMode) { return isolate->heap()->undefined_value(); } else { @@ -9453,30 +10230,31 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, } FixedArrayBase* new_dictionary; PropertyDetails details = PropertyDetails(attributes, NORMAL); - MaybeObject* maybe = dictionary->AddNumberEntry(index, value, details); + MaybeObject* maybe = dictionary->AddNumberEntry(index, *value, details); if (!maybe->To(&new_dictionary)) return maybe; - if (dictionary != SeededNumberDictionary::cast(new_dictionary)) { + if (*dictionary != SeededNumberDictionary::cast(new_dictionary)) { if (is_arguments) { elements->set(1, new_dictionary); } else { - set_elements(new_dictionary); + self->set_elements(new_dictionary); } - dictionary = SeededNumberDictionary::cast(new_dictionary); + dictionary = + handle(SeededNumberDictionary::cast(new_dictionary), isolate); } } // Update the array length if this JSObject is an array. - if (IsJSArray()) { + if (self->IsJSArray()) { MaybeObject* result = - JSArray::cast(this)->JSArrayUpdateLengthFromIndex(index, value); + JSArray::cast(*self)->JSArrayUpdateLengthFromIndex(index, *value); if (result->IsFailure()) return result; } // Attempt to put this object back in fast case. - if (ShouldConvertToFastElements()) { + if (self->ShouldConvertToFastElements()) { uint32_t new_length = 0; - if (IsJSArray()) { - CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length)); + if (self->IsJSArray()) { + CHECK(JSArray::cast(*self)->length()->ToArrayIndex(&new_length)); } else { new_length = dictionary->max_number_key() + 1; } @@ -9485,16 +10263,15 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, : kDontAllowSmiElements; bool has_smi_only_elements = false; bool should_convert_to_fast_double_elements = - ShouldConvertToFastDoubleElements(&has_smi_only_elements); + self->ShouldConvertToFastDoubleElements(&has_smi_only_elements); if (has_smi_only_elements) { smi_mode = kForceSmiElements; } MaybeObject* result = should_convert_to_fast_double_elements - ? SetFastDoubleElementsCapacityAndLength(new_length, new_length) - : SetFastElementsCapacityAndLength(new_length, - new_length, - smi_mode); - ValidateElements(); + ? self->SetFastDoubleElementsCapacityAndLength(new_length, new_length) + : self->SetFastElementsCapacityAndLength( + new_length, new_length, smi_mode); + self->ValidateElements(); if (result->IsFailure()) return result; #ifdef DEBUG if (FLAG_trace_normalization) { @@ -9503,7 +10280,7 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, } #endif } - return value; + return *value; } @@ -9659,28 +10436,27 @@ Handle<Object> JSObject::SetElement(Handle<JSObject> object, MaybeObject* JSObject::SetElement(uint32_t index, - Object* value, + Object* value_raw, PropertyAttributes attributes, StrictModeFlag strict_mode, bool check_prototype, SetPropertyMode set_mode) { + Isolate* isolate = GetIsolate(); + // Check access rights if needed. if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) { - HandleScope scope(heap->isolate()); - Handle<Object> value_handle(value); - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET); - return *value_handle; + if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_SET)) { + isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); + return value_raw; } } if (IsJSGlobalProxy()) { Object* proto = GetPrototype(); - if (proto->IsNull()) return value; + if (proto->IsNull()) return value_raw; ASSERT(proto->IsJSGlobalObject()); return JSObject::cast(proto)->SetElement(index, - value, + value_raw, attributes, strict_mode, check_prototype, @@ -9689,10 +10465,8 @@ MaybeObject* JSObject::SetElement(uint32_t index, // Don't allow element properties to be redefined for external arrays. if (HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) { - Isolate* isolate = GetHeap()->isolate(); - Handle<Object> receiver(this); Handle<Object> number = isolate->factory()->NewNumberFromUint(index); - Handle<Object> args[] = { receiver, number }; + Handle<Object> args[] = { handle(this, isolate), number }; Handle<Object> error = isolate->factory()->NewTypeError( "redef_external_array_element", HandleVector(args, ARRAY_SIZE(args))); return isolate->Throw(*error); @@ -9707,22 +10481,55 @@ MaybeObject* JSObject::SetElement(uint32_t index, dictionary->set_requires_slow_elements(); } + if (!(FLAG_harmony_observation && map()->is_observed())) { + return HasIndexedInterceptor() + ? SetElementWithInterceptor( + index, value_raw, attributes, strict_mode, check_prototype, set_mode) + : SetElementWithoutInterceptor( + index, value_raw, attributes, strict_mode, check_prototype, set_mode); + } + + // From here on, everything has to be handlified. + Handle<JSObject> self(this); + Handle<Object> value(value_raw); + PropertyAttributes old_attributes = self->GetLocalElementAttribute(index); + Handle<Object> old_value = isolate->factory()->the_hole_value(); + Handle<Object> old_length; + + if (old_attributes != ABSENT) { + if (self->GetLocalElementAccessorPair(index) == NULL) + old_value = Object::GetElement(self, index); + } else if (self->IsJSArray()) { + // Store old array length in case adding an element grows the array. + old_length = handle(Handle<JSArray>::cast(self)->length(), isolate); + } + // Check for lookup interceptor - if (HasIndexedInterceptor()) { - return SetElementWithInterceptor(index, - value, - attributes, - strict_mode, - check_prototype, - set_mode); + MaybeObject* result = self->HasIndexedInterceptor() + ? self->SetElementWithInterceptor( + index, *value, attributes, strict_mode, check_prototype, set_mode) + : self->SetElementWithoutInterceptor( + index, *value, attributes, strict_mode, check_prototype, set_mode); + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + Handle<String> name = isolate->factory()->Uint32ToString(index); + PropertyAttributes new_attributes = self->GetLocalElementAttribute(index); + if (old_attributes == ABSENT) { + EnqueueChangeRecord(self, "new", name, old_value); + if (self->IsJSArray() && + !old_length->SameValue(Handle<JSArray>::cast(self)->length())) { + EnqueueChangeRecord( + self, "updated", isolate->factory()->length_symbol(), old_length); + } + } else if (old_attributes != new_attributes || old_value->IsTheHole()) { + EnqueueChangeRecord(self, "reconfigured", name, old_value); + } else if (!old_value->SameValue(*Object::GetElement(self, index))) { + EnqueueChangeRecord(self, "updated", name, old_value); } - return SetElementWithoutInterceptor(index, - value, - attributes, - strict_mode, - check_prototype, - set_mode); + return *hresult; } @@ -9838,6 +10645,8 @@ MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { to_kind = GetHoleyElementsKind(to_kind); } + if (from_kind == to_kind) return this; + Isolate* isolate = GetIsolate(); if (elements() == isolate->heap()->empty_fixed_array() || (IsFastSmiOrObjectElementsKind(from_kind) && @@ -10372,9 +11181,16 @@ bool JSObject::HasRealNamedCallbackProperty(String* key) { int JSObject::NumberOfLocalProperties(PropertyAttributes filter) { - return HasFastProperties() ? - map()->NumberOfDescribedProperties(filter) : - property_dictionary()->NumberOfElementsFilterAttributes(filter); + if (HasFastProperties()) { + Map* map = this->map(); + if (filter == NONE) return map->NumberOfOwnDescriptors(); + if (filter == DONT_ENUM) { + int result = map->EnumLength(); + if (result != Map::kInvalidEnumCache) return result; + } + return map->NumberOfDescribedProperties(OWN_DESCRIPTORS, filter); + } + return property_dictionary()->NumberOfElementsFilterAttributes(filter); } @@ -10497,9 +11313,10 @@ void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) { ASSERT(storage->length() >= (NumberOfLocalProperties() - index)); if (HasFastProperties()) { + int real_size = map()->NumberOfOwnDescriptors(); DescriptorArray* descs = map()->instance_descriptors(); - ASSERT(storage->length() >= index + descs->number_of_descriptors()); - for (int i = 0; i < descs->number_of_descriptors(); i++) { + ASSERT(storage->length() >= index + real_size); + for (int i = 0; i < real_size; i++) { storage->set(index + i, descs->GetKey(i)); } } else { @@ -10931,11 +11748,10 @@ class AsciiSymbolKey : public SequentialSymbolKey<char> { class SubStringAsciiSymbolKey : public HashTableKey { public: - explicit SubStringAsciiSymbolKey(Handle<SeqAsciiString> string, + explicit SubStringAsciiSymbolKey(Handle<SeqOneByteString> string, int from, - int length, - uint32_t seed) - : string_(string), from_(from), length_(length), seed_(seed) { } + int length) + : string_(string), from_(from), length_(length) { } uint32_t Hash() { ASSERT(length_ >= 0); @@ -10952,7 +11768,7 @@ class SubStringAsciiSymbolKey : public HashTableKey { // chance this is an array index. while (i < length_ && hasher.is_array_index()) { hasher.AddCharacter(static_cast<uc32>( - string_->SeqAsciiStringGet(i + from_))); + string_->SeqOneByteStringGet(i + from_))); i++; } @@ -10960,7 +11776,7 @@ class SubStringAsciiSymbolKey : public HashTableKey { // index. while (i < length_) { hasher.AddCharacterNoIndex(static_cast<uc32>( - string_->SeqAsciiStringGet(i + from_))); + string_->SeqOneByteStringGet(i + from_))); i++; } hash_field_ = hasher.GetHashField(); @@ -10988,11 +11804,10 @@ class SubStringAsciiSymbolKey : public HashTableKey { } private: - Handle<SeqAsciiString> string_; + Handle<SeqOneByteString> string_; int from_; int length_; uint32_t hash_field_; - uint32_t seed_; }; @@ -11827,7 +12642,7 @@ class TwoCharHashTableKey : public HashTableKey { hash += hash << 3; hash ^= hash >> 11; hash += hash << 15; - if ((hash & String::kHashBitMask) == 0) hash = String::kZeroHash; + if ((hash & String::kHashBitMask) == 0) hash = StringHasher::kZeroHash; #ifdef DEBUG StringHasher hasher(2, seed); hasher.AddCharacter(c1); @@ -11913,11 +12728,12 @@ MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str, } -MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle<SeqAsciiString> str, - int from, - int length, - Object** s) { - SubStringAsciiSymbolKey key(str, from, length, GetHeap()->HashSeed()); +MaybeObject* SymbolTable::LookupSubStringAsciiSymbol( + Handle<SeqOneByteString> str, + int from, + int length, + Object** s) { + SubStringAsciiSymbolKey key(str, from, length); return LookupKey(&key, s); } @@ -12202,8 +13018,8 @@ MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() { int pos = 0; for (int i = 0; i < capacity; i++) { if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) { - enumeration_order->set( - pos++, Smi::FromInt(DetailsAt(i).dictionary_index())); + int index = DetailsAt(i).dictionary_index(); + enumeration_order->set(pos++, Smi::FromInt(index)); } } @@ -12332,7 +13148,8 @@ MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key, uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash); // Insert element at empty or deleted entry if (!details.IsDeleted() && - details.dictionary_index() == 0 && Shape::kIsEnumerable) { + details.dictionary_index() == 0 && + Shape::kIsEnumerable) { // Assign an enumeration index to the property and update // SetNextEnumerationIndex. int index = NextEnumerationIndex(); @@ -12604,8 +13421,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( PropertyType type = DetailsAt(i).type(); ASSERT(type != FIELD); instance_descriptor_length++; - if (type == NORMAL && - (!value->IsJSFunction() || heap->InNewSpace(value))) { + if (type == NORMAL && !value->IsJSFunction()) { number_of_fields += 1; } } @@ -12638,7 +13454,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( return maybe_descriptors; } - FixedArray::WhitenessWitness witness(descriptors); + DescriptorArray::WhitenessWitness witness(descriptors); int number_of_allocated_fields = number_of_fields + unused_property_fields - inobject_props; @@ -12670,7 +13486,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( int enumeration_index = details.descriptor_index(); PropertyType type = details.type(); - if (value->IsJSFunction() && !heap->InNewSpace(value)) { + if (value->IsJSFunction()) { ConstantFunctionDescriptor d(key, JSFunction::cast(value), details.attributes(), @@ -12705,8 +13521,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( descriptors->Sort(); - MaybeObject* maybe_failure = new_map->InitializeDescriptors(descriptors); - if (maybe_failure->IsFailure()) return maybe_failure; + new_map->InitializeDescriptors(descriptors); new_map->set_unused_property_fields(unused_property_fields); // Transform the object. |