summaryrefslogtreecommitdiff
path: root/deps/v8/src/objects.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/objects.cc')
-rw-r--r--deps/v8/src/objects.cc2760
1 files changed, 1388 insertions, 1372 deletions
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index 6b2f64ac2e..45d108b1ca 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -69,13 +69,13 @@ MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor,
}
-MaybeObject* Object::ToObject(Context* global_context) {
+MaybeObject* Object::ToObject(Context* native_context) {
if (IsNumber()) {
- return CreateJSValue(global_context->number_function(), this);
+ return CreateJSValue(native_context->number_function(), this);
} else if (IsBoolean()) {
- return CreateJSValue(global_context->boolean_function(), this);
+ return CreateJSValue(native_context->boolean_function(), this);
} else if (IsString()) {
- return CreateJSValue(global_context->string_function(), this);
+ return CreateJSValue(native_context->string_function(), this);
}
ASSERT(IsJSObject());
return this;
@@ -87,16 +87,16 @@ MaybeObject* Object::ToObject() {
return this;
} else if (IsNumber()) {
Isolate* isolate = Isolate::Current();
- Context* global_context = isolate->context()->global_context();
- return CreateJSValue(global_context->number_function(), this);
+ Context* native_context = isolate->context()->native_context();
+ return CreateJSValue(native_context->number_function(), this);
} else if (IsBoolean()) {
Isolate* isolate = HeapObject::cast(this)->GetIsolate();
- Context* global_context = isolate->context()->global_context();
- return CreateJSValue(global_context->boolean_function(), this);
+ Context* native_context = isolate->context()->native_context();
+ return CreateJSValue(native_context->boolean_function(), this);
} else if (IsString()) {
Isolate* isolate = HeapObject::cast(this)->GetIsolate();
- Context* global_context = isolate->context()->global_context();
- return CreateJSValue(global_context->string_function(), this);
+ Context* native_context = isolate->context()->native_context();
+ return CreateJSValue(native_context->string_function(), this);
}
// Throw a type error.
@@ -134,13 +134,16 @@ void Object::Lookup(String* name, LookupResult* result) {
if (IsJSReceiver()) {
holder = this;
} else {
- Context* global_context = Isolate::Current()->context()->global_context();
+ Context* native_context = Isolate::Current()->context()->native_context();
if (IsNumber()) {
- holder = global_context->number_function()->instance_prototype();
+ holder = native_context->number_function()->instance_prototype();
} else if (IsString()) {
- holder = global_context->string_function()->instance_prototype();
+ holder = native_context->string_function()->instance_prototype();
} else if (IsBoolean()) {
- holder = global_context->boolean_function()->instance_prototype();
+ holder = native_context->boolean_function()->instance_prototype();
+ } else {
+ Isolate::Current()->PushStackTraceAndDie(
+ 0xDEAD0000, this, JSReceiver::cast(this)->map(), 0xDEAD0001);
}
}
ASSERT(holder != NULL); // Cannot handle null or undefined.
@@ -190,6 +193,7 @@ MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver,
}
Object* fun_obj = data->getter();
v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
+ if (call_fun == NULL) return isolate->heap()->undefined_value();
HandleScope scope(isolate);
JSObject* self = JSObject::cast(receiver);
Handle<String> key(name);
@@ -206,7 +210,9 @@ MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver,
if (result.IsEmpty()) {
return isolate->heap()->undefined_value();
}
- return *v8::Utils::OpenHandle(*result);
+ Object* return_value = *v8::Utils::OpenHandle(*result);
+ return_value->VerifyApiCallResultType();
+ return return_value;
}
// __defineGetter__ callback
@@ -406,16 +412,16 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
} else {
result->holder()->LocalLookupRealNamedProperty(name, &r);
}
- if (r.IsProperty()) {
- return GetPropertyAttributeWithFailedAccessCheck(receiver,
- &r,
- name,
- continue_search);
- }
- break;
+ if (!r.IsFound()) break;
+ return GetPropertyAttributeWithFailedAccessCheck(receiver,
+ &r,
+ name,
+ continue_search);
}
- default:
+ case HANDLER:
+ case TRANSITION:
+ case NONEXISTENT:
UNREACHABLE();
}
}
@@ -481,10 +487,21 @@ MaybeObject* JSObject::SetNormalizedProperty(String* name,
set_properties(StringDictionary::cast(dict));
return value;
}
- // Preserve enumeration index.
- details = PropertyDetails(details.attributes(),
- details.type(),
- property_dictionary()->DetailsAt(entry).index());
+
+ PropertyDetails original_details = property_dictionary()->DetailsAt(entry);
+ int enumeration_index;
+ // Preserve the enumeration index unless the property was deleted.
+ if (original_details.IsDeleted()) {
+ enumeration_index = property_dictionary()->NextEnumerationIndex();
+ property_dictionary()->SetNextEnumerationIndex(enumeration_index + 1);
+ } else {
+ enumeration_index = original_details.dictionary_index();
+ ASSERT(enumeration_index > 0);
+ }
+
+ details = PropertyDetails(
+ details.attributes(), details.type(), enumeration_index);
+
if (IsGlobalObject()) {
JSGlobalPropertyCell* cell =
JSGlobalPropertyCell::cast(property_dictionary()->ValueAt(entry));
@@ -512,11 +529,12 @@ MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) {
// map change to invalidate any ICs that think they can load
// from the DontDelete cell without checking if it contains
// the hole value.
- Object* new_map;
- { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
- if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
- }
- set_map(Map::cast(new_map));
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ ASSERT(new_map->is_dictionary_map());
+ set_map(new_map);
}
JSGlobalPropertyCell* cell =
JSGlobalPropertyCell::cast(dictionary->ValueAt(entry));
@@ -638,9 +656,9 @@ MaybeObject* Object::GetProperty(Object* receiver,
return result->holder()->GetPropertyWithInterceptor(
recvr, name, attributes);
}
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
break;
}
UNREACHABLE();
@@ -661,13 +679,13 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
holder = holder->GetPrototype()) {
if (!holder->IsJSObject()) {
Isolate* isolate = heap->isolate();
- Context* global_context = isolate->context()->global_context();
+ Context* native_context = isolate->context()->native_context();
if (holder->IsNumber()) {
- holder = global_context->number_function()->instance_prototype();
+ holder = native_context->number_function()->instance_prototype();
} else if (holder->IsString()) {
- holder = global_context->string_function()->instance_prototype();
+ holder = native_context->string_function()->instance_prototype();
} else if (holder->IsBoolean()) {
- holder = global_context->boolean_function()->instance_prototype();
+ holder = native_context->boolean_function()->instance_prototype();
} else if (holder->IsJSProxy()) {
return JSProxy::cast(holder)->GetElementWithHandler(receiver, index);
} else {
@@ -709,7 +727,7 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
Object* Object::GetPrototype() {
if (IsSmi()) {
Heap* heap = Isolate::Current()->heap();
- Context* context = heap->isolate()->context()->global_context();
+ Context* context = heap->isolate()->context()->native_context();
return context->number_function()->instance_prototype();
}
@@ -721,7 +739,7 @@ Object* Object::GetPrototype() {
return heap_object->map()->prototype();
}
Heap* heap = heap_object->GetHeap();
- Context* context = heap->isolate()->context()->global_context();
+ Context* context = heap->isolate()->context()->native_context();
if (heap_object->IsHeapNumber()) {
return context->number_function()->instance_prototype();
@@ -763,7 +781,6 @@ MaybeObject* Object::GetHash(CreationFlag flag) {
bool Object::SameValue(Object* other) {
if (other == this) return true;
- if (!IsHeapObject() || !other->IsHeapObject()) return false;
// The object is either a number, a string, an odd-ball,
// a real JS object, or a Harmony proxy.
@@ -1399,8 +1416,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
case EXTERNAL_DOUBLE_ARRAY_TYPE:
break;
case SHARED_FUNCTION_INFO_TYPE: {
- SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(this);
- shared->SharedFunctionInfoIterateBody(v);
+ SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
break;
}
@@ -1488,20 +1504,19 @@ String* JSReceiver::constructor_name() {
MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
String* name,
- Object* value) {
- int index = new_map->PropertyIndexFor(name);
+ Object* value,
+ int field_index) {
if (map()->unused_property_fields() == 0) {
- ASSERT(map()->unused_property_fields() == 0);
int new_unused = new_map->unused_property_fields();
- Object* values;
+ FixedArray* values;
{ MaybeObject* maybe_values =
properties()->CopySize(properties()->length() + new_unused + 1);
- if (!maybe_values->ToObject(&values)) return maybe_values;
+ if (!maybe_values->To(&values)) return maybe_values;
}
- set_properties(FixedArray::cast(values));
+ set_properties(values);
}
set_map(new_map);
- return FastPropertyAtPut(index, value);
+ return FastPropertyAtPut(field_index, value);
}
@@ -1526,93 +1541,62 @@ MaybeObject* JSObject::AddFastProperty(String* name,
PropertyAttributes attributes,
StoreFromKeyed store_mode) {
ASSERT(!IsJSGlobalProxy());
+ ASSERT(map()->instance_descriptors()->Search(name) ==
+ DescriptorArray::kNotFound);
// Normalize the object if the name is an actual string (not the
// hidden symbols) and is not a real identifier.
+ // Normalize the object if it will have too many fast properties.
Isolate* isolate = GetHeap()->isolate();
StringInputBuffer buffer(name);
- if (!IsIdentifier(isolate->unicode_cache(), &buffer)
- && name != isolate->heap()->hidden_symbol()) {
+ if ((!IsIdentifier(isolate->unicode_cache(), &buffer)
+ && name != isolate->heap()->hidden_symbol()) ||
+ (map()->unused_property_fields() == 0 &&
+ TooManyFastProperties(properties()->length(), store_mode))) {
Object* obj;
- { MaybeObject* maybe_obj =
- NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
+ MaybeObject* maybe_obj =
+ NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+
return AddSlowProperty(name, value, attributes);
}
- DescriptorArray* old_descriptors = map()->instance_descriptors();
// Compute the new index for new field.
int index = map()->NextFreePropertyIndex();
// Allocate new instance descriptors with (name, index) added
- FieldDescriptor new_field(name, index, attributes);
- Object* new_descriptors;
- { MaybeObject* maybe_new_descriptors =
- old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
- return maybe_new_descriptors;
- }
- }
-
- // Only allow map transition if the object isn't the global object and there
- // is not a transition for the name, or there's a transition for the name but
- // it's unrelated to properties.
- int descriptor_index = old_descriptors->Search(name);
-
- // Element transitions are stored in the descriptor for property "", which is
- // not a identifier and should have forced a switch to slow properties above.
- bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound;
- bool allow_map_transition =
- can_insert_transition &&
- (isolate->context()->global_context()->object_function()->map() != map());
+ FieldDescriptor new_field(name, index, attributes, 0);
ASSERT(index < map()->inobject_properties() ||
(index - map()->inobject_properties()) < properties()->length() ||
map()->unused_property_fields() == 0);
- // Allocate a new map for the object.
- Object* r;
- { MaybeObject* maybe_r = map()->CopyDropDescriptors();
- if (!maybe_r->ToObject(&r)) return maybe_r;
- }
- Map* new_map = Map::cast(r);
- if (allow_map_transition) {
- // Allocate new instance descriptors for the old map with map transition.
- MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
- Object* r;
- { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
- if (!maybe_r->ToObject(&r)) return maybe_r;
- }
- old_descriptors = DescriptorArray::cast(r);
- }
+
+ FixedArray* values = NULL;
if (map()->unused_property_fields() == 0) {
- if (TooManyFastProperties(properties()->length(), store_mode)) {
- Object* obj;
- { MaybeObject* maybe_obj =
- NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
- return AddSlowProperty(name, value, attributes);
- }
// Make room for the new value
- Object* values;
- { MaybeObject* maybe_values =
- properties()->CopySize(properties()->length() + kFieldsAdded);
- if (!maybe_values->ToObject(&values)) return maybe_values;
- }
- set_properties(FixedArray::cast(values));
+ MaybeObject* maybe_values =
+ properties()->CopySize(properties()->length() + kFieldsAdded);
+ if (!maybe_values->To(&values)) return maybe_values;
+ }
+
+ // Only allow map transition if the object isn't the global object.
+ TransitionFlag flag = isolate->empty_object_map() != map()
+ ? INSERT_TRANSITION
+ : OMIT_TRANSITION;
+
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyAddDescriptor(&new_field, flag);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ if (map()->unused_property_fields() == 0) {
+ ASSERT(values != NULL);
+ set_properties(values);
new_map->set_unused_property_fields(kFieldsAdded - 1);
} else {
new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
}
- // We have now allocated all the necessary objects.
- // All the changes can be applied at once, so they are atomic.
- if (allow_map_transition) {
- map()->set_instance_descriptors(old_descriptors);
- }
- new_map->SetBackPointer(map());
- new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
+
set_map(new_map);
return FastPropertyAtPut(index, value);
}
@@ -1623,57 +1607,26 @@ MaybeObject* JSObject::AddConstantFunctionProperty(
JSFunction* function,
PropertyAttributes attributes) {
// Allocate new instance descriptors with (name, function) added
- ConstantFunctionDescriptor d(name, function, attributes);
- Object* new_descriptors;
- { MaybeObject* maybe_new_descriptors =
- map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
- return maybe_new_descriptors;
- }
- }
-
- // Allocate a new map for the object.
- Object* new_map;
- { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
- if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
- }
+ ConstantFunctionDescriptor d(name, function, attributes, 0);
- DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
- Map::cast(new_map)->set_instance_descriptors(descriptors);
- Map* old_map = map();
- set_map(Map::cast(new_map));
-
- // If the old map is the global object map (from new Object()),
- // then transitions are not added to it, so we are done.
Heap* heap = GetHeap();
- if (old_map == heap->isolate()->context()->global_context()->
- object_function()->map()) {
- return function;
- }
+ TransitionFlag flag =
+ // Do not add transitions to the empty object map (map of "new Object()"),
+ // nor to global objects.
+ (map() == heap->isolate()->empty_object_map() || IsGlobalObject() ||
+ // Don't add transitions to special properties with non-trivial
+ // attributes.
+ // TODO(verwaest): Once we support attribute changes, these transitions
+ // should be kept as well.
+ attributes != NONE)
+ ? OMIT_TRANSITION
+ : INSERT_TRANSITION;
- // Do not add CONSTANT_TRANSITIONS to global objects
- if (IsGlobalObject()) {
- return function;
- }
-
- // Add a CONSTANT_TRANSITION descriptor to the old map,
- // so future assignments to this property on other objects
- // of the same type will create a normal field, not a constant function.
- // Don't do this for special properties, with non-trival attributes.
- if (attributes != NONE) {
- return function;
- }
- ConstTransitionDescriptor mark(name, Map::cast(new_map));
- { MaybeObject* maybe_new_descriptors =
- old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
- // We have accomplished the main goal, so return success.
- return function;
- }
- }
- old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
- Map::cast(new_map)->SetBackPointer(old_map);
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyAddDescriptor(&d, flag);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ set_map(new_map);
return function;
}
@@ -1770,9 +1723,10 @@ MaybeObject* JSObject::SetPropertyPostInterceptor(
// Check local property, ignore interceptor.
LookupResult result(GetIsolate());
LocalLookupRealNamedProperty(name, &result);
+ if (!result.IsFound()) map()->LookupTransition(this, name, &result);
if (result.IsFound()) {
- // An existing property, a map transition or a null descriptor was
- // found. Use set property to handle all these cases.
+ // An existing property or a map transition was found. Use set property to
+ // handle all these cases.
return SetProperty(&result, name, value, attributes, strict_mode);
}
bool done = false;
@@ -1794,8 +1748,7 @@ MaybeObject* JSObject::ReplaceSlowProperty(String* name,
int new_enumeration_index = 0; // 0 means "Use the next available index."
if (old_index != -1) {
// All calls to ReplaceSlowProperty have had all transitions removed.
- ASSERT(!dictionary->ContainsTransition(old_index));
- new_enumeration_index = dictionary->DetailsAt(old_index).index();
+ new_enumeration_index = dictionary->DetailsAt(old_index).dictionary_index();
}
PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
@@ -1803,38 +1756,28 @@ MaybeObject* JSObject::ReplaceSlowProperty(String* name,
}
-MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
+MaybeObject* JSObject::ConvertTransitionToMapTransition(
+ int transition_index,
String* name,
Object* new_value,
PropertyAttributes attributes) {
Map* old_map = map();
Object* result;
- { MaybeObject* maybe_result =
- ConvertDescriptorToField(name, new_value, attributes);
- if (!maybe_result->ToObject(&result)) return maybe_result;
- }
- // If we get to this point we have succeeded - do not return failure
- // after this point. Later stuff is optional.
- if (!HasFastProperties()) {
- return result;
- }
- // Do not add transitions to the map of "new Object()".
- if (map() == GetIsolate()->context()->global_context()->
- object_function()->map()) {
- return result;
- }
- MapTransitionDescriptor transition(name,
- map(),
- attributes);
- Object* new_descriptors;
- { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
- CopyInsert(&transition, KEEP_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
- return result; // Yes, return _result_.
- }
- }
- old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
+ MaybeObject* maybe_result =
+ ConvertDescriptorToField(name, new_value, attributes);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ if (!HasFastProperties()) return result;
+
+ // 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());
+
+ // 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);
return result;
}
@@ -1846,57 +1789,36 @@ MaybeObject* JSObject::ConvertDescriptorToField(String* name,
if (map()->unused_property_fields() == 0 &&
TooManyFastProperties(properties()->length(), MAY_BE_STORE_FROM_KEYED)) {
Object* obj;
- { MaybeObject* maybe_obj =
- NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
+ MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
return ReplaceSlowProperty(name, new_value, attributes);
}
int index = map()->NextFreePropertyIndex();
- FieldDescriptor new_field(name, index, attributes);
- // Make a new DescriptorArray replacing an entry with FieldDescriptor.
- Object* descriptors_unchecked;
- { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
- CopyInsert(&new_field, REMOVE_TRANSITIONS);
- if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
- return maybe_descriptors_unchecked;
- }
- }
- DescriptorArray* new_descriptors =
- DescriptorArray::cast(descriptors_unchecked);
+ FieldDescriptor new_field(name, index, attributes, 0);
// Make a new map for the object.
- Object* new_map_unchecked;
- { MaybeObject* maybe_new_map_unchecked = map()->CopyDropDescriptors();
- if (!maybe_new_map_unchecked->ToObject(&new_map_unchecked)) {
- return maybe_new_map_unchecked;
- }
- }
- Map* new_map = Map::cast(new_map_unchecked);
- new_map->set_instance_descriptors(new_descriptors);
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyInsertDescriptor(&new_field,
+ OMIT_TRANSITION);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
// Make new properties array if necessary.
- FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
+ FixedArray* new_properties = NULL;
int new_unused_property_fields = map()->unused_property_fields() - 1;
if (map()->unused_property_fields() == 0) {
new_unused_property_fields = kFieldsAdded - 1;
- Object* new_properties_object;
- { MaybeObject* maybe_new_properties_object =
- properties()->CopySize(properties()->length() + kFieldsAdded);
- if (!maybe_new_properties_object->ToObject(&new_properties_object)) {
- return maybe_new_properties_object;
- }
- }
- new_properties = FixedArray::cast(new_properties_object);
+ MaybeObject* maybe_new_properties =
+ properties()->CopySize(properties()->length() + kFieldsAdded);
+ if (!maybe_new_properties->To(&new_properties)) return maybe_new_properties;
}
// Update pointers to commit changes.
// Object points to the new map.
new_map->set_unused_property_fields(new_unused_property_fields);
set_map(new_map);
- if (new_properties) {
- set_properties(FixedArray::cast(new_properties));
+ if (new_properties != NULL) {
+ set_properties(new_properties);
}
return FastPropertyAtPut(index, new_value);
}
@@ -1964,6 +1886,9 @@ MaybeObject* JSReceiver::SetProperty(String* name,
JSReceiver::StoreFromKeyed store_mode) {
LookupResult result(GetIsolate());
LocalLookup(name, &result);
+ if (!result.IsFound()) {
+ map()->LookupTransition(JSObject::cast(this), name, &result);
+ }
return SetProperty(&result, name, value, attributes, strict_mode, store_mode);
}
@@ -2152,9 +2077,9 @@ MaybeObject* JSObject::SetPropertyViaPrototypes(
return result.proxy()->SetPropertyViaPrototypesWithHandler(
this, name, value, attributes, strict_mode, done);
}
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
break;
}
}
@@ -2171,32 +2096,120 @@ MaybeObject* JSObject::SetPropertyViaPrototypes(
}
-void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
- DescriptorArray* descriptors = map()->instance_descriptors();
- int number = descriptors->SearchWithCache(name);
- if (number != DescriptorArray::kNotFound) {
- result->DescriptorResult(this, descriptors->GetDetails(number), number);
- } else {
- result->NotFound();
+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);
+ 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());
+ // For now this trick is only applied to fixed arrays in new and paged space.
+ ASSERT(!HEAP->lo_space()->Contains(elms));
-void Map::LookupInDescriptors(JSObject* holder,
- String* name,
- LookupResult* result) {
- DescriptorArray* descriptors = instance_descriptors();
- DescriptorLookupCache* cache =
- GetHeap()->isolate()->descriptor_lookup_cache();
- int number = cache->Lookup(descriptors, name);
- if (number == DescriptorLookupCache::kAbsent) {
- number = descriptors->Search(name);
- cache->Update(descriptors, name, number);
- }
- if (number != DescriptorArray::kNotFound) {
- result->DescriptorResult(holder, descriptors->GetDetails(number), number);
+ const int len = elms->length();
+
+ ASSERT(to_trim < len);
+
+ Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim);
+
+ if (trim_mode == FROM_GC) {
+#ifdef DEBUG
+ ZapEndOfFixedArray(new_end, to_trim);
+#endif
} else {
- result->NotFound();
+ ZapEndOfFixedArray(new_end, to_trim);
+ }
+
+ int size_delta = to_trim * kPointerSize;
+
+ // Technically in new space this write might be omitted (except for
+ // debug mode which iterates through the heap), but to play safer
+ // we still do it.
+ heap->CreateFillerObjectAt(new_end, size_delta);
+
+ elms->set_length(len - to_trim);
+
+ // Maintain marking consistency for IncrementalMarking.
+ if (Marking::IsBlack(Marking::MarkBitFrom(elms))) {
+ if (trim_mode == FROM_GC) {
+ MemoryChunk::IncrementLiveBytesFromGC(elms->address(), -size_delta);
+ } else {
+ MemoryChunk::IncrementLiveBytesFromMutator(elms->address(), -size_delta);
+ }
+ }
+}
+
+
+void Map::CopyAppendCallbackDescriptors(Handle<Map> map,
+ Handle<Object> descriptors) {
+ Isolate* isolate = map->GetIsolate();
+ Handle<DescriptorArray> array(map->instance_descriptors());
+ v8::NeanderArray callbacks(descriptors);
+ int nof_callbacks = callbacks.length();
+ int descriptor_count = array->number_of_descriptors();
+
+ // 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
+ // temporarily put the heap in an invalid state while appending descriptors.
+ for (int i = 0; i < nof_callbacks; ++i) {
+ Handle<AccessorInfo> entry(AccessorInfo::cast(callbacks.get(i)));
+ Handle<String> key =
+ isolate->factory()->SymbolFromString(
+ Handle<String>(String::cast(entry->name())));
+ 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);
+
+ // Fill in new callback descriptors. Process the callbacks from
+ // back to front so that the last callback with a given name takes
+ // precedence over previously added callbacks with that name.
+ for (int i = nof_callbacks - 1; i >= 0; i--) {
+ 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) {
+ CallbacksDescriptor desc(key, entry, entry->property_attributes());
+ map->AppendDescriptor(&desc, witness);
+ }
+ }
+
+ 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);
}
}
@@ -2250,21 +2263,16 @@ static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) {
ASSERT(index <= to_index);
for (; index < to_index; ++index) {
- Map* next_map = current_map->elements_transition_map();
- if (next_map == NULL) {
- return current_map;
- }
- current_map = next_map;
+ if (!current_map->HasElementsTransition()) return current_map;
+ current_map = current_map->elements_transition_map();
}
- if (!IsFastElementsKind(to_kind)) {
+ if (!IsFastElementsKind(to_kind) && current_map->HasElementsTransition()) {
Map* next_map = current_map->elements_transition_map();
- if (next_map != NULL && next_map->elements_kind() == to_kind) {
- return next_map;
- }
- ASSERT(current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND);
- } else {
- ASSERT(current_map->elements_kind() == to_kind);
+ if (next_map->elements_kind() == to_kind) return next_map;
}
+ ASSERT(IsFastElementsKind(to_kind)
+ ? current_map->elements_kind() == to_kind
+ : current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND);
return current_map;
}
@@ -2276,29 +2284,6 @@ Map* Map::LookupElementsTransitionMap(ElementsKind to_kind) {
}
-MaybeObject* Map::CreateNextElementsTransition(ElementsKind next_kind) {
- ASSERT(elements_transition_map() == NULL ||
- ((elements_transition_map()->elements_kind() == DICTIONARY_ELEMENTS ||
- IsExternalArrayElementsKind(
- elements_transition_map()->elements_kind())) &&
- (next_kind == DICTIONARY_ELEMENTS ||
- IsExternalArrayElementsKind(next_kind))));
- ASSERT(!IsFastElementsKind(next_kind) ||
- IsMoreGeneralElementsKindTransition(elements_kind(), next_kind));
- ASSERT(next_kind != elements_kind());
-
- Map* next_map;
- MaybeObject* maybe_next_map =
- this->CopyDropTransitions(DescriptorArray::CANNOT_BE_SHARED);
- if (!maybe_next_map->To(&next_map)) return maybe_next_map;
-
- next_map->set_elements_kind(next_kind);
- next_map->SetBackPointer(this);
- this->set_elements_transition_map(next_map);
- return next_map;
-}
-
-
static MaybeObject* AddMissingElementsTransitions(Map* map,
ElementsKind to_kind) {
ASSERT(IsFastElementsKind(map->elements_kind()));
@@ -2312,18 +2297,18 @@ static MaybeObject* AddMissingElementsTransitions(Map* map,
Map* current_map = map;
for (; index < to_index; ++index) {
- ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(index + 1);
- MaybeObject* maybe_next_map =
- current_map->CreateNextElementsTransition(next_kind);
- if (!maybe_next_map->To(&current_map)) return maybe_next_map;
+ ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(index + 1);
+ MaybeObject* maybe_next_map =
+ current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION);
+ if (!maybe_next_map->To(&current_map)) return maybe_next_map;
}
// In case we are exiting the fast elements kind system, just add the map in
// the end.
if (!IsFastElementsKind(to_kind)) {
- MaybeObject* maybe_next_map =
- current_map->CreateNextElementsTransition(to_kind);
- if (!maybe_next_map->To(&current_map)) return maybe_next_map;
+ MaybeObject* maybe_next_map =
+ current_map->CopyAsElementsKind(to_kind, INSERT_TRANSITION);
+ if (!maybe_next_map->To(&current_map)) return maybe_next_map;
}
ASSERT(current_map->elements_kind() == to_kind);
@@ -2340,22 +2325,6 @@ Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object,
}
-// If the map is using the empty descriptor array, install a new empty
-// descriptor array that will contain an element transition.
-// TODO(verwaest) Goes away once the descriptor array is immutable.
-static MaybeObject* EnsureMayContainTransitions(Map* map) {
- if (map->instance_descriptors()->MayContainTransitions()) return map;
- DescriptorArray* descriptor_array;
- MaybeObject* maybe_descriptor_array =
- DescriptorArray::Allocate(0, DescriptorArray::CANNOT_BE_SHARED);
- if (!maybe_descriptor_array->To(&descriptor_array)) {
- return maybe_descriptor_array;
- }
- map->set_instance_descriptors(descriptor_array);
- return map;
-}
-
-
MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
Map* start_map = map();
ElementsKind from_kind = start_map->elements_kind();
@@ -2364,12 +2333,11 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
return start_map;
}
- Context* global_context = GetIsolate()->context()->global_context();
bool allow_store_transition =
// Only remember the map transition if the object's map is NOT equal to
// the global object_function's map and there is not an already existing
// non-matching element transition.
- (global_context->object_function()->map() != map()) &&
+ (GetIsolate()->empty_object_map() != map()) &&
!start_map->IsUndefined() && !start_map->is_shared() &&
IsFastElementsKind(from_kind);
@@ -2381,16 +2349,9 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
}
if (!allow_store_transition) {
- // Create a new free-floating map only if we are not allowed to store it.
- Map* new_map = NULL;
- MaybeObject* maybe_new_map =
- start_map->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
- new_map->set_elements_kind(to_kind);
- return new_map;
+ return start_map->CopyAsElementsKind(to_kind, OMIT_TRANSITION);
}
- EnsureMayContainTransitions(start_map);
Map* closest_map = FindClosestElementsTransition(start_map, to_kind);
if (closest_map->elements_kind() == to_kind) {
@@ -2412,47 +2373,47 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
}
if (HasFastProperties()) {
- LookupInDescriptor(name, result);
- if (result->IsFound()) {
- // A property, a map transition or a null descriptor was found.
- // We return all of these result types because
- // LocalLookupRealNamedProperty is used when setting properties
- // where map transitions and null descriptors are handled.
- ASSERT(result->holder() == this && result->type() != NORMAL);
- // Disallow caching for uninitialized constants. These can only
- // occur as fields.
- if (result->IsReadOnly() && result->type() == FIELD &&
- FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
- result->DisallowCaching();
- }
- return;
+ map()->LookupDescriptor(this, name, result);
+ // A property or a map transition was found. We return all of these result
+ // types because LocalLookupRealNamedProperty is used when setting
+ // properties where map transitions are handled.
+ ASSERT(!result->IsFound() ||
+ (result->holder() == this && result->IsFastPropertyType()));
+ // Disallow caching for uninitialized constants. These can only
+ // occur as fields.
+ if (result->IsField() &&
+ result->IsReadOnly() &&
+ FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
+ result->DisallowCaching();
}
- } else {
- int entry = property_dictionary()->FindEntry(name);
- if (entry != StringDictionary::kNotFound) {
- Object* value = property_dictionary()->ValueAt(entry);
- if (IsGlobalObject()) {
- PropertyDetails d = property_dictionary()->DetailsAt(entry);
- if (d.IsDeleted()) {
- result->NotFound();
- return;
- }
- value = JSGlobalPropertyCell::cast(value)->value();
+ return;
+ }
+
+ int entry = property_dictionary()->FindEntry(name);
+ if (entry != StringDictionary::kNotFound) {
+ Object* value = property_dictionary()->ValueAt(entry);
+ if (IsGlobalObject()) {
+ PropertyDetails d = property_dictionary()->DetailsAt(entry);
+ if (d.IsDeleted()) {
+ result->NotFound();
+ return;
}
- // Make sure to disallow caching for uninitialized constants
- // found in the dictionary-mode objects.
- if (value->IsTheHole()) result->DisallowCaching();
- result->DictionaryResult(this, entry);
- return;
+ value = JSGlobalPropertyCell::cast(value)->value();
}
+ // Make sure to disallow caching for uninitialized constants
+ // found in the dictionary-mode objects.
+ if (value->IsTheHole()) result->DisallowCaching();
+ result->DictionaryResult(this, entry);
+ return;
}
+
result->NotFound();
}
void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
LocalLookupRealNamedProperty(name, result);
- if (result->IsProperty()) return;
+ if (result->IsFound()) return;
LookupRealNamedPropertyInPrototypes(name, result);
}
@@ -2468,8 +2429,8 @@ void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
return result->HandlerResult(JSProxy::cast(pt));
}
JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
- ASSERT(!(result->IsProperty() && result->type() == INTERCEPTOR));
- if (result->IsProperty()) return;
+ ASSERT(!(result->IsFound() && result->type() == INTERCEPTOR));
+ if (result->IsFound()) return;
}
result->NotFound();
}
@@ -2538,7 +2499,7 @@ MaybeObject* JSReceiver::SetProperty(LookupResult* result,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
JSReceiver::StoreFromKeyed store_mode) {
- if (result->IsFound() && result->type() == HANDLER) {
+ if (result->IsHandler()) {
return result->proxy()->SetPropertyWithHandler(
this, key, value, attributes, strict_mode);
} else {
@@ -2676,7 +2637,7 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
String* name_raw, DeleteMode mode) {
Isolate* isolate = GetIsolate();
HandleScope scope(isolate);
- Handle<Object> receiver(this);
+ Handle<JSProxy> receiver(this);
Handle<Object> name(name_raw);
Handle<Object> args[] = { name };
@@ -2686,8 +2647,9 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
Object* bool_result = result->ToBoolean();
if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) {
+ Handle<Object> handler(receiver->handler());
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete");
- Handle<Object> args[] = { Handle<Object>(handler()), trap_name };
+ Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
"handler_failed", HandleVector(args, ARRAY_SIZE(args)));
isolate->Throw(*error);
@@ -2790,7 +2752,7 @@ void JSProxy::Fix() {
Object* hash;
if (maybe_hash->To<Object>(&hash) && hash->IsSmi()) {
Handle<JSObject> new_self(JSObject::cast(*self));
- isolate->factory()->SetIdentityHash(new_self, hash);
+ isolate->factory()->SetIdentityHash(new_self, Smi::cast(hash));
}
}
@@ -2823,8 +2785,8 @@ MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(const char* name,
MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
- String* name,
- Object* value,
+ String* name_raw,
+ Object* value_raw,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
StoreFromKeyed store_mode) {
@@ -2836,114 +2798,127 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
// Optimization for 2-byte strings often used as keys in a decompression
// dictionary. We make these short keys into symbols to avoid constantly
// reallocating them.
- if (!name->IsSymbol() && name->length() <= 2) {
+ if (!name_raw->IsSymbol() && name_raw->length() <= 2) {
Object* symbol_version;
- { MaybeObject* maybe_symbol_version = heap->LookupSymbol(name);
+ { MaybeObject* maybe_symbol_version = heap->LookupSymbol(name_raw);
if (maybe_symbol_version->ToObject(&symbol_version)) {
- name = String::cast(symbol_version);
+ name_raw = String::cast(symbol_version);
}
}
}
// Check access rights if needed.
if (IsAccessCheckNeeded()) {
- if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
+ if (!heap->isolate()->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(
- result, name, value, true, strict_mode);
+ result, name_raw, value_raw, true, strict_mode);
}
}
if (IsJSGlobalProxy()) {
Object* proto = GetPrototype();
- if (proto->IsNull()) return value;
+ if (proto->IsNull()) return value_raw;
ASSERT(proto->IsJSGlobalObject());
return JSObject::cast(proto)->SetPropertyForResult(
- result, name, value, attributes, strict_mode, store_mode);
+ result, name_raw, value_raw, attributes, strict_mode, store_mode);
}
- if (!result->IsProperty() && !IsJSContextExtensionObject()) {
+ // From this point on everything needs to be handlified, because
+ // SetPropertyViaPrototypes might call back into JavaScript.
+ HandleScope scope(GetIsolate());
+ Handle<JSObject> self(this);
+ Handle<String> name(name_raw);
+ Handle<Object> value(value_raw);
+
+ if (!result->IsProperty() && !self->IsJSContextExtensionObject()) {
bool done = false;
- MaybeObject* result_object =
- SetPropertyViaPrototypes(name, value, attributes, strict_mode, &done);
+ MaybeObject* result_object = self->SetPropertyViaPrototypes(
+ *name, *value, attributes, strict_mode, &done);
if (done) return result_object;
}
if (!result->IsFound()) {
// Neither properties nor transitions found.
- return AddProperty(name, value, attributes, strict_mode, store_mode);
+ return self->AddProperty(
+ *name, *value, attributes, strict_mode, store_mode);
}
- if (result->IsReadOnly() && result->IsProperty()) {
+ if (result->IsProperty() && result->IsReadOnly()) {
if (strict_mode == kStrictMode) {
- Handle<JSObject> self(this);
- Handle<String> hname(name);
- Handle<Object> args[] = { hname, self };
+ Handle<Object> args[] = { name, self };
return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError(
"strict_read_only_property", HandleVector(args, ARRAY_SIZE(args))));
} else {
- return value;
+ return *value;
}
}
+
// 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()) {
case NORMAL:
- return SetNormalizedProperty(result, value);
+ return self->SetNormalizedProperty(result, *value);
case FIELD:
- return FastPropertyAtPut(result->GetFieldIndex(), value);
- case MAP_TRANSITION:
- if (attributes == result->GetAttributes()) {
- // Only use map transition if the attributes match.
- return AddFastPropertyUsingMap(result->GetTransitionMap(),
- name,
- value);
- }
- return ConvertDescriptorToField(name, value, attributes);
+ return self->FastPropertyAtPut(result->GetFieldIndex(), *value);
case CONSTANT_FUNCTION:
// Only replace the function if necessary.
- if (value == result->GetConstantFunction()) return value;
+ if (*value == result->GetConstantFunction()) return *value;
// Preserve the attributes of this existing property.
attributes = result->GetAttributes();
- return ConvertDescriptorToField(name, value, attributes);
+ return self->ConvertDescriptorToField(*name, *value, attributes);
case CALLBACKS: {
Object* callback_object = result->GetCallbackObject();
- if (callback_object->IsAccessorPair() &&
- !AccessorPair::cast(callback_object)->ContainsAccessor()) {
- return ConvertDescriptorToField(name, value, attributes);
- }
- return SetPropertyWithCallback(callback_object,
- name,
- value,
- result->holder(),
- strict_mode);
+ return self->SetPropertyWithCallback(callback_object,
+ *name,
+ *value,
+ result->holder(),
+ strict_mode);
}
case INTERCEPTOR:
- return SetPropertyWithInterceptor(name, value, attributes, strict_mode);
- case CONSTANT_TRANSITION: {
+ return self->SetPropertyWithInterceptor(*name,
+ *value,
+ attributes,
+ strict_mode);
+ case TRANSITION: {
+ Map* transition_map = result->GetTransitionTarget();
+ int descriptor = transition_map->LastAdded();
+
+ DescriptorArray* descriptors = transition_map->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+
+ if (details.type() == FIELD) {
+ if (attributes == details.attributes()) {
+ int field_index = descriptors->GetFieldIndex(descriptor);
+ return self->AddFastPropertyUsingMap(transition_map,
+ *name,
+ *value,
+ field_index);
+ }
+ 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.
- Map* target_map = result->GetTransitionMap();
- DescriptorArray* target_descriptors = target_map->instance_descriptors();
- int number = target_descriptors->SearchWithCache(name);
- ASSERT(number != DescriptorArray::kNotFound);
- ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
- JSFunction* function =
- JSFunction::cast(target_descriptors->GetValue(number));
- if (value == function) {
- set_map(target_map);
- return value;
+ if (constant_function == *value) {
+ self->set_map(transition_map);
+ return constant_function;
}
- // Otherwise, replace with a MAP_TRANSITION to a new map with a
- // FIELD, even if the value is a constant function.
- return ConvertDescriptorToFieldAndMapTransition(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);
}
- case NULL_DESCRIPTOR:
- return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case HANDLER:
+ case NONEXISTENT:
UNREACHABLE();
- return value;
+ return *value;
}
UNREACHABLE(); // keep the compiler happy
- return value;
+ return *value;
}
@@ -2978,6 +2953,7 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
Isolate* isolate = GetIsolate();
LookupResult result(isolate);
LocalLookup(name, &result);
+ if (!result.IsFound()) map()->LookupTransition(this, name, &result);
// Check access rights if needed.
if (IsAccessCheckNeeded()) {
if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
@@ -3005,22 +2981,14 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
return AddProperty(name, value, attributes, kNonStrictMode);
}
- PropertyDetails details = PropertyDetails(attributes, NORMAL);
-
// Check of IsReadOnly removed from here in clone.
switch (result.type()) {
- case NORMAL:
+ case NORMAL: {
+ PropertyDetails details = PropertyDetails(attributes, NORMAL);
return SetNormalizedProperty(name, value, details);
+ }
case FIELD:
return FastPropertyAtPut(result.GetFieldIndex(), value);
- case MAP_TRANSITION:
- if (attributes == result.GetAttributes()) {
- // Only use map transition if the attributes match.
- return AddFastPropertyUsingMap(result.GetTransitionMap(),
- name,
- value);
- }
- return ConvertDescriptorToField(name, value, attributes);
case CONSTANT_FUNCTION:
// Only replace the function if necessary.
if (value == result.GetConstantFunction()) return value;
@@ -3031,12 +2999,35 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
case INTERCEPTOR:
// Override callback in clone
return ConvertDescriptorToField(name, value, attributes);
- case CONSTANT_TRANSITION:
- // Replace with a MAP_TRANSITION to a new map with a FIELD, even
- // if the value is a function.
- case NULL_DESCRIPTOR:
- return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
+ case TRANSITION: {
+ Map* transition_map = result.GetTransitionTarget();
+ int descriptor = transition_map->LastAdded();
+
+ DescriptorArray* descriptors = transition_map->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+
+ if (details.type() == FIELD) {
+ if (attributes == details.attributes()) {
+ int field_index = descriptors->GetFieldIndex(descriptor);
+ return AddFastPropertyUsingMap(transition_map,
+ name,
+ value,
+ field_index);
+ }
+ return ConvertDescriptorToField(name, value, attributes);
+ } else if (details.type() == CALLBACKS) {
+ return ConvertDescriptorToField(name, value, attributes);
+ }
+
+ 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);
+ }
case HANDLER:
+ case NONEXISTENT:
UNREACHABLE();
}
UNREACHABLE(); // keep the compiler happy
@@ -3051,7 +3042,7 @@ PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
// Check local property, ignore interceptor.
LookupResult result(GetIsolate());
LocalLookupRealNamedProperty(name, &result);
- if (result.IsProperty()) return result.GetAttributes();
+ if (result.IsFound()) return result.GetAttributes();
if (continue_search) {
// Continue searching via the prototype chain.
@@ -3144,7 +3135,7 @@ PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
receiver, result, name, continue_search);
}
}
- if (result->IsProperty()) {
+ if (result->IsFound()) {
switch (result->type()) {
case NORMAL: // fall through
case FIELD:
@@ -3158,7 +3149,8 @@ PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
case INTERCEPTOR:
return result->holder()->GetPropertyAttributeWithInterceptor(
JSObject::cast(receiver), name, continue_search);
- default:
+ case TRANSITION:
+ case NONEXISTENT:
UNREACHABLE();
}
}
@@ -3197,17 +3189,16 @@ MaybeObject* NormalizedMapCache::Get(JSObject* obj,
// except for the code cache, which can contain some ics which can be
// applied to the shared map.
Object* fresh;
- { MaybeObject* maybe_fresh =
- fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
- if (maybe_fresh->ToObject(&fresh)) {
- ASSERT(memcmp(Map::cast(fresh)->address(),
- Map::cast(result)->address(),
- Map::kCodeCacheOffset) == 0);
- int offset = Map::kCodeCacheOffset + kPointerSize;
- ASSERT(memcmp(Map::cast(fresh)->address() + offset,
- Map::cast(result)->address() + offset,
- Map::kSize - offset) == 0);
- }
+ MaybeObject* maybe_fresh =
+ fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
+ if (maybe_fresh->ToObject(&fresh)) {
+ ASSERT(memcmp(Map::cast(fresh)->address(),
+ Map::cast(result)->address(),
+ Map::kCodeCacheOffset) == 0);
+ int offset = Map::kCodeCacheOffset + kPointerSize;
+ ASSERT(memcmp(Map::cast(fresh)->address() + offset,
+ Map::cast(result)->address() + offset,
+ Map::kSize - offset) == 0);
}
}
#endif
@@ -3218,6 +3209,7 @@ MaybeObject* NormalizedMapCache::Get(JSObject* obj,
fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
if (!maybe_result->ToObject(&result)) return maybe_result;
}
+ ASSERT(Map::cast(result)->is_dictionary_map());
set(index, result);
isolate->counters()->normalized_maps()->Increment();
@@ -3297,8 +3289,9 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
PropertyDetails details = descs->GetDetails(i);
switch (details.type()) {
case CONSTANT_FUNCTION: {
- PropertyDetails d =
- PropertyDetails(details.attributes(), NORMAL, details.index());
+ PropertyDetails d = PropertyDetails(details.attributes(),
+ NORMAL,
+ details.descriptor_index());
Object* value = descs->GetConstantFunction(i);
MaybeObject* maybe_dictionary =
dictionary->Add(descs->GetKey(i), value, d);
@@ -3306,8 +3299,9 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
break;
}
case FIELD: {
- PropertyDetails d =
- PropertyDetails(details.attributes(), NORMAL, details.index());
+ PropertyDetails d = PropertyDetails(details.attributes(),
+ NORMAL,
+ details.descriptor_index());
Object* value = FastPropertyAt(descs->GetFieldIndex(i));
MaybeObject* maybe_dictionary =
dictionary->Add(descs->GetKey(i), value, d);
@@ -3315,25 +3309,19 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
break;
}
case CALLBACKS: {
- if (!descs->IsProperty(i)) break;
Object* value = descs->GetCallbacksObject(i);
- if (value->IsAccessorPair()) {
- MaybeObject* maybe_copy =
- AccessorPair::cast(value)->CopyWithoutTransitions();
- if (!maybe_copy->To(&value)) return maybe_copy;
- }
+ details = details.set_pointer(0);
MaybeObject* maybe_dictionary =
dictionary->Add(descs->GetKey(i), value, details);
if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
break;
}
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
case INTERCEPTOR:
break;
case HANDLER:
case NORMAL:
+ case TRANSITION:
+ case NONEXISTENT:
UNREACHABLE();
break;
}
@@ -3346,11 +3334,11 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
dictionary->SetNextEnumerationIndex(index);
Map* new_map;
- { MaybeObject* maybe_map =
- current_heap->isolate()->context()->global_context()->
- normalized_map_cache()->Get(this, mode);
- if (!maybe_map->To(&new_map)) return maybe_map;
- }
+ MaybeObject* maybe_map =
+ current_heap->isolate()->context()->native_context()->
+ normalized_map_cache()->Get(this, mode);
+ if (!maybe_map->To(&new_map)) return maybe_map;
+ ASSERT(new_map->is_dictionary_map());
// We have now successfully allocated all the necessary objects.
// Changes can now be made with the guarantee that all of them take effect.
@@ -3366,9 +3354,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
-instance_size_delta);
}
-
set_map(new_map);
- new_map->clear_instance_descriptors();
set_properties(dictionary);
@@ -3515,7 +3501,7 @@ Smi* JSReceiver::GenerateIdentityHash() {
}
-MaybeObject* JSObject::SetIdentityHash(Object* hash, CreationFlag flag) {
+MaybeObject* JSObject::SetIdentityHash(Smi* hash, CreationFlag flag) {
MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_symbol(),
hash);
if (maybe->IsFailure()) return maybe;
@@ -3561,6 +3547,7 @@ MaybeObject* JSProxy::GetIdentityHash(CreationFlag flag) {
Object* JSObject::GetHiddenProperty(String* key) {
+ ASSERT(key->IsSymbol());
if (IsJSGlobalProxy()) {
// For a proxy, use the prototype as target object.
Object* proxy_parent = GetPrototype();
@@ -3570,22 +3557,32 @@ Object* JSObject::GetHiddenProperty(String* key) {
return JSObject::cast(proxy_parent)->GetHiddenProperty(key);
}
ASSERT(!IsJSGlobalProxy());
- MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false);
+ 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 GetHeap()->undefined_value();
+ Object* inline_value = hidden_lookup->ToObjectUnchecked();
+
+ if (inline_value->IsSmi()) {
+ // Handle inline-stored identity hash.
+ if (key == GetHeap()->identity_hash_symbol()) {
+ return inline_value;
+ } else {
+ return GetHeap()->undefined_value();
+ }
}
- StringDictionary* dictionary =
- StringDictionary::cast(hidden_lookup->ToObjectUnchecked());
- int entry = dictionary->FindEntry(key);
- if (entry == StringDictionary::kNotFound) return GetHeap()->undefined_value();
- return dictionary->ValueAt(entry);
+
+ if (inline_value->IsUndefined()) return GetHeap()->undefined_value();
+
+ ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value);
+ Object* entry = hashtable->Lookup(key);
+ if (entry->IsTheHole()) return GetHeap()->undefined_value();
+ return entry;
}
Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj,
- Handle<String> key,
- Handle<Object> value) {
+ Handle<String> key,
+ Handle<Object> value) {
CALL_HEAP_FUNCTION(obj->GetIsolate(),
obj->SetHiddenProperty(*key, *value),
Object);
@@ -3593,6 +3590,7 @@ Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj,
MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) {
+ ASSERT(key->IsSymbol());
if (IsJSGlobalProxy()) {
// For a proxy, use the prototype as target object.
Object* proxy_parent = GetPrototype();
@@ -3602,27 +3600,31 @@ MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) {
return JSObject::cast(proxy_parent)->SetHiddenProperty(key, value);
}
ASSERT(!IsJSGlobalProxy());
- MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(true);
- StringDictionary* dictionary;
- if (!hidden_lookup->To<StringDictionary>(&dictionary)) return hidden_lookup;
- // If it was found, check if the key is already in the dictionary.
- int entry = dictionary->FindEntry(key);
- if (entry != StringDictionary::kNotFound) {
- // If key was found, just update the value.
- dictionary->ValueAtPut(entry, value);
- return this;
+ // 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 (value->IsSmi() &&
+ key == GetHeap()->identity_hash_symbol() &&
+ (inline_value->IsUndefined() || inline_value->IsSmi())) {
+ return SetHiddenPropertiesHashTable(value);
}
- // Key was not already in the dictionary, so add the entry.
- MaybeObject* insert_result = dictionary->Add(key,
- value,
- PropertyDetails(NONE, NORMAL));
- StringDictionary* new_dict;
- if (!insert_result->To<StringDictionary>(&new_dict)) return insert_result;
- if (new_dict != dictionary) {
+
+ hidden_lookup = GetHiddenPropertiesHashTable(CREATE_NEW_IF_ABSENT);
+ ObjectHashTable* hashtable;
+ if (!hidden_lookup->To(&hashtable)) return hidden_lookup;
+
+ // If it was found, check if the key is already in the dictionary.
+ MaybeObject* insert_result = hashtable->Put(key, value);
+ ObjectHashTable* new_table;
+ if (!insert_result->To(&new_table)) return insert_result;
+ if (new_table != hashtable) {
// If adding the key expanded the dictionary (i.e., Add returned a new
// dictionary), store it back to the object.
- MaybeObject* store_result = SetHiddenPropertiesDictionary(new_dict);
+ MaybeObject* store_result = SetHiddenPropertiesHashTable(new_table);
if (store_result->IsFailure()) return store_result;
}
// Return this to mark success.
@@ -3631,6 +3633,7 @@ MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) {
void JSObject::DeleteHiddenProperty(String* key) {
+ ASSERT(key->IsSymbol());
if (IsJSGlobalProxy()) {
// For a proxy, use the prototype as target object.
Object* proxy_parent = GetPrototype();
@@ -3640,18 +3643,18 @@ void JSObject::DeleteHiddenProperty(String* key) {
JSObject::cast(proxy_parent)->DeleteHiddenProperty(key);
return;
}
- MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false);
+ 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;
- StringDictionary* dictionary =
- StringDictionary::cast(hidden_lookup->ToObjectUnchecked());
- int entry = dictionary->FindEntry(key);
- if (entry == StringDictionary::kNotFound) {
- // Key wasn't in dictionary. Deletion is a success.
- return;
- }
- // Key was in the dictionary. Remove it.
- dictionary->DeleteProperty(entry, JSReceiver::FORCE_DELETION);
+ // We never delete (inline-stored) identity hashes.
+ ASSERT(!hidden_lookup->ToObjectUnchecked()->IsSmi());
+
+ ObjectHashTable* hashtable =
+ ObjectHashTable::cast(hidden_lookup->ToObjectUnchecked());
+ MaybeObject* delete_result = hashtable->Put(key, GetHeap()->the_hole_value());
+ USE(delete_result);
+ ASSERT(!delete_result->IsFailure()); // Delete does not cause GC.
}
@@ -3662,78 +3665,97 @@ bool JSObject::HasHiddenProperties() {
}
-MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) {
+MaybeObject* JSObject::GetHiddenPropertiesHashTable(
+ InitializeHiddenProperties init_option) {
ASSERT(!IsJSGlobalProxy());
+ Object* inline_value;
if (HasFastProperties()) {
// If the object has fast properties, check whether the first slot
// in the descriptor array matches the hidden symbol. Since the
// hidden symbols hash code is zero (and no other string has hash
// code zero) it will always occupy the first entry if present.
DescriptorArray* descriptors = this->map()->instance_descriptors();
- if ((descriptors->number_of_descriptors() > 0) &&
- (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) {
- if (descriptors->GetType(0) == FIELD) {
- Object* hidden_store =
- this->FastPropertyAt(descriptors->GetFieldIndex(0));
- return StringDictionary::cast(hidden_store);
+ if (descriptors->number_of_descriptors() > 0) {
+ int sorted_index = descriptors->GetSortedKeyIndex(0);
+ if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol()) {
+ ASSERT(descriptors->GetType(sorted_index) == FIELD);
+ inline_value = this->FastPropertyAt(
+ descriptors->GetFieldIndex(sorted_index));
} else {
- ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR ||
- descriptors->GetType(0) == MAP_TRANSITION);
+ inline_value = GetHeap()->undefined_value();
}
+ } else {
+ inline_value = GetHeap()->undefined_value();
}
} else {
PropertyAttributes attributes;
// You can't install a getter on a property indexed by the hidden symbol,
// so we can be sure that GetLocalPropertyPostInterceptor returns a real
// object.
- Object* lookup =
+ inline_value =
GetLocalPropertyPostInterceptor(this,
GetHeap()->hidden_symbol(),
&attributes)->ToObjectUnchecked();
- if (!lookup->IsUndefined()) {
- return StringDictionary::cast(lookup);
- }
}
- if (!create_if_absent) return GetHeap()->undefined_value();
- const int kInitialSize = 5;
- MaybeObject* dict_alloc = StringDictionary::Allocate(kInitialSize);
- StringDictionary* dictionary;
- if (!dict_alloc->To<StringDictionary>(&dictionary)) return dict_alloc;
+
+ if (init_option == ONLY_RETURN_INLINE_VALUE ||
+ inline_value->IsHashTable()) {
+ return inline_value;
+ }
+
+ ObjectHashTable* hashtable;
+ static const int kInitialCapacity = 4;
+ MaybeObject* maybe_obj =
+ ObjectHashTable::Allocate(kInitialCapacity,
+ ObjectHashTable::USE_CUSTOM_MINIMUM_CAPACITY);
+ if (!maybe_obj->To<ObjectHashTable>(&hashtable)) return maybe_obj;
+
+ if (inline_value->IsSmi()) {
+ // We were storing the identity hash inline and now allocated an actual
+ // dictionary. Put the identity hash into the new dictionary.
+ MaybeObject* insert_result =
+ hashtable->Put(GetHeap()->identity_hash_symbol(), inline_value);
+ ObjectHashTable* new_table;
+ if (!insert_result->To(&new_table)) return insert_result;
+ // We expect no resizing for the first insert.
+ ASSERT_EQ(hashtable, new_table);
+ }
+
MaybeObject* store_result =
SetPropertyPostInterceptor(GetHeap()->hidden_symbol(),
- dictionary,
+ hashtable,
DONT_ENUM,
kNonStrictMode,
OMIT_EXTENSIBILITY_CHECK);
if (store_result->IsFailure()) return store_result;
- return dictionary;
+ return hashtable;
}
-MaybeObject* JSObject::SetHiddenPropertiesDictionary(
- StringDictionary* dictionary) {
+MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) {
ASSERT(!IsJSGlobalProxy());
- ASSERT(HasHiddenProperties());
+ // We can store the identity hash inline iff there is no backing store
+ // for hidden properties yet.
+ ASSERT(HasHiddenProperties() != value->IsSmi());
if (HasFastProperties()) {
// If the object has fast properties, check whether the first slot
// in the descriptor array matches the hidden symbol. Since the
// hidden symbols hash code is zero (and no other string has hash
// code zero) it will always occupy the first entry if present.
DescriptorArray* descriptors = this->map()->instance_descriptors();
- if ((descriptors->number_of_descriptors() > 0) &&
- (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) {
- if (descriptors->GetType(0) == FIELD) {
- this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary);
+ if (descriptors->number_of_descriptors() > 0) {
+ int sorted_index = descriptors->GetSortedKeyIndex(0);
+ if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol()) {
+ ASSERT(descriptors->GetType(sorted_index) == FIELD);
+ this->FastPropertyAtPut(descriptors->GetFieldIndex(sorted_index),
+ value);
return this;
- } else {
- ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR ||
- descriptors->GetType(0) == MAP_TRANSITION);
}
}
}
MaybeObject* store_result =
SetPropertyPostInterceptor(GetHeap()->hidden_symbol(),
- dictionary,
+ value,
DONT_ENUM,
kNonStrictMode,
OMIT_EXTENSIBILITY_CHECK);
@@ -3747,7 +3769,7 @@ MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
// Check local property, ignore interceptor.
LookupResult result(GetIsolate());
LocalLookupRealNamedProperty(name, &result);
- if (!result.IsProperty()) return GetHeap()->true_value();
+ if (!result.IsFound()) return GetHeap()->true_value();
// Normalize object if needed.
Object* obj;
@@ -3781,7 +3803,9 @@ MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) {
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
if (!result.IsEmpty()) {
ASSERT(result->IsBoolean());
- return *v8::Utils::OpenHandle(*result);
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
}
}
MaybeObject* raw_result =
@@ -3816,7 +3840,9 @@ MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
if (!result.IsEmpty()) {
ASSERT(result->IsBoolean());
- return *v8::Utils::OpenHandle(*result);
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
}
MaybeObject* raw_result = this_handle->GetElementsAccessor()->Delete(
*this_handle,
@@ -3896,7 +3922,7 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
} else {
LookupResult result(isolate);
LocalLookup(name, &result);
- if (!result.IsProperty()) return isolate->heap()->true_value();
+ if (!result.IsFound()) return isolate->heap()->true_value();
// Ignore attributes if forcing a deletion.
if (result.IsDontDelete() && mode != FORCE_DELETION) {
if (mode == STRICT_DELETION) {
@@ -3909,7 +3935,7 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
return isolate->heap()->false_value();
}
// Check for interceptor.
- if (result.type() == INTERCEPTOR) {
+ if (result.IsInterceptor()) {
// Skip interceptor if forcing a deletion.
if (mode == FORCE_DELETION) {
return DeletePropertyPostInterceptor(name, mode);
@@ -4036,15 +4062,15 @@ bool JSObject::ReferencesObject(Object* obj) {
if (IsJSFunction()) {
// Get the constructor function for arguments array.
JSObject* arguments_boilerplate =
- heap->isolate()->context()->global_context()->
+ heap->isolate()->context()->native_context()->
arguments_boilerplate();
JSFunction* arguments_function =
JSFunction::cast(arguments_boilerplate->map()->constructor());
- // Get the context and don't check if it is the global context.
+ // Get the context and don't check if it is the native context.
JSFunction* f = JSFunction::cast(this);
Context* context = f->context();
- if (context->IsGlobalContext()) {
+ if (context->IsNativeContext()) {
return false;
}
@@ -4120,10 +4146,9 @@ MaybeObject* JSObject::PreventExtensions() {
// Do a map transition, other objects with this map may still
// be extensible.
Map* new_map;
- { MaybeObject* maybe =
- map()->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
- if (!maybe->To<Map>(&new_map)) return maybe;
- }
+ MaybeObject* maybe = map()->Copy();
+ if (!maybe->To(&new_map)) return maybe;
+
new_map->set_is_extensible(false);
set_map(new_map);
ASSERT(!map()->is_extensible());
@@ -4143,27 +4168,31 @@ bool JSReceiver::IsSimpleEnum() {
o = JSObject::cast(o)->GetPrototype()) {
if (!o->IsJSObject()) return false;
JSObject* curr = JSObject::cast(o);
- if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
+ int enum_length = curr->map()->EnumLength();
+ if (enum_length == Map::kInvalidEnumCache) return false;
ASSERT(!curr->HasNamedInterceptor());
ASSERT(!curr->HasIndexedInterceptor());
ASSERT(!curr->IsAccessCheckNeeded());
if (curr->NumberOfEnumElements() > 0) return false;
- if (curr != this) {
- FixedArray* curr_fixed_array =
- FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
- if (curr_fixed_array->length() > 0) return false;
- }
+ if (curr != this && enum_length != 0) return false;
}
return true;
}
+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 result = 0;
DescriptorArray* descs = instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
PropertyDetails details = descs->GetDetails(i);
- if (descs->IsProperty(i) && (details.attributes() & filter) == 0) {
+ if ((details.attributes() & filter) == 0) {
result++;
}
}
@@ -4174,9 +4203,7 @@ int Map::NumberOfDescribedProperties(PropertyAttributes filter) {
int Map::PropertyIndexFor(String* name) {
DescriptorArray* descs = instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
- return descs->GetFieldIndex(i);
- }
+ if (name->Equals(descs->GetKey(i))) return descs->GetFieldIndex(i);
}
return -1;
}
@@ -4255,20 +4282,20 @@ void JSReceiver::Lookup(String* name, LookupResult* result) {
current != heap->null_value();
current = JSObject::cast(current)->GetPrototype()) {
JSReceiver::cast(current)->LocalLookup(name, result);
- if (result->IsProperty()) return;
+ if (result->IsFound()) return;
}
result->NotFound();
}
// Search object and its prototype chain for callback properties.
-void JSObject::LookupCallback(String* name, LookupResult* result) {
+void JSObject::LookupCallbackProperty(String* name, LookupResult* result) {
Heap* heap = GetHeap();
for (Object* current = this;
current != heap->null_value() && current->IsJSObject();
current = JSObject::cast(current)->GetPrototype()) {
JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
- if (result->IsFound() && result->type() == CALLBACKS) return;
+ if (result->IsPropertyCallbacks()) return;
}
result->NotFound();
}
@@ -4371,7 +4398,7 @@ MaybeObject* JSObject::DefineElementAccessor(uint32_t index,
MaybeObject* JSObject::CreateAccessorPairFor(String* name) {
LookupResult result(GetHeap()->isolate());
LocalLookupRealNamedProperty(name, &result);
- if (result.IsProperty() && result.type() == CALLBACKS) {
+ if (result.IsPropertyCallbacks()) {
// Note that the result can actually have IsDontDelete() == true when we
// e.g. have to fall back to the slow case while adding a setter after
// successfully reusing a map transition for a getter. Nevertheless, this is
@@ -4380,7 +4407,7 @@ MaybeObject* JSObject::CreateAccessorPairFor(String* name) {
// DefinePropertyAccessor below.
Object* obj = result.GetCallbackObject();
if (obj->IsAccessorPair()) {
- return AccessorPair::cast(obj)->CopyWithoutTransitions();
+ return AccessorPair::cast(obj)->Copy();
}
}
return GetHeap()->AllocateAccessorPair();
@@ -4414,9 +4441,9 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name,
}
AccessorPair* accessors;
- { MaybeObject* maybe_accessors = CreateAccessorPairFor(name);
- if (!maybe_accessors->To(&accessors)) return maybe_accessors;
- }
+ MaybeObject* maybe_accessors = CreateAccessorPairFor(name);
+ if (!maybe_accessors->To(&accessors)) return maybe_accessors;
+
accessors->SetComponents(getter, setter);
return SetPropertyCallback(name, accessors, attributes);
}
@@ -4433,8 +4460,8 @@ bool JSObject::CanSetCallback(String* name) {
// to be overwritten because allowing overwriting could potentially
// cause security problems.
LookupResult callback_result(GetIsolate());
- LookupCallback(name, &callback_result);
- if (callback_result.IsProperty()) {
+ LookupCallbackProperty(name, &callback_result);
+ if (callback_result.IsFound()) {
Object* obj = callback_result.GetCallbackObject();
if (obj->IsAccessorInfo() &&
AccessorInfo::cast(obj)->prohibits_overwriting()) {
@@ -4488,17 +4515,17 @@ MaybeObject* JSObject::SetPropertyCallback(String* name,
Object* structure,
PropertyAttributes attributes) {
// Normalize object to make this operation simple.
- { MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
- if (maybe_ok->IsFailure()) return maybe_ok;
- }
+ MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (maybe_ok->IsFailure()) return maybe_ok;
// For the global object allocate a new map to invalidate the global inline
// caches which have a global property cell reference directly in the code.
if (IsGlobalObject()) {
Map* new_map;
- { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
- }
+ MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ ASSERT(new_map->is_dictionary_map());
+
set_map(new_map);
// When running crankshaft, changing the map is not enough. We
// need to deoptimize all functions that rely on this global
@@ -4508,9 +4535,8 @@ MaybeObject* JSObject::SetPropertyCallback(String* name,
// Update the dictionary with the new CALLBACKS property.
PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
- { MaybeObject* maybe_ok = SetNormalizedProperty(name, structure, details);
- if (maybe_ok->IsFailure()) return maybe_ok;
- }
+ maybe_ok = SetNormalizedProperty(name, structure, details);
+ if (maybe_ok->IsFailure()) return maybe_ok;
return GetHeap()->undefined_value();
}
@@ -4562,111 +4588,32 @@ MaybeObject* JSObject::DefineAccessor(String* name,
}
-static MaybeObject* CreateFreshAccessor(JSObject* obj,
- String* name,
- AccessorComponent component,
- Object* accessor,
- PropertyAttributes attributes) {
- // step 1: create a new getter/setter pair with only the accessor in it
- Heap* heap = obj->GetHeap();
- AccessorPair* accessors2;
- { MaybeObject* maybe_accessors2 = heap->AllocateAccessorPair();
- if (!maybe_accessors2->To(&accessors2)) return maybe_accessors2;
- }
- accessors2->set(component, accessor);
+static MaybeObject* TryAccessorTransition(JSObject* self,
+ Map* transitioned_map,
+ int target_descriptor,
+ AccessorComponent component,
+ Object* accessor,
+ PropertyAttributes attributes) {
+ DescriptorArray* descs = transitioned_map->instance_descriptors();
+ PropertyDetails details = descs->GetDetails(target_descriptor);
- // step 2: create a copy of the descriptors, incl. the new getter/setter pair
- Map* map1 = obj->map();
- CallbacksDescriptor callbacks_descr2(name, accessors2, attributes);
- DescriptorArray* descriptors2;
- { MaybeObject* maybe_descriptors2 =
- map1->instance_descriptors()->CopyInsert(&callbacks_descr2,
- REMOVE_TRANSITIONS);
- if (!maybe_descriptors2->To(&descriptors2)) return maybe_descriptors2;
- }
+ // If the transition target was not callbacks, fall back to the slow case.
+ if (details.type() != CALLBACKS) return self->GetHeap()->null_value();
+ Object* descriptor = descs->GetCallbacksObject(target_descriptor);
+ if (!descriptor->IsAccessorPair()) return self->GetHeap()->null_value();
- // step 3: create a new map with the new descriptors
- Map* map2;
- { MaybeObject* maybe_map2 = map1->CopyDropDescriptors();
- if (!maybe_map2->To(&map2)) return maybe_map2;
- }
- map2->set_instance_descriptors(descriptors2);
+ Object* target_accessor = AccessorPair::cast(descriptor)->get(component);
+ PropertyAttributes target_attributes = details.attributes();
- // step 4: create a new getter/setter pair with a transition to the new map
- AccessorPair* accessors1;
- { MaybeObject* maybe_accessors1 = heap->AllocateAccessorPair();
- if (!maybe_accessors1->To(&accessors1)) return maybe_accessors1;
+ // Reuse transition if adding same accessor with same attributes.
+ if (target_accessor == accessor && target_attributes == attributes) {
+ self->set_map(transitioned_map);
+ return self;
}
- accessors1->set(component, map2);
- // step 5: create a copy of the descriptors, incl. the new getter/setter pair
- // with the transition
- CallbacksDescriptor callbacks_descr1(name, accessors1, attributes);
- DescriptorArray* descriptors1;
- { MaybeObject* maybe_descriptors1 =
- map1->instance_descriptors()->CopyInsert(&callbacks_descr1,
- KEEP_TRANSITIONS);
- if (!maybe_descriptors1->To(&descriptors1)) return maybe_descriptors1;
- }
-
- // step 6: everything went well so far, so we make our changes visible
- obj->set_map(map2);
- map1->set_instance_descriptors(descriptors1);
- map2->SetBackPointer(map1);
- return obj;
-}
-
-
-static bool TransitionToSameAccessor(Object* map,
- String* name,
- AccessorComponent component,
- Object* accessor,
- PropertyAttributes attributes ) {
- DescriptorArray* descs = Map::cast(map)->instance_descriptors();
- int number = descs->SearchWithCache(name);
- ASSERT(number != DescriptorArray::kNotFound);
- Object* target_accessor =
- AccessorPair::cast(descs->GetCallbacksObject(number))->get(component);
- PropertyAttributes target_attributes = descs->GetDetails(number).attributes();
- return target_accessor == accessor && target_attributes == attributes;
-}
-
-
-static MaybeObject* NewCallbackTransition(JSObject* obj,
- String* name,
- AccessorComponent component,
- Object* accessor,
- PropertyAttributes attributes,
- AccessorPair* accessors2) {
- // step 1: copy the old getter/setter pair and set the new accessor
- AccessorPair* accessors3;
- { MaybeObject* maybe_accessors3 = accessors2->CopyWithoutTransitions();
- if (!maybe_accessors3->To(&accessors3)) return maybe_accessors3;
- }
- accessors3->set(component, accessor);
-
- // step 2: create a copy of the descriptors, incl. the new getter/setter pair
- Map* map2 = obj->map();
- CallbacksDescriptor callbacks_descr3(name, accessors3, attributes);
- DescriptorArray* descriptors3;
- { MaybeObject* maybe_descriptors3 =
- map2->instance_descriptors()->CopyInsert(&callbacks_descr3,
- REMOVE_TRANSITIONS);
- if (!maybe_descriptors3->To(&descriptors3)) return maybe_descriptors3;
- }
-
- // step 3: create a new map with the new descriptors
- Map* map3;
- { MaybeObject* maybe_map3 = map2->CopyDropDescriptors();
- if (!maybe_map3->To(&map3)) return maybe_map3;
- }
- map3->set_instance_descriptors(descriptors3);
-
- // step 4: everything went well so far, so we make our changes visible
- obj->set_map(map3);
- accessors2->set(component, map3);
- map3->SetBackPointer(map2);
- return obj;
+ // If either not the same accessor, or not the same attributes, fall back to
+ // the slow case.
+ return self->GetHeap()->null_value();
}
@@ -4678,40 +4625,74 @@ MaybeObject* JSObject::DefineFastAccessor(String* name,
LookupResult result(GetIsolate());
LocalLookup(name, &result);
- // If we have a new property, create a fresh accessor plus a transition to it.
- if (!result.IsFound()) {
- return CreateFreshAccessor(this, name, component, accessor, attributes);
- }
+ if (result.IsFound()
+ && !result.IsPropertyCallbacks()
+ && !result.IsTransition()) return GetHeap()->null_value();
+
+ // Return success if the same accessor with the same attributes already exist.
+ AccessorPair* source_accessors = NULL;
+ if (result.IsPropertyCallbacks()) {
+ Object* callback_value = result.GetCallbackObject();
+ if (callback_value->IsAccessorPair()) {
+ source_accessors = AccessorPair::cast(callback_value);
+ Object* entry = source_accessors->get(component);
+ if (entry == accessor && result.GetAttributes() == attributes) {
+ return this;
+ }
+ } else {
+ return GetHeap()->null_value();
+ }
- // If the property is not a JavaScript accessor, fall back to the slow case.
- if (result.type() != CALLBACKS) return GetHeap()->null_value();
- Object* callback_value = result.GetCallbackObject();
- if (!callback_value->IsAccessorPair()) return GetHeap()->null_value();
- AccessorPair* accessors = AccessorPair::cast(callback_value);
+ int descriptor_number = result.GetDescriptorIndex();
- // Follow a callback transition, if there is a fitting one.
- Object* entry = accessors->get(component);
- if (entry->IsMap() &&
- TransitionToSameAccessor(entry, name, component, accessor, attributes)) {
- set_map(Map::cast(entry));
- return this;
+ map()->LookupTransition(this, name, &result);
+
+ 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);
+ return TryAccessorTransition(
+ this, target, descriptor_number, component, accessor, attributes);
+ }
+ } else {
+ // If not, lookup a transition.
+ map()->LookupTransition(this, name, &result);
+
+ // If there is a transition, try to follow it.
+ if (result.IsFound()) {
+ Map* target = result.GetTransitionTarget();
+ int descriptor_number = target->LastAdded();
+ ASSERT(target->instance_descriptors()->GetKey(descriptor_number) == name);
+ return TryAccessorTransition(
+ this, target, descriptor_number, component, accessor, attributes);
+ }
}
- // When we re-add the same accessor again, there is nothing to do.
- if (entry == accessor && result.GetAttributes() == attributes) return this;
+ // If there is no transition yet, add a transition to the a new accessor pair
+ // containing the accessor.
+ AccessorPair* accessors;
+ MaybeObject* maybe_accessors;
- // Only the other accessor has been set so far, create a new transition.
- if (entry->IsTheHole()) {
- return NewCallbackTransition(this,
- name,
- component,
- accessor,
- attributes,
- accessors);
+ // Allocate a new pair if there were no source accessors. Otherwise, copy the
+ // pair and modify the accessor.
+ if (source_accessors != NULL) {
+ maybe_accessors = source_accessors->Copy();
+ } else {
+ maybe_accessors = GetHeap()->AllocateAccessorPair();
}
+ if (!maybe_accessors->To(&accessors)) return maybe_accessors;
+ accessors->set(component, accessor);
- // Nothing from the above worked, so we have to fall back to the slow case.
- return GetHeap()->null_value();
+ CallbacksDescriptor new_accessors_desc(name, accessors, attributes);
+
+ Map* new_map;
+ MaybeObject* maybe_new_map =
+ map()->CopyInsertDescriptor(&new_accessors_desc, INSERT_TRANSITION);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ set_map(new_map);
+ return this;
}
@@ -4739,9 +4720,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
// Try to flatten before operating on the string.
name->TryFlatten();
- if (!CanSetCallback(name)) {
- return isolate->heap()->undefined_value();
- }
+ if (!CanSetCallback(name)) return isolate->heap()->undefined_value();
uint32_t index = 0;
bool is_element = name->AsArrayIndex(&index);
@@ -4777,23 +4756,22 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
break;
}
- { MaybeObject* maybe_ok =
- SetElementCallback(index, info, info->property_attributes());
- if (maybe_ok->IsFailure()) return maybe_ok;
- }
+ MaybeObject* maybe_ok =
+ SetElementCallback(index, info, info->property_attributes());
+ if (maybe_ok->IsFailure()) return maybe_ok;
} else {
// Lookup the name.
LookupResult result(isolate);
LocalLookup(name, &result);
// 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.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
+ if (result.IsFound() && (result.IsReadOnly() || result.IsDontDelete())) {
return isolate->heap()->undefined_value();
}
- { MaybeObject* maybe_ok =
- SetPropertyCallback(name, info, info->property_attributes());
- if (maybe_ok->IsFailure()) return maybe_ok;
- }
+
+ MaybeObject* maybe_ok =
+ SetPropertyCallback(name, info, info->property_attributes());
+ if (maybe_ok->IsFailure()) return maybe_ok;
}
return this;
@@ -4819,9 +4797,9 @@ Object* JSObject::LookupAccessor(String* name, AccessorComponent component) {
if (name->AsArrayIndex(&index)) {
for (Object* obj = this;
obj != heap->null_value();
- obj = JSObject::cast(obj)->GetPrototype()) {
- JSObject* js_object = JSObject::cast(obj);
- if (js_object->HasDictionaryElements()) {
+ obj = JSReceiver::cast(obj)->GetPrototype()) {
+ if (obj->IsJSObject() && JSObject::cast(obj)->HasDictionaryElements()) {
+ JSObject* js_object = JSObject::cast(obj);
SeededNumberDictionary* dictionary = js_object->element_dictionary();
int entry = dictionary->FindEntry(index);
if (entry != SeededNumberDictionary::kNotFound) {
@@ -4836,12 +4814,12 @@ Object* JSObject::LookupAccessor(String* name, AccessorComponent component) {
} else {
for (Object* obj = this;
obj != heap->null_value();
- obj = JSObject::cast(obj)->GetPrototype()) {
+ obj = JSReceiver::cast(obj)->GetPrototype()) {
LookupResult result(heap->isolate());
- JSObject::cast(obj)->LocalLookup(name, &result);
- if (result.IsProperty()) {
+ JSReceiver::cast(obj)->LocalLookup(name, &result);
+ if (result.IsFound()) {
if (result.IsReadOnly()) return heap->undefined_value();
- if (result.type() == CALLBACKS) {
+ if (result.IsPropertyCallbacks()) {
Object* obj = result.GetCallbackObject();
if (obj->IsAccessorPair()) {
return AccessorPair::cast(obj)->GetComponent(component);
@@ -4875,46 +4853,19 @@ Object* JSObject::SlowReverseLookup(Object* value) {
}
-MaybeObject* Map::CopyDropDescriptors() {
- Heap* heap = GetHeap();
- Object* result;
- { MaybeObject* maybe_result =
- heap->AllocateMap(instance_type(), instance_size());
- if (!maybe_result->ToObject(&result)) return maybe_result;
- }
- Map::cast(result)->set_prototype(prototype());
- Map::cast(result)->set_constructor(constructor());
- // Don't copy descriptors, so map transitions always remain a forest.
- // If we retained the same descriptors we would have two maps
- // pointing to the same transition which is bad because the garbage
- // collector relies on being able to reverse pointers from transitions
- // to maps. If properties need to be retained use CopyDropTransitions.
- Map::cast(result)->clear_instance_descriptors();
- // Please note instance_type and instance_size are set when allocated.
- Map::cast(result)->set_inobject_properties(inobject_properties());
- Map::cast(result)->set_unused_property_fields(unused_property_fields());
-
- // If the map has pre-allocated properties always start out with a descriptor
- // array describing these properties.
- if (pre_allocated_property_fields() > 0) {
- ASSERT(constructor()->IsJSFunction());
- JSFunction* ctor = JSFunction::cast(constructor());
- Object* descriptors;
- { MaybeObject* maybe_descriptors =
- ctor->initial_map()->instance_descriptors()->RemoveTransitions(
- DescriptorArray::MAY_BE_SHARED);
- if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
- }
- Map::cast(result)->set_instance_descriptors(
- DescriptorArray::cast(descriptors));
- Map::cast(result)->set_pre_allocated_property_fields(
- pre_allocated_property_fields());
- }
- Map::cast(result)->set_bit_field(bit_field());
- Map::cast(result)->set_bit_field2(bit_field2());
- Map::cast(result)->set_bit_field3(bit_field3());
- Map::cast(result)->set_is_shared(false);
- Map::cast(result)->ClearCodeCache(heap);
+MaybeObject* Map::RawCopy(int instance_size) {
+ Map* result;
+ MaybeObject* maybe_result =
+ GetHeap()->AllocateMap(instance_type(), instance_size);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ result->set_prototype(prototype());
+ 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);
return result;
}
@@ -4926,29 +4877,21 @@ MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
new_instance_size -= inobject_properties() * kPointerSize;
}
- Object* result;
- { MaybeObject* maybe_result =
- GetHeap()->AllocateMap(instance_type(), new_instance_size);
- if (!maybe_result->ToObject(&result)) return maybe_result;
- }
+ Map* result;
+ MaybeObject* maybe_result = RawCopy(new_instance_size);
+ if (!maybe_result->To(&result)) return maybe_result;
if (mode != CLEAR_INOBJECT_PROPERTIES) {
- Map::cast(result)->set_inobject_properties(inobject_properties());
+ result->set_inobject_properties(inobject_properties());
}
- Map::cast(result)->set_prototype(prototype());
- Map::cast(result)->set_constructor(constructor());
-
- Map::cast(result)->set_bit_field(bit_field());
- Map::cast(result)->set_bit_field2(bit_field2());
- Map::cast(result)->set_bit_field3(bit_field3());
- Map::cast(result)->set_code_cache(code_cache());
-
- Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
+ result->set_code_cache(code_cache());
+ result->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
+ result->set_dictionary_map(true);
#ifdef DEBUG
- if (FLAG_verify_heap && Map::cast(result)->is_shared()) {
- Map::cast(result)->SharedMapVerify();
+ if (FLAG_verify_heap && result->is_shared()) {
+ result->SharedMapVerify();
}
#endif
@@ -4956,22 +4899,184 @@ MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
}
-MaybeObject* Map::CopyDropTransitions(
- DescriptorArray::SharedMode shared_mode) {
- Object* new_map;
- { MaybeObject* maybe_new_map = CopyDropDescriptors();
- if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
+MaybeObject* Map::CopyDropDescriptors() {
+ Map* result;
+ MaybeObject* maybe_result = RawCopy(instance_size());
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ // Please note instance_type and instance_size are set when allocated.
+ result->set_inobject_properties(inobject_properties());
+ result->set_unused_property_fields(unused_property_fields());
+
+ result->set_pre_allocated_property_fields(pre_allocated_property_fields());
+ result->set_is_shared(false);
+ result->ClearCodeCache(GetHeap());
+ return result;
+}
+
+
+MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
+ String* name,
+ TransitionFlag flag) {
+ 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());
}
- Object* descriptors;
- { MaybeObject* maybe_descriptors =
- instance_descriptors()->RemoveTransitions(shared_mode);
- if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
+
+ if (flag == INSERT_TRANSITION && CanHaveMoreTransitions()) {
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions = AddTransition(name, result);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+
+ set_transitions(transitions);
+ result->SetBackPointer(this);
+ }
+
+ return result;
+}
+
+
+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 ||
+ IsExternalArrayElementsKind(
+ elements_transition_map()->elements_kind())) &&
+ (kind == DICTIONARY_ELEMENTS ||
+ IsExternalArrayElementsKind(kind))));
+ ASSERT(!IsFastElementsKind(kind) ||
+ IsMoreGeneralElementsKindTransition(elements_kind(), kind));
+ ASSERT(kind != elements_kind());
+
+ MaybeObject* added_elements = set_elements_transition_map(new_map);
+ if (added_elements->IsFailure()) return added_elements;
+
+ new_map->SetBackPointer(this);
}
- cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
+
return new_map;
}
+MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() {
+ if (pre_allocated_property_fields() == 0) return CopyDropDescriptors();
+
+ // If the map has pre-allocated properties always start out with a descriptor
+ // array describing these properties.
+ ASSERT(constructor()->IsJSFunction());
+ JSFunction* ctor = JSFunction::cast(constructor());
+ Map* map = ctor->initial_map();
+ DescriptorArray* descriptors = map->instance_descriptors();
+
+ return CopyReplaceDescriptors(descriptors, NULL, OMIT_TRANSITION);
+}
+
+
+MaybeObject* Map::Copy() {
+ DescriptorArray* descriptors = instance_descriptors();
+ return CopyReplaceDescriptors(descriptors, NULL, OMIT_TRANSITION);
+}
+
+
+MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor,
+ TransitionFlag flag) {
+ DescriptorArray* descriptors = instance_descriptors();
+
+ // Ensure the key is a symbol.
+ 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 new_size = old_size + 1;
+
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ FixedArray::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);
+
+ SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
+
+ return CopyReplaceDescriptors(new_descriptors, key, flag);
+}
+
+
+MaybeObject* Map::CopyInsertDescriptor(Descriptor* descriptor,
+ TransitionFlag flag) {
+ DescriptorArray* old_descriptors = instance_descriptors();
+
+ // Ensure the key is a symbol.
+ MaybeObject* maybe_result = descriptor->KeyToSymbol();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // We replace the key if it is already present.
+ int index = old_descriptors->SearchWithCache(descriptor->GetKey());
+ if (index != DescriptorArray::kNotFound) {
+ return CopyReplaceDescriptor(descriptor, index, flag);
+ }
+ return CopyAddDescriptor(descriptor, flag);
+}
+
+
+MaybeObject* Map::CopyReplaceDescriptor(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;
+
+ String* key = descriptor->GetKey();
+ ASSERT(key == descriptors->GetKey(insertion_index));
+
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(size);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_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);
+ }
+
+ 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());
+
+ return CopyReplaceDescriptors(new_descriptors, key, flag);
+}
+
+
void Map::UpdateCodeCache(Handle<Map> map,
Handle<String> name,
Handle<Code> code) {
@@ -4980,6 +5085,7 @@ void Map::UpdateCodeCache(Handle<Map> map,
map->UpdateCodeCache(*name, *code));
}
+
MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
ASSERT(!is_shared() || code->allowed_in_shared_map_code_cache());
@@ -5028,85 +5134,43 @@ void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
// field of the contens array while it is running.
class IntrusiveMapTransitionIterator {
public:
- explicit IntrusiveMapTransitionIterator(DescriptorArray* descriptor_array)
- : descriptor_array_(descriptor_array) { }
+ explicit IntrusiveMapTransitionIterator(TransitionArray* transition_array)
+ : transition_array_(transition_array) { }
void Start() {
ASSERT(!IsIterating());
- if (descriptor_array_->MayContainTransitions())
- *DescriptorArrayHeader() = Smi::FromInt(0);
+ *TransitionArrayHeader() = Smi::FromInt(0);
}
bool IsIterating() {
- return descriptor_array_->MayContainTransitions() &&
- (*DescriptorArrayHeader())->IsSmi();
+ return (*TransitionArrayHeader())->IsSmi();
}
Map* Next() {
ASSERT(IsIterating());
- // Attention, tricky index manipulation ahead: Two consecutive indices are
- // assigned to each descriptor. Most descriptors directly advance to the
- // next descriptor by adding 2 to the index. The exceptions are the
- // CALLBACKS entries: An even index means we look at its getter, and an odd
- // index means we look at its setter.
- int raw_index = Smi::cast(*DescriptorArrayHeader())->value();
- int index = raw_index / 2;
- int number_of_descriptors = descriptor_array_->number_of_descriptors();
- while (index < number_of_descriptors) {
- PropertyDetails details(descriptor_array_->GetDetails(index));
- switch (details.type()) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- // We definitely have a map transition.
- *DescriptorArrayHeader() = Smi::FromInt(raw_index + 2);
- return static_cast<Map*>(descriptor_array_->GetValue(index));
- case CALLBACKS: {
- // We might have a map transition in a getter or in a setter.
- AccessorPair* accessors =
- static_cast<AccessorPair*>(descriptor_array_->GetValue(index));
- Object* accessor;
- if ((raw_index & 1) == 0) {
- accessor = accessors->setter();
- } else {
- ++index;
- accessor = accessors->getter();
- }
- ++raw_index;
- if (accessor->IsMap()) {
- *DescriptorArrayHeader() = Smi::FromInt(raw_index);
- return static_cast<Map*>(accessor);
- }
- break;
- }
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- case NULL_DESCRIPTOR:
- // We definitely have no map transition.
- raw_index += 2;
- ++index;
- break;
- }
+ int index = Smi::cast(*TransitionArrayHeader())->value();
+ int number_of_transitions = transition_array_->number_of_transitions();
+ while (index < number_of_transitions) {
+ *TransitionArrayHeader() = Smi::FromInt(index + 1);
+ return transition_array_->GetTarget(index);
}
- if (index == descriptor_array_->number_of_descriptors()) {
- Map* elements_transition = descriptor_array_->elements_transition_map();
- if (elements_transition != NULL) {
- *DescriptorArrayHeader() = Smi::FromInt(raw_index + 2);
- return elements_transition;
- }
+
+ if (index == number_of_transitions &&
+ transition_array_->HasElementsTransition()) {
+ Map* elements_transition = transition_array_->elements_transition();
+ *TransitionArrayHeader() = Smi::FromInt(index + 1);
+ return elements_transition;
}
- *DescriptorArrayHeader() = descriptor_array_->GetHeap()->fixed_array_map();
+ *TransitionArrayHeader() = transition_array_->GetHeap()->fixed_array_map();
return NULL;
}
private:
- Object** DescriptorArrayHeader() {
- return HeapObject::RawField(descriptor_array_, DescriptorArray::kMapOffset);
+ Object** TransitionArrayHeader() {
+ return HeapObject::RawField(transition_array_, TransitionArray::kMapOffset);
}
- DescriptorArray* descriptor_array_;
+ TransitionArray* transition_array_;
};
@@ -5119,11 +5183,11 @@ class IntrusivePrototypeTransitionIterator {
void Start() {
ASSERT(!IsIterating());
- if (HasTransitions()) *Header() = Smi::FromInt(0);
+ *Header() = Smi::FromInt(0);
}
bool IsIterating() {
- return HasTransitions() && (*Header())->IsSmi();
+ return (*Header())->IsSmi();
}
Map* Next() {
@@ -5138,23 +5202,17 @@ class IntrusivePrototypeTransitionIterator {
}
private:
- bool HasTransitions() {
- return proto_trans_->map()->IsSmi() || proto_trans_->IsFixedArray();
- }
-
Object** Header() {
return HeapObject::RawField(proto_trans_, FixedArray::kMapOffset);
}
int NumberOfTransitions() {
- ASSERT(HasTransitions());
FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset);
return Smi::cast(num)->value();
}
Map* GetTransition(int transitionNumber) {
- ASSERT(HasTransitions());
FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
return Map::cast(proto_trans->get(IndexFor(transitionNumber)));
}
@@ -5204,43 +5262,43 @@ class TraversableMap : public Map {
return old_parent;
}
- // Can either be Smi (no instance descriptors), or a descriptor array with the
- // header overwritten as a Smi (thus iterating).
- DescriptorArray* MutatedInstanceDescriptors() {
- Object* object =
- *HeapObject::RawField(this, kInstanceDescriptorsOrBitField3Offset);
- if (object->IsSmi()) {
- return GetHeap()->empty_descriptor_array();
- } else {
- DescriptorArray* descriptor_array =
- static_cast<DescriptorArray*>(object);
- return descriptor_array;
- }
- }
-
// Start iterating over this map's children, possibly destroying a FixedArray
// map (see explanation above).
void ChildIteratorStart() {
- IntrusiveMapTransitionIterator(instance_descriptors()).Start();
- IntrusivePrototypeTransitionIterator(
- unchecked_prototype_transitions()).Start();
+ if (HasTransitionArray()) {
+ if (HasPrototypeTransitions()) {
+ IntrusivePrototypeTransitionIterator(GetPrototypeTransitions()).Start();
+ }
+
+ IntrusiveMapTransitionIterator(transitions()).Start();
+ }
}
// If we have an unvisited child map, return that one and advance. If we have
// none, return NULL and reset any destroyed FixedArray maps.
TraversableMap* ChildIteratorNext() {
- IntrusivePrototypeTransitionIterator
- proto_iterator(unchecked_prototype_transitions());
- if (proto_iterator.IsIterating()) {
- Map* next = proto_iterator.Next();
- if (next != NULL) return static_cast<TraversableMap*>(next);
+ TransitionArray* transition_array = unchecked_transition_array();
+ if (!transition_array->map()->IsSmi() &&
+ !transition_array->IsTransitionArray()) {
+ return NULL;
+ }
+
+ if (transition_array->HasPrototypeTransitions()) {
+ HeapObject* proto_transitions =
+ transition_array->UncheckedPrototypeTransitions();
+ IntrusivePrototypeTransitionIterator proto_iterator(proto_transitions);
+ if (proto_iterator.IsIterating()) {
+ Map* next = proto_iterator.Next();
+ if (next != NULL) return static_cast<TraversableMap*>(next);
+ }
}
- IntrusiveMapTransitionIterator
- descriptor_iterator(MutatedInstanceDescriptors());
- if (descriptor_iterator.IsIterating()) {
- Map* next = descriptor_iterator.Next();
+
+ IntrusiveMapTransitionIterator transition_iterator(transition_array);
+ if (transition_iterator.IsIterating()) {
+ Map* next = transition_iterator.Next();
if (next != NULL) return static_cast<TraversableMap*>(next);
}
+
return NULL;
}
};
@@ -5271,7 +5329,7 @@ MaybeObject* CodeCache::Update(String* name, Code* code) {
// The number of monomorphic stubs for normal load/store/call IC's can grow to
// a large number and therefore they need to go into a hash table. They are
// used to load global properties from cells.
- if (code->type() == NORMAL) {
+ if (code->type() == Code::NORMAL) {
// Make sure that a hash table is allocated for the normal load code cache.
if (normal_type_cache()->IsUndefined()) {
Object* result;
@@ -5362,7 +5420,7 @@ MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
Object* CodeCache::Lookup(String* name, Code::Flags flags) {
- if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
+ if (Code::ExtractTypeFromFlags(flags) == Code::NORMAL) {
return LookupNormalTypeCache(name, flags);
} else {
return LookupDefaultCache(name, flags);
@@ -5400,7 +5458,7 @@ Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
int CodeCache::GetIndex(Object* name, Code* code) {
- if (code->type() == NORMAL) {
+ if (code->type() == Code::NORMAL) {
if (normal_type_cache()->IsUndefined()) return -1;
CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
return cache->GetIndex(String::cast(name), code->flags());
@@ -5416,7 +5474,7 @@ int CodeCache::GetIndex(Object* name, Code* code) {
void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
- if (code->type() == NORMAL) {
+ if (code->type() == Code::NORMAL) {
ASSERT(!normal_type_cache()->IsUndefined());
CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
@@ -5734,7 +5792,7 @@ MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
MaybeObject* maybe_result =
accessor->AddElementsToFixedArray(NULL, NULL, this, other);
FixedArray* result;
- if (!maybe_result->To<FixedArray>(&result)) return maybe_result;
+ if (!maybe_result->To(&result)) return maybe_result;
#ifdef DEBUG
if (FLAG_enable_slow_asserts) {
for (int i = 0; i < result->length(); i++) {
@@ -5790,24 +5848,17 @@ bool FixedArray::IsEqualTo(FixedArray* other) {
#endif
-MaybeObject* DescriptorArray::Allocate(int number_of_descriptors,
- SharedMode shared_mode) {
+MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
Heap* heap = Isolate::Current()->heap();
// Do not use DescriptorArray::cast on incomplete object.
FixedArray* result;
- if (number_of_descriptors == 0 && shared_mode == MAY_BE_SHARED) {
- return heap->empty_descriptor_array();
- }
+ if (number_of_descriptors == 0) return heap->empty_descriptor_array();
// Allocate the array of keys.
- { MaybeObject* maybe_array =
- heap->AllocateFixedArray(ToKeyIndex(number_of_descriptors));
- if (!maybe_array->To(&result)) return maybe_array;
- }
+ MaybeObject* maybe_array =
+ heap->AllocateFixedArray(LengthFor(number_of_descriptors));
+ if (!maybe_array->To(&result)) return maybe_array;
- result->set(kBitField3StorageIndex, Smi::FromInt(0));
- result->set(kEnumerationIndexIndex,
- Smi::FromInt(PropertyDetails::kInitialIndex));
- result->set(kTransitionsIndex, Smi::FromInt(0));
+ result->set(kEnumCacheIndex, Smi::FromInt(0));
return result;
}
@@ -5818,9 +5869,9 @@ void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
ASSERT(new_index_cache->IsSmi() || new_index_cache->IsFixedArray());
if (HasEnumCache()) {
- FixedArray::cast(get(kEnumerationIndexIndex))->
+ FixedArray::cast(get(kEnumCacheIndex))->
set(kEnumCacheBridgeCacheIndex, new_cache);
- FixedArray::cast(get(kEnumerationIndexIndex))->
+ FixedArray::cast(get(kEnumCacheIndex))->
set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
} else {
if (IsEmpty()) return; // Do nothing for empty descriptor array.
@@ -5828,197 +5879,49 @@ void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
set(kEnumCacheBridgeCacheIndex, new_cache);
FixedArray::cast(bridge_storage)->
set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
- NoWriteBarrierSet(FixedArray::cast(bridge_storage),
- kEnumCacheBridgeEnumIndex,
- get(kEnumerationIndexIndex));
- set(kEnumerationIndexIndex, bridge_storage);
+ set(kEnumCacheIndex, bridge_storage);
}
}
-static bool InsertionPointFound(String* key1, String* key2) {
- return key1->Hash() > key2->Hash() || key1 == key2;
-}
-
-
-void DescriptorArray::CopyFrom(Handle<DescriptorArray> dst,
- int dst_index,
- Handle<DescriptorArray> src,
+void DescriptorArray::CopyFrom(int dst_index,
+ DescriptorArray* src,
int src_index,
const WhitenessWitness& witness) {
- CALL_HEAP_FUNCTION_VOID(dst->GetIsolate(),
- dst->CopyFrom(dst_index, *src, src_index, witness));
-}
-
-
-MaybeObject* DescriptorArray::CopyFrom(int dst_index,
- DescriptorArray* src,
- int src_index,
- const WhitenessWitness& witness) {
Object* value = src->GetValue(src_index);
PropertyDetails details = src->GetDetails(src_index);
- if (details.type() == CALLBACKS && value->IsAccessorPair()) {
- MaybeObject* maybe_copy =
- AccessorPair::cast(value)->CopyWithoutTransitions();
- if (!maybe_copy->To(&value)) return maybe_copy;
- }
Descriptor desc(src->GetKey(src_index), value, details);
Set(dst_index, &desc, witness);
- return this;
}
-MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
- TransitionFlag transition_flag) {
- // Transitions are only kept when inserting another transition.
- // This precondition is not required by this function's implementation, but
- // is currently required by the semantics of maps, so we check it.
- // Conversely, we filter after replacing, so replacing a transition and
- // removing all other transitions is not supported.
- bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
- ASSERT(remove_transitions == !descriptor->ContainsTransition());
- ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
-
- // Ensure the key is a symbol.
- { MaybeObject* maybe_result = descriptor->KeyToSymbol();
- if (maybe_result->IsFailure()) return maybe_result;
- }
-
- int new_size = 0;
- for (int i = 0; i < number_of_descriptors(); i++) {
- if (IsNullDescriptor(i)) continue;
- if (remove_transitions && IsTransitionOnly(i)) continue;
- new_size++;
- }
-
- // If key is in descriptor, we replace it in-place when filtering.
- // Count a null descriptor for key as inserted, not replaced.
- int index = Search(descriptor->GetKey());
- const bool replacing = (index != kNotFound);
- bool keep_enumeration_index = false;
- if (!replacing) {
- ++new_size;
- } else if (!IsTransitionOnly(index)) {
- // We are replacing an existing descriptor. We keep the enumeration index
- // of a visible property.
- keep_enumeration_index = true;
- } else if (remove_transitions) {
- // Replaced descriptor has been counted as removed if it is a transition
- // that will be replaced. Adjust count in this case.
- ++new_size;
- }
-
- DescriptorArray* new_descriptors;
- { SharedMode mode = remove_transitions ? MAY_BE_SHARED : CANNOT_BE_SHARED;
- MaybeObject* maybe_result = Allocate(new_size, mode);
- if (!maybe_result->To(&new_descriptors)) return maybe_result;
- }
-
- DescriptorArray::WhitenessWitness witness(new_descriptors);
-
- // Set the enumeration index in the descriptors and set the enumeration index
- // in the result.
- int enumeration_index = NextEnumerationIndex();
- if (!descriptor->ContainsTransition()) {
- if (keep_enumeration_index) {
- descriptor->SetEnumerationIndex(GetDetails(index).index());
- } else {
- descriptor->SetEnumerationIndex(enumeration_index);
- ++enumeration_index;
- }
- }
- Map* old_elements_transition = elements_transition_map();
- if ((!remove_transitions) && (old_elements_transition != NULL)) {
- new_descriptors->set_elements_transition_map(old_elements_transition);
- }
- new_descriptors->SetNextEnumerationIndex(enumeration_index);
-
- // Copy the descriptors, filtering out transitions and null descriptors,
- // and inserting or replacing a descriptor.
- int to_index = 0;
- int insertion_index = -1;
- int from_index = 0;
- while (from_index < number_of_descriptors()) {
- if (insertion_index < 0 &&
- InsertionPointFound(GetKey(from_index), descriptor->GetKey())) {
- insertion_index = to_index++;
- if (replacing) from_index++;
- } else {
- if (!(IsNullDescriptor(from_index) ||
- (remove_transitions && IsTransitionOnly(from_index)))) {
- MaybeObject* copy_result =
- new_descriptors->CopyFrom(to_index++, this, from_index, witness);
- if (copy_result->IsFailure()) return copy_result;
- }
- from_index++;
- }
- }
- if (insertion_index < 0) insertion_index = to_index++;
-
- ASSERT(insertion_index < new_descriptors->number_of_descriptors());
- new_descriptors->Set(insertion_index, descriptor, witness);
-
- ASSERT(to_index == new_descriptors->number_of_descriptors());
- SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
-
- return new_descriptors;
-}
-
-
-MaybeObject* DescriptorArray::RemoveTransitions(SharedMode shared_mode) {
- // Allocate the new descriptor array.
- int new_number_of_descriptors = 0;
- for (int i = 0; i < number_of_descriptors(); i++) {
- if (IsProperty(i)) new_number_of_descriptors++;
- }
- DescriptorArray* new_descriptors;
- { MaybeObject* maybe_result = Allocate(new_number_of_descriptors,
- shared_mode);
- if (!maybe_result->To(&new_descriptors)) return maybe_result;
- }
-
- // Copy the content.
- DescriptorArray::WhitenessWitness witness(new_descriptors);
- int next_descriptor = 0;
- for (int i = 0; i < number_of_descriptors(); i++) {
- if (IsProperty(i)) {
- MaybeObject* copy_result =
- new_descriptors->CopyFrom(next_descriptor++, this, i, witness);
- if (copy_result->IsFailure()) return copy_result;
- }
- }
- ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
- new_descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
-
- return new_descriptors;
-}
-
// We need the whiteness witness since sort will reshuffle the entries in the
// descriptor array. If the descriptor array were to be black, the shuffling
// would move a slot that was already recorded as pointing into an evacuation
// candidate. This would result in missing updates upon evacuation.
-void DescriptorArray::SortUnchecked(const WhitenessWitness& witness) {
+void DescriptorArray::Sort() {
// In-place heap sort.
int len = number_of_descriptors();
-
+ // Reset sorting since the descriptor array might contain invalid pointers.
+ for (int i = 0; i < len; ++i) SetSortedKey(i, i);
// Bottom-up max-heap construction.
// Index of the last node with children
const int max_parent_index = (len / 2) - 1;
for (int i = max_parent_index; i >= 0; --i) {
int parent_index = i;
- const uint32_t parent_hash = GetKey(i)->Hash();
+ const uint32_t parent_hash = GetSortedKey(i)->Hash();
while (parent_index <= max_parent_index) {
int child_index = 2 * parent_index + 1;
- uint32_t child_hash = GetKey(child_index)->Hash();
+ uint32_t child_hash = GetSortedKey(child_index)->Hash();
if (child_index + 1 < len) {
- uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
+ uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash();
if (right_child_hash > child_hash) {
child_index++;
child_hash = right_child_hash;
}
}
if (child_hash <= parent_hash) break;
- NoIncrementalWriteBarrierSwapDescriptors(parent_index, child_index);
+ SwapSortedKeys(parent_index, child_index);
// Now element at child_index could be < its children.
parent_index = child_index; // parent_hash remains correct.
}
@@ -6027,83 +5930,37 @@ void DescriptorArray::SortUnchecked(const WhitenessWitness& witness) {
// Extract elements and create sorted array.
for (int i = len - 1; i > 0; --i) {
// Put max element at the back of the array.
- NoIncrementalWriteBarrierSwapDescriptors(0, i);
+ SwapSortedKeys(0, i);
// Shift down the new top element.
int parent_index = 0;
- const uint32_t parent_hash = GetKey(parent_index)->Hash();
+ const uint32_t parent_hash = GetSortedKey(parent_index)->Hash();
const int max_parent_index = (i / 2) - 1;
while (parent_index <= max_parent_index) {
int child_index = parent_index * 2 + 1;
- uint32_t child_hash = GetKey(child_index)->Hash();
+ uint32_t child_hash = GetSortedKey(child_index)->Hash();
if (child_index + 1 < i) {
- uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
+ uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash();
if (right_child_hash > child_hash) {
child_index++;
child_hash = right_child_hash;
}
}
if (child_hash <= parent_hash) break;
- NoIncrementalWriteBarrierSwapDescriptors(parent_index, child_index);
+ SwapSortedKeys(parent_index, child_index);
parent_index = child_index;
}
}
}
-void DescriptorArray::Sort(const WhitenessWitness& witness) {
- SortUnchecked(witness);
- SLOW_ASSERT(IsSortedNoDuplicates());
-}
-
-
-int DescriptorArray::BinarySearch(String* name, int low, int high) {
- uint32_t hash = name->Hash();
- int limit = high;
-
- ASSERT(low <= high);
-
- while (low != high) {
- int mid = (low + high) / 2;
- String* mid_name = GetKey(mid);
- uint32_t mid_hash = mid_name->Hash();
-
- if (mid_hash >= hash) {
- high = mid;
- } else {
- low = mid + 1;
- }
- }
-
- for (; low <= limit && GetKey(low)->Hash() == hash; ++low) {
- if (GetKey(low)->Equals(name) && !IsNullDescriptor(low))
- return low;
- }
-
- return kNotFound;
-}
-
-
-int DescriptorArray::LinearSearch(SearchMode mode, String* name, int len) {
- uint32_t hash = name->Hash();
- for (int number = 0; number < len; number++) {
- String* entry = GetKey(number);
- if (mode == EXPECT_SORTED && entry->Hash() > hash) break;
- if (name->Equals(entry) && !IsNullDescriptor(number)) {
- return number;
- }
- }
- return kNotFound;
-}
-
-
-MaybeObject* AccessorPair::CopyWithoutTransitions() {
+MaybeObject* AccessorPair::Copy() {
Heap* heap = GetHeap();
AccessorPair* copy;
- { MaybeObject* maybe_copy = heap->AllocateAccessorPair();
- if (!maybe_copy->To(&copy)) return maybe_copy;
- }
- copy->set_getter(getter()->IsMap() ? heap->the_hole_value() : getter());
- copy->set_setter(setter()->IsMap() ? heap->the_hole_value() : setter());
+ MaybeObject* maybe_copy = heap->AllocateAccessorPair();
+ if (!maybe_copy->To(&copy)) return maybe_copy;
+
+ copy->set_getter(getter());
+ copy->set_setter(setter());
return copy;
}
@@ -7349,70 +7206,59 @@ static bool ClearBackPointer(Heap* heap, Object* target) {
}
+// TODO(mstarzinger): This method should be moved into MarkCompactCollector,
+// because it cannot be called from outside the GC and we already have methods
+// depending on the transitions layout in the GC anyways.
void Map::ClearNonLiveTransitions(Heap* heap) {
- DescriptorArray* d = DescriptorArray::cast(
- *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset));
- if (d->IsEmpty()) return;
- Smi* NullDescriptorDetails =
- PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
- for (int i = 0; i < d->number_of_descriptors(); ++i) {
- // If the pair (value, details) is a map transition, check if the target is
- // live. If not, null the descriptor. Also drop the back pointer for that
- // map transition, so that this map is not reached again by following a back
- // pointer from that non-live map.
- bool keep_entry = false;
- PropertyDetails details(d->GetDetails(i));
- switch (details.type()) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- keep_entry = !ClearBackPointer(heap, d->GetValue(i));
- break;
- case CALLBACKS: {
- Object* object = d->GetValue(i);
- if (object->IsAccessorPair()) {
- AccessorPair* accessors = AccessorPair::cast(object);
- Object* getter = accessors->getter();
- if (getter->IsMap()) {
- if (ClearBackPointer(heap, getter)) {
- accessors->set_getter(heap->the_hole_value());
- } else {
- keep_entry = true;
- }
- } else if (!getter->IsTheHole()) {
- keep_entry = true;
- }
- Object* setter = accessors->setter();
- if (setter->IsMap()) {
- if (ClearBackPointer(heap, setter)) {
- accessors->set_setter(heap->the_hole_value());
- } else {
- keep_entry = true;
- }
- } else if (!setter->IsTheHole()) {
- keep_entry = true;
- }
- } else {
- keep_entry = true;
- }
- break;
+ // If there are no transitions to be cleared, return.
+ // TODO(verwaest) Should be an assert, otherwise back pointers are not
+ // properly cleared.
+ if (!HasTransitionArray()) return;
+
+ TransitionArray* t = transitions();
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+
+ int transition_index = 0;
+
+ // Compact all live descriptors to the left.
+ for (int i = 0; i < t->number_of_transitions(); ++i) {
+ if (!ClearBackPointer(heap, t->GetTarget(i))) {
+ if (i != transition_index) {
+ String* key = t->GetKey(i);
+ t->SetKey(transition_index, key);
+ Object** key_slot = t->GetKeySlot(transition_index);
+ collector->RecordSlot(key_slot, key_slot, key);
+ // Target slots do not need to be recorded since maps are not compacted.
+ t->SetTarget(transition_index, t->GetTarget(i));
}
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- case NULL_DESCRIPTOR:
- keep_entry = true;
- break;
- }
- // Make sure that an entry containing only dead transitions gets collected.
- // What we *really* want to do here is removing this entry completely, but
- // for technical reasons we can't do this, so we zero it out instead.
- if (!keep_entry) {
- d->SetDetailsUnchecked(i, NullDescriptorDetails);
- d->SetNullValueUnchecked(i, heap);
+ transition_index++;
}
}
+
+ if (t->HasElementsTransition() &&
+ ClearBackPointer(heap, t->elements_transition())) {
+ t->ClearElementsTransition();
+ } else {
+ // If there are no transitions to be cleared, return.
+ // TODO(verwaest) Should be an assert, otherwise back pointers are not
+ // properly cleared.
+ 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 trim = t->number_of_transitions() - transition_index;
+ if (trim > 0) {
+ RightTrimFixedArray<FROM_GC>(
+ heap, t, trim * TransitionArray::kTransitionSize);
+ }
}
@@ -7445,8 +7291,7 @@ bool Map::EquivalentToForNormalization(Map* other,
instance_type() == other->instance_type() &&
bit_field() == other->bit_field() &&
bit_field2() == other->bit_field2() &&
- (bit_field3() & ~(1<<Map::kIsShared)) ==
- (other->bit_field3() & ~(1<<Map::kIsShared));
+ function_with_prototype() == other->function_with_prototype();
}
@@ -7467,13 +7312,19 @@ void JSFunction::MarkForLazyRecompilation() {
ReplaceCode(builtins->builtin(Builtins::kLazyRecompile));
}
+void JSFunction::MarkForParallelRecompilation() {
+ ASSERT(is_compiled() && !IsOptimized());
+ ASSERT(shared()->allows_lazy_compilation() || code()->optimizable());
+ Builtins* builtins = GetIsolate()->builtins();
+ ReplaceCode(builtins->builtin(Builtins::kParallelRecompile));
-bool SharedFunctionInfo::EnsureCompiled(Handle<SharedFunctionInfo> shared,
- ClearExceptionFlag flag) {
- return shared->is_compiled() || CompileLazy(shared, flag);
+ // Unlike MarkForLazyRecompilation, after queuing a function for
+ // recompilation on the compiler thread, we actually tail-call into
+ // the full code. We reset the profiler ticks here so that the
+ // function doesn't bother the runtime profiler too much.
+ shared()->code()->set_profiler_ticks(0);
}
-
static bool CompileLazyHelper(CompilationInfo* info,
ClearExceptionFlag flag) {
// Compile the source information to a code object.
@@ -7490,11 +7341,77 @@ static bool CompileLazyHelper(CompilationInfo* info,
bool SharedFunctionInfo::CompileLazy(Handle<SharedFunctionInfo> shared,
ClearExceptionFlag flag) {
- CompilationInfo info(shared);
+ ASSERT(shared->allows_lazy_compilation_without_context());
+ CompilationInfoWithZone info(shared);
return CompileLazyHelper(&info, flag);
}
+void SharedFunctionInfo::ClearOptimizedCodeMap() {
+ set_optimized_code_map(Smi::FromInt(0));
+}
+
+
+void SharedFunctionInfo::AddToOptimizedCodeMap(
+ Handle<SharedFunctionInfo> shared,
+ Handle<Context> native_context,
+ Handle<Code> code,
+ Handle<FixedArray> literals) {
+ ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
+ ASSERT(native_context->IsNativeContext());
+ STATIC_ASSERT(kEntryLength == 3);
+ Object* value = shared->optimized_code_map();
+ Handle<FixedArray> new_code_map;
+ if (value->IsSmi()) {
+ // No optimized code map.
+ ASSERT_EQ(0, Smi::cast(value)->value());
+ // Crate 3 entries per context {context, code, literals}.
+ new_code_map = FACTORY->NewFixedArray(kEntryLength);
+ new_code_map->set(0, *native_context);
+ new_code_map->set(1, *code);
+ new_code_map->set(2, *literals);
+ } else {
+ // Copy old map and append one new entry.
+ Handle<FixedArray> old_code_map(FixedArray::cast(value));
+ ASSERT_EQ(-1, shared->SearchOptimizedCodeMap(*native_context));
+ int old_length = old_code_map->length();
+ int new_length = old_length + kEntryLength;
+ new_code_map = FACTORY->NewFixedArray(new_length);
+ old_code_map->CopyTo(0, *new_code_map, 0, old_length);
+ new_code_map->set(old_length, *native_context);
+ new_code_map->set(old_length + 1, *code);
+ new_code_map->set(old_length + 2, *literals);
+ }
+#ifdef DEBUG
+ for (int i = 0; i < new_code_map->length(); i += kEntryLength) {
+ ASSERT(new_code_map->get(i)->IsNativeContext());
+ ASSERT(new_code_map->get(i + 1)->IsCode());
+ ASSERT(Code::cast(new_code_map->get(i + 1))->kind() ==
+ Code::OPTIMIZED_FUNCTION);
+ ASSERT(new_code_map->get(i + 2)->IsFixedArray());
+ }
+#endif
+ shared->set_optimized_code_map(*new_code_map);
+}
+
+
+void SharedFunctionInfo::InstallFromOptimizedCodeMap(JSFunction* function,
+ int index) {
+ ASSERT(index > 0);
+ ASSERT(optimized_code_map()->IsFixedArray());
+ FixedArray* code_map = FixedArray::cast(optimized_code_map());
+ if (!bound()) {
+ FixedArray* cached_literals = FixedArray::cast(code_map->get(index + 1));
+ ASSERT(cached_literals != NULL);
+ function->set_literals(cached_literals);
+ }
+ Code* code = Code::cast(code_map->get(index));
+ ASSERT(code != NULL);
+ ASSERT(function->context()->native_context() == code_map->get(index - 1));
+ function->ReplaceCode(code);
+}
+
+
bool JSFunction::CompileLazy(Handle<JSFunction> function,
ClearExceptionFlag flag) {
bool result = true;
@@ -7502,7 +7419,8 @@ bool JSFunction::CompileLazy(Handle<JSFunction> function,
function->ReplaceCode(function->shared()->code());
function->shared()->set_code_age(0);
} else {
- CompilationInfo info(function);
+ ASSERT(function->shared()->allows_lazy_compilation());
+ CompilationInfoWithZone info(function);
result = CompileLazyHelper(&info, flag);
ASSERT(!result || function->is_compiled());
}
@@ -7511,14 +7429,20 @@ bool JSFunction::CompileLazy(Handle<JSFunction> function,
bool JSFunction::CompileOptimized(Handle<JSFunction> function,
- int osr_ast_id,
+ BailoutId osr_ast_id,
ClearExceptionFlag flag) {
- CompilationInfo info(function);
+ CompilationInfoWithZone info(function);
info.SetOptimizing(osr_ast_id);
return CompileLazyHelper(&info, flag);
}
+bool JSFunction::EnsureCompiled(Handle<JSFunction> function,
+ ClearExceptionFlag flag) {
+ return function->is_compiled() || CompileLazy(function, flag);
+}
+
+
bool JSFunction::IsInlineable() {
if (IsBuiltin()) return false;
SharedFunctionInfo* shared_info = shared();
@@ -7564,12 +7488,10 @@ MaybeObject* JSFunction::SetInstancePrototype(Object* value) {
// If the function has allocated the initial map
// replace it with a copy containing the new prototype.
Map* new_map;
- MaybeObject* maybe_new_map =
- initial_map()->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
+ MaybeObject* maybe_new_map = initial_map()->Copy();
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
new_map->set_prototype(value);
- MaybeObject* maybe_object =
- set_initial_map_and_cache_transitions(new_map);
+ MaybeObject* maybe_object = set_initial_map_and_cache_transitions(new_map);
if (maybe_object->IsFailure()) return maybe_object;
} else {
// Put the value in the initial map field until an initial map is
@@ -7595,16 +7517,15 @@ MaybeObject* JSFunction::SetPrototype(Object* value) {
// Remove map transitions because they point to maps with a
// different prototype.
Map* new_map;
- { MaybeObject* maybe_new_map =
- map()->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
- }
+ MaybeObject* maybe_new_map = map()->Copy();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
Heap* heap = new_map->GetHeap();
set_map(new_map);
new_map->set_constructor(value);
new_map->set_non_instance_prototype(true);
construct_prototype =
- heap->isolate()->context()->global_context()->
+ heap->isolate()->context()->native_context()->
initial_object_prototype();
} else {
map()->set_non_instance_prototype(false);
@@ -7614,30 +7535,25 @@ MaybeObject* JSFunction::SetPrototype(Object* value) {
}
-Object* JSFunction::RemovePrototype() {
- Context* global_context = context()->global_context();
+void JSFunction::RemovePrototype() {
+ Context* native_context = context()->native_context();
Map* no_prototype_map = shared()->is_classic_mode()
- ? global_context->function_without_prototype_map()
- : global_context->strict_mode_function_without_prototype_map();
+ ? native_context->function_without_prototype_map()
+ : native_context->strict_mode_function_without_prototype_map();
- if (map() == no_prototype_map) {
- // Be idempotent.
- return this;
- }
+ if (map() == no_prototype_map) return;
ASSERT(map() == (shared()->is_classic_mode()
- ? global_context->function_map()
- : global_context->strict_mode_function_map()));
+ ? native_context->function_map()
+ : native_context->strict_mode_function_map()));
set_map(no_prototype_map);
set_prototype_or_initial_map(no_prototype_map->GetHeap()->the_hole_value());
- return this;
}
-Object* JSFunction::SetInstanceClassName(String* name) {
+void JSFunction::SetInstanceClassName(String* name) {
shared()->set_instance_class_name(name);
- return this;
}
@@ -7647,8 +7563,8 @@ void JSFunction::PrintName(FILE* out) {
}
-Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
- return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
+Context* JSFunction::NativeContextFromLiterals(FixedArray* literals) {
+ return Context::cast(literals->get(JSFunction::kLiteralNativeContextIndex));
}
@@ -7716,26 +7632,33 @@ bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
return false;
}
- // If the prototype is null inline constructors cause no problems.
- if (!prototype->IsJSObject()) {
- ASSERT(prototype->IsNull());
- return true;
- }
-
Heap* heap = GetHeap();
- // Traverse the proposed prototype chain looking for setters for properties of
- // the same names as are set by the inline constructor.
+ // Traverse the proposed prototype chain looking for properties of the
+ // same names as are set by the inline constructor.
for (Object* obj = prototype;
obj != heap->null_value();
obj = obj->GetPrototype()) {
- JSObject* js_object = JSObject::cast(obj);
+ JSReceiver* receiver = JSReceiver::cast(obj);
for (int i = 0; i < this_property_assignments_count(); i++) {
LookupResult result(heap->isolate());
String* name = GetThisPropertyAssignmentName(i);
- js_object->LocalLookupRealNamedProperty(name, &result);
- if (result.IsFound() && result.type() == CALLBACKS) {
- return false;
+ receiver->LocalLookup(name, &result);
+ if (result.IsFound()) {
+ switch (result.type()) {
+ case NORMAL:
+ case FIELD:
+ case CONSTANT_FUNCTION:
+ break;
+ case INTERCEPTOR:
+ case CALLBACKS:
+ case HANDLER:
+ return false;
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
}
}
}
@@ -7880,7 +7803,7 @@ void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
}
-void SharedFunctionInfo::DisableOptimization() {
+void SharedFunctionInfo::DisableOptimization(const char* reason) {
// Disable optimization for the shared function info and mark the
// code as non-optimizable. The marker on the shared function info
// is there because we flush non-optimized code thereby loosing the
@@ -7896,13 +7819,14 @@ void SharedFunctionInfo::DisableOptimization() {
code()->set_optimizable(false);
}
if (FLAG_trace_opt) {
- PrintF("[disabled optimization for %s]\n", *DebugName()->ToCString());
+ PrintF("[disabled optimization for %s, reason: %s]\n",
+ *DebugName()->ToCString(), reason);
}
}
-bool SharedFunctionInfo::VerifyBailoutId(int id) {
- ASSERT(id != AstNode::kNoNumber);
+bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) {
+ ASSERT(!id.IsNone());
Code* unoptimized = code();
DeoptimizationOutputData* data =
DeoptimizationOutputData::cast(unoptimized->deoptimization_data());
@@ -7985,7 +7909,7 @@ void SharedFunctionInfo::ResetForNewContext(int new_ic_age) {
if (code()->kind() == Code::FUNCTION) {
code()->set_profiler_ticks(0);
if (optimization_disabled() &&
- opt_count() >= Compiler::kDefaultMaxOptCount) {
+ opt_count() >= FLAG_max_opt_count) {
// Re-enable optimizations if they were disabled due to opt_count limit.
set_optimization_disabled(false);
code()->set_optimizable(true);
@@ -8039,9 +7963,20 @@ void SharedFunctionInfo::CompleteInobjectSlackTracking() {
}
-void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
- v->VisitSharedFunctionInfo(this);
- SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
+int SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context) {
+ ASSERT(native_context->IsNativeContext());
+ if (!FLAG_cache_optimized_code) return -1;
+ Object* value = optimized_code_map();
+ if (!value->IsSmi()) {
+ FixedArray* optimized_code_map = FixedArray::cast(value);
+ int length = optimized_code_map->length();
+ for (int i = 0; i < length; i += 3) {
+ if (optimized_code_map->get(i) == native_context) {
+ return i + 1;
+ }
+ }
+ }
+ return -1;
}
@@ -8269,7 +8204,6 @@ void Code::ClearTypeFeedbackCells(Heap* heap) {
TypeFeedbackCells* type_feedback_cells =
TypeFeedbackInfo::cast(raw_info)->type_feedback_cells();
for (int i = 0; i < type_feedback_cells->CellCount(); i++) {
- ASSERT(type_feedback_cells->AstId(i)->IsSmi());
JSGlobalPropertyCell* cell = type_feedback_cells->Cell(i);
cell->set_value(TypeFeedbackCells::RawUninitializedSentinel(heap));
}
@@ -8296,7 +8230,7 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
for (int i = 0; i < deopt_count; i++) {
PrintF(out, "%6d %6d %6d %6d",
i,
- AstId(i)->value(),
+ AstId(i).ToInt(),
ArgumentsStackHeight(i)->value(),
Pc(i)->value());
@@ -8330,11 +8264,14 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
case Translation::JS_FRAME: {
int ast_id = iterator.Next();
int function_id = iterator.Next();
- JSFunction* function =
- JSFunction::cast(LiteralArray()->get(function_id));
unsigned height = iterator.Next();
PrintF(out, "{ast_id=%d, function=", ast_id);
- function->PrintName(out);
+ if (function_id != Translation::kSelfLiteralId) {
+ Object* function = LiteralArray()->get(function_id);
+ JSFunction::cast(function)->PrintName(out);
+ } else {
+ PrintF(out, "<self>");
+ }
PrintF(out, ", height=%u}", height);
break;
}
@@ -8351,6 +8288,17 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
break;
}
+ case Translation::GETTER_STUB_FRAME:
+ case Translation::SETTER_STUB_FRAME: {
+ int function_id = iterator.Next();
+ JSFunction* function =
+ JSFunction::cast(LiteralArray()->get(function_id));
+ PrintF(out, "{function=");
+ function->PrintName(out);
+ PrintF(out, "}");
+ break;
+ }
+
case Translation::DUPLICATE:
break;
@@ -8366,6 +8314,14 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
break;
}
+ case Translation::UINT32_REGISTER: {
+ int reg_code = iterator.Next();
+ PrintF(out,
+ "{input=%s (unsigned)}",
+ converter.NameOfCPURegister(reg_code));
+ break;
+ }
+
case Translation::DOUBLE_REGISTER: {
int reg_code = iterator.Next();
PrintF(out, "{input=%s}",
@@ -8385,6 +8341,12 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
break;
}
+ case Translation::UINT32_STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ PrintF(out, "{input=%d (unsigned)}", input_slot_index);
+ break;
+ }
+
case Translation::DOUBLE_STACK_SLOT: {
int input_slot_index = iterator.Next();
PrintF(out, "{input=%d}", input_slot_index);
@@ -8415,7 +8377,7 @@ void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) {
for (int i = 0; i < this->DeoptPoints(); i++) {
int pc_and_state = this->PcAndState(i)->value();
PrintF("%6d %8d %s\n",
- this->AstId(i)->value(),
+ this->AstId(i).ToInt(),
FullCodeGenerator::PcField::decode(pc_and_state),
FullCodeGenerator::State2String(
FullCodeGenerator::StateField::decode(pc_and_state)));
@@ -8461,17 +8423,15 @@ const char* Code::ICState2String(InlineCacheState state) {
}
-const char* Code::PropertyType2String(PropertyType type) {
+const char* Code::StubType2String(StubType type) {
switch (type) {
case NORMAL: return "NORMAL";
case FIELD: return "FIELD";
case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
case CALLBACKS: return "CALLBACKS";
- case HANDLER: return "HANDLER";
case INTERCEPTOR: return "INTERCEPTOR";
case MAP_TRANSITION: return "MAP_TRANSITION";
- case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
- case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
+ case NONEXISTENT: return "NONEXISTENT";
}
UNREACHABLE(); // keep the compiler happy
return NULL;
@@ -8509,7 +8469,7 @@ void Code::Disassemble(const char* name, FILE* out) {
PrintF(out, "ic_state = %s\n", ICState2String(ic_state()));
PrintExtraICState(out, kind(), extra_ic_state());
if (ic_state() == MONOMORPHIC) {
- PrintF(out, "type = %s\n", PropertyType2String(type()));
+ PrintF(out, "type = %s\n", StubType2String(type()));
}
if (is_call_stub() || is_keyed_call_stub()) {
PrintF(out, "argc = %d\n", arguments_count());
@@ -8565,6 +8525,8 @@ 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
@@ -8581,6 +8543,12 @@ void Code::Disassemble(const char* name, FILE* out) {
}
PrintF(out, "\n");
}
+#ifdef OBJECT_PRINT
+ if (!type_feedback_info()->IsUndefined()) {
+ TypeFeedbackInfo::cast(type_feedback_info())->TypeFeedbackInfoPrint(out);
+ PrintF(out, "\n");
+ }
+#endif
}
PrintF("RelocInfo (size = %d)\n", relocation_size());
@@ -8740,7 +8708,7 @@ MaybeObject* JSArray::SetElementsLength(Object* len) {
Map* Map::GetPrototypeTransition(Object* prototype) {
- FixedArray* cache = prototype_transitions();
+ FixedArray* cache = GetPrototypeTransitions();
int number_of_transitions = NumberOfProtoTransitions();
const int proto_offset =
kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset;
@@ -8762,7 +8730,7 @@ MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
// Don't cache prototype transition if this map is shared.
if (is_shared() || !FLAG_cache_prototype_transitions) return this;
- FixedArray* cache = prototype_transitions();
+ FixedArray* cache = GetPrototypeTransitions();
const int step = kProtoTransitionElementsPerEntry;
const int header = kProtoTransitionHeaderSize;
@@ -8785,7 +8753,8 @@ MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
new_cache->set(i + header, cache->get(i + header));
}
cache = new_cache;
- set_prototype_transitions(cache);
+ MaybeObject* set_result = SetPrototypeTransitions(cache);
+ if (set_result->IsFailure()) return set_result;
}
int last = transitions - 1;
@@ -8864,15 +8833,12 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
Map* new_map = map->GetPrototypeTransition(value);
if (new_map == NULL) {
- { MaybeObject* maybe_new_map =
- map->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
- }
+ MaybeObject* maybe_new_map = map->Copy();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
- { MaybeObject* maybe_new_cache =
- map->PutPrototypeTransition(value, new_map);
- if (maybe_new_cache->IsFailure()) return maybe_new_cache;
- }
+ MaybeObject* maybe_new_cache =
+ map->PutPrototypeTransition(value, new_map);
+ if (maybe_new_cache->IsFailure()) return maybe_new_cache;
new_map->set_prototype(value);
}
@@ -9152,6 +9118,7 @@ MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
Handle<AccessorInfo> data(AccessorInfo::cast(structure));
Object* fun_obj = data->getter();
v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
+ if (call_fun == NULL) return isolate->heap()->undefined_value();
HandleScope scope(isolate);
Handle<JSObject> self(JSObject::cast(receiver));
Handle<JSObject> holder_handle(JSObject::cast(holder));
@@ -9168,7 +9135,9 @@ MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
}
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
if (result.IsEmpty()) return isolate->heap()->undefined_value();
- return *v8::Utils::OpenHandle(*result);
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
}
// __defineGetter__ callback
@@ -9432,7 +9401,8 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
// is read-only (a declared const that has not been initialized). If a
// value is being defined we skip attribute checks completely.
if (set_mode == DEFINE_PROPERTY) {
- details = PropertyDetails(attributes, NORMAL, details.index());
+ details = PropertyDetails(
+ attributes, NORMAL, details.dictionary_index());
dictionary->DetailsAtPut(entry, details);
} else if (details.IsReadOnly() && !element->IsTheHole()) {
if (strict_mode == kNonStrictMode) {
@@ -9722,8 +9692,9 @@ 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[] = { Handle<Object>(this), number };
+ Handle<Object> args[] = { receiver, number };
Handle<Object> error = isolate->factory()->NewTypeError(
"redef_external_array_element", HandleVector(args, ARRAY_SIZE(args)));
return isolate->Throw(*error);
@@ -9985,7 +9956,11 @@ MaybeObject* JSObject::GetElementWithInterceptor(Object* receiver,
result = getter(index, info);
}
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
- if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
+ if (!result.IsEmpty()) {
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
+ }
}
Heap* heap = holder_handle->GetHeap();
@@ -10235,7 +10210,7 @@ MaybeObject* JSObject::GetPropertyPostInterceptor(
// Check local property in holder, ignore interceptor.
LookupResult result(GetIsolate());
LocalLookupRealNamedProperty(name, &result);
- if (result.IsProperty()) {
+ if (result.IsFound()) {
return GetProperty(receiver, &result, name, attributes);
}
// Continue searching via the prototype chain.
@@ -10253,7 +10228,7 @@ MaybeObject* JSObject::GetLocalPropertyPostInterceptor(
// Check local property in holder, ignore interceptor.
LookupResult result(GetIsolate());
LocalLookupRealNamedProperty(name, &result);
- if (result.IsProperty()) {
+ if (result.IsFound()) {
return GetProperty(receiver, &result, name, attributes);
}
return GetHeap()->undefined_value();
@@ -10287,7 +10262,9 @@ MaybeObject* JSObject::GetPropertyWithInterceptor(
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
if (!result.IsEmpty()) {
*attributes = NONE;
- return *v8::Utils::OpenHandle(*result);
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
}
}
@@ -10312,7 +10289,7 @@ bool JSObject::HasRealNamedProperty(String* key) {
LookupResult result(isolate);
LocalLookupRealNamedProperty(key, &result);
- return result.IsProperty() && (result.type() != INTERCEPTOR);
+ return result.IsFound() && !result.IsInterceptor();
}
@@ -10392,7 +10369,7 @@ bool JSObject::HasRealNamedCallbackProperty(String* key) {
LookupResult result(isolate);
LocalLookupRealNamedProperty(key, &result);
- return result.IsFound() && (result.type() == CALLBACKS);
+ return result.IsPropertyCallbacks();
}
@@ -10523,10 +10500,10 @@ void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
ASSERT(storage->length() >= (NumberOfLocalProperties() - index));
if (HasFastProperties()) {
DescriptorArray* descs = map()->instance_descriptors();
+ ASSERT(storage->length() >= index + descs->number_of_descriptors());
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
+ storage->set(index + i, descs->GetKey(i));
}
- ASSERT(storage->length() >= index);
} else {
property_dictionary()->CopyKeysTo(storage,
index,
@@ -11096,8 +11073,12 @@ void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
template<typename Shape, typename Key>
MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
+ MinimumCapacity capacity_option,
PretenureFlag pretenure) {
- int capacity = ComputeCapacity(at_least_space_for);
+ ASSERT(!capacity_option || IS_POWER_OF_TWO(at_least_space_for));
+ int capacity = (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY)
+ ? at_least_space_for
+ : ComputeCapacity(at_least_space_for);
if (capacity > HashTable::kMaxCapacity) {
return Failure::OutOfMemoryException();
}
@@ -11154,30 +11135,6 @@ int StringDictionary::FindEntry(String* key) {
}
-bool StringDictionary::ContainsTransition(int entry) {
- switch (DetailsAt(entry).type()) {
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- return true;
- case CALLBACKS: {
- Object* value = ValueAt(entry);
- if (!value->IsAccessorPair()) return false;
- AccessorPair* accessors = AccessorPair::cast(value);
- return accessors->getter()->IsMap() || accessors->setter()->IsMap();
- }
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case HANDLER:
- case INTERCEPTOR:
- case NULL_DESCRIPTOR:
- return false;
- }
- UNREACHABLE(); // Keep the compiler happy.
- return false;
-}
-
-
template<typename Shape, typename Key>
MaybeObject* HashTable<Shape, Key>::Rehash(HashTable* new_table, Key key) {
ASSERT(NumberOfElements() < new_table->Capacity());
@@ -11230,7 +11187,9 @@ MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
(capacity > kMinCapacityForPretenure) && !GetHeap()->InNewSpace(this);
Object* obj;
{ MaybeObject* maybe_obj =
- Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
+ Allocate(nof * 2,
+ USE_DEFAULT_MINIMUM_CAPACITY,
+ pretenure ? TENURED : NOT_TENURED);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
@@ -11259,7 +11218,9 @@ MaybeObject* HashTable<Shape, Key>::Shrink(Key key) {
!GetHeap()->InNewSpace(this);
Object* obj;
{ MaybeObject* maybe_obj =
- Allocate(at_least_room_for, pretenure ? TENURED : NOT_TENURED);
+ Allocate(at_least_room_for,
+ USE_DEFAULT_MINIMUM_CAPACITY,
+ pretenure ? TENURED : NOT_TENURED);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
@@ -11664,7 +11625,7 @@ Object* ExternalPixelArray::SetValue(uint32_t index, Object* value) {
clamped_value = 255;
} else {
// Other doubles are rounded to the nearest integer.
- clamped_value = static_cast<uint8_t>(double_value + 0.5);
+ clamped_value = static_cast<uint8_t>(lrint(double_value));
}
} else {
// Clamp undefined to zero (default). All other types have been
@@ -12004,8 +11965,23 @@ MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
}
-Object* CompilationCacheTable::Lookup(String* src) {
- StringKey key(src);
+// The key for the script compilation cache is dependent on the mode flags,
+// because they change the global language mode and thus binding behaviour.
+// If flags change at some point, we must ensure that we do not hit the cache
+// for code compiled with different settings.
+static LanguageMode CurrentGlobalLanguageMode() {
+ return FLAG_use_strict
+ ? (FLAG_harmony_scoping ? EXTENDED_MODE : STRICT_MODE)
+ : CLASSIC_MODE;
+}
+
+
+Object* CompilationCacheTable::Lookup(String* src, Context* context) {
+ SharedFunctionInfo* shared = context->closure()->shared();
+ StringSharedKey key(src,
+ shared,
+ CurrentGlobalLanguageMode(),
+ RelocInfo::kNoPosition);
int entry = FindEntry(&key);
if (entry == kNotFound) return GetHeap()->undefined_value();
return get(EntryToIndex(entry) + 1);
@@ -12035,17 +12011,24 @@ Object* CompilationCacheTable::LookupRegExp(String* src,
}
-MaybeObject* CompilationCacheTable::Put(String* src, Object* value) {
- StringKey key(src);
- Object* obj;
- { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
+MaybeObject* CompilationCacheTable::Put(String* src,
+ Context* context,
+ Object* value) {
+ SharedFunctionInfo* shared = context->closure()->shared();
+ StringSharedKey key(src,
+ shared,
+ CurrentGlobalLanguageMode(),
+ RelocInfo::kNoPosition);
+ CompilationCacheTable* cache;
+ MaybeObject* maybe_cache = EnsureCapacity(1, &key);
+ if (!maybe_cache->To(&cache)) return maybe_cache;
+
+ Object* k;
+ MaybeObject* maybe_k = key.AsObject();
+ if (!maybe_k->To(&k)) return maybe_k;
- CompilationCacheTable* cache =
- reinterpret_cast<CompilationCacheTable*>(obj);
int entry = cache->FindInsertionEntry(key.Hash());
- cache->set(EntryToIndex(entry), src);
+ cache->set(EntryToIndex(entry), k);
cache->set(EntryToIndex(entry) + 1, value);
cache->ElementAdded();
return cache;
@@ -12189,6 +12172,12 @@ MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
}
+void StringDictionary::DoGenerateNewEnumerationIndices(
+ Handle<StringDictionary> dictionary) {
+ CALL_HEAP_FUNCTION_VOID(dictionary->GetIsolate(),
+ dictionary->GenerateNewEnumerationIndices());
+}
+
template<typename Shape, typename Key>
MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
Heap* heap = Dictionary<Shape, Key>::GetHeap();
@@ -12215,7 +12204,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).index()));
+ enumeration_order->set(
+ pos++, Smi::FromInt(DetailsAt(i).dictionary_index()));
}
}
@@ -12314,6 +12304,8 @@ template<typename Shape, typename Key>
MaybeObject* Dictionary<Shape, Key>::Add(Key key,
Object* value,
PropertyDetails details) {
+ ASSERT(details.dictionary_index() == details.descriptor_index());
+
// Valdate key is absent.
SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
// Check whether the dictionary should be extended.
@@ -12341,7 +12333,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.index() == 0 && Shape::kIsEnumerable) {
+ if (!details.IsDeleted() &&
+ details.dictionary_index() == 0 && Shape::kIsEnumerable) {
// Assign an enumeration index to the property and update
// SetNextEnumerationIndex.
int index = NextEnumerationIndex();
@@ -12432,7 +12425,7 @@ MaybeObject* SeededNumberDictionary::Set(uint32_t key,
// Preserve enumeration index.
details = PropertyDetails(details.attributes(),
details.type(),
- DetailsAt(entry).index());
+ DetailsAt(entry).dictionary_index());
MaybeObject* maybe_object_key = SeededNumberDictionaryShape::AsObject(key);
Object* object_key;
if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
@@ -12503,23 +12496,45 @@ void Dictionary<Shape, Key>::CopyKeysTo(
}
-void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
- FixedArray* sort_array) {
- ASSERT(storage->length() >= NumberOfEnumElements());
+FixedArray* StringDictionary::CopyEnumKeysTo(FixedArray* storage) {
+ int length = storage->length();
+ ASSERT(length >= NumberOfEnumElements());
+ Heap* heap = GetHeap();
+ Object* undefined_value = heap->undefined_value();
int capacity = Capacity();
- int index = 0;
+ int properties = 0;
+
+ // Fill in the enumeration array by assigning enumerable keys at their
+ // enumeration index. This will leave holes in the array if there are keys
+ // that are deleted or not enumerable.
for (int i = 0; i < capacity; i++) {
Object* k = KeyAt(i);
if (IsKey(k)) {
PropertyDetails details = DetailsAt(i);
if (details.IsDeleted() || details.IsDontEnum()) continue;
- storage->set(index, k);
- sort_array->set(index, Smi::FromInt(details.index()));
- index++;
+ properties++;
+ storage->set(details.dictionary_index() - 1, k);
+ if (properties == length) break;
}
}
- storage->SortPairs(sort_array, sort_array->length());
- ASSERT(storage->length() >= index);
+
+ // There are holes in the enumeration array if less properties were assigned
+ // than the length of the array. If so, crunch all the existing properties
+ // together by shifting them to the left (maintaining the enumeration order),
+ // and trimming of the right side of the array.
+ if (properties < length) {
+ if (properties == 0) return heap->empty_fixed_array();
+ properties = 0;
+ for (int i = 0; i < length; ++i) {
+ Object* value = storage->get(i);
+ if (value != undefined_value) {
+ storage->set(properties, value);
+ ++properties;
+ }
+ }
+ RightTrimFixedArray<FROM_MUTATOR>(heap, storage, length - properties);
+ }
+ return storage;
}
@@ -12569,18 +12584,12 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
JSObject* obj, int unused_property_fields) {
// Make sure we preserve dictionary representation if there are too many
// descriptors.
- if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
-
- // Figure out if it is necessary to generate new enumeration indices.
- int max_enumeration_index =
- NextEnumerationIndex() +
- (DescriptorArray::kMaxNumberOfDescriptors -
- NumberOfElements());
- if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
- Object* result;
- { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
- if (!maybe_result->ToObject(&result)) return maybe_result;
- }
+ int number_of_elements = NumberOfElements();
+ if (number_of_elements > DescriptorArray::kMaxNumberOfDescriptors) return obj;
+
+ if (number_of_elements != NextEnumerationIndex()) {
+ MaybeObject* maybe_result = GenerateNewEnumerationIndices();
+ if (maybe_result->IsFailure()) return maybe_result;
}
int instance_descriptor_length = 0;
@@ -12604,19 +12613,35 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
}
}
+ int inobject_props = obj->map()->inobject_properties();
+
+ // Allocate new map.
+ Map* new_map;
+ MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ new_map->set_dictionary_map(false);
+
+ if (instance_descriptor_length == 0) {
+ ASSERT_LE(unused_property_fields, inobject_props);
+ // Transform the object.
+ new_map->set_unused_property_fields(inobject_props);
+ obj->set_map(new_map);
+ obj->set_properties(heap->empty_fixed_array());
+ // Check that it really works.
+ ASSERT(obj->HasFastProperties());
+ return obj;
+ }
+
// Allocate the instance descriptor.
DescriptorArray* descriptors;
- { MaybeObject* maybe_descriptors =
- DescriptorArray::Allocate(instance_descriptor_length,
- DescriptorArray::MAY_BE_SHARED);
- if (!maybe_descriptors->To<DescriptorArray>(&descriptors)) {
- return maybe_descriptors;
- }
+ MaybeObject* maybe_descriptors =
+ DescriptorArray::Allocate(instance_descriptor_length);
+ if (!maybe_descriptors->To(&descriptors)) {
+ return maybe_descriptors;
}
- DescriptorArray::WhitenessWitness witness(descriptors);
+ FixedArray::WhitenessWitness witness(descriptors);
- int inobject_props = obj->map()->inobject_properties();
int number_of_allocated_fields =
number_of_fields + unused_property_fields - inobject_props;
if (number_of_allocated_fields < 0) {
@@ -12626,33 +12651,33 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
}
// Allocate the fixed array for the fields.
- Object* fields;
- { MaybeObject* maybe_fields =
- heap->AllocateFixedArray(number_of_allocated_fields);
- if (!maybe_fields->ToObject(&fields)) return maybe_fields;
- }
+ FixedArray* fields;
+ MaybeObject* maybe_fields =
+ heap->AllocateFixedArray(number_of_allocated_fields);
+ if (!maybe_fields->To(&fields)) return maybe_fields;
// Fill in the instance descriptor and the fields.
- int next_descriptor = 0;
int current_offset = 0;
for (int i = 0; i < capacity; i++) {
Object* k = KeyAt(i);
if (IsKey(k)) {
Object* value = ValueAt(i);
// Ensure the key is a symbol before writing into the instance descriptor.
- Object* key;
- { MaybeObject* maybe_key = heap->LookupSymbol(String::cast(k));
- if (!maybe_key->ToObject(&key)) return maybe_key;
- }
+ String* key;
+ MaybeObject* maybe_key = heap->LookupSymbol(String::cast(k));
+ if (!maybe_key->To(&key)) return maybe_key;
+
PropertyDetails details = DetailsAt(i);
+ ASSERT(details.descriptor_index() == details.dictionary_index());
+ int enumeration_index = details.descriptor_index();
PropertyType type = details.type();
if (value->IsJSFunction() && !heap->InNewSpace(value)) {
- ConstantFunctionDescriptor d(String::cast(key),
+ ConstantFunctionDescriptor d(key,
JSFunction::cast(value),
details.attributes(),
- details.index());
- descriptors->Set(next_descriptor++, &d, witness);
+ enumeration_index);
+ descriptors->Set(enumeration_index - 1, &d, witness);
} else if (type == NORMAL) {
if (current_offset < inobject_props) {
obj->InObjectPropertyAtPut(current_offset,
@@ -12660,24 +12685,19 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
UPDATE_WRITE_BARRIER);
} else {
int offset = current_offset - inobject_props;
- FixedArray::cast(fields)->set(offset, value);
+ fields->set(offset, value);
}
- FieldDescriptor d(String::cast(key),
+ FieldDescriptor d(key,
current_offset++,
details.attributes(),
- details.index());
- descriptors->Set(next_descriptor++, &d, witness);
+ enumeration_index);
+ descriptors->Set(enumeration_index - 1, &d, witness);
} else if (type == CALLBACKS) {
- if (value->IsAccessorPair()) {
- MaybeObject* maybe_copy =
- AccessorPair::cast(value)->CopyWithoutTransitions();
- if (!maybe_copy->To(&value)) return maybe_copy;
- }
- CallbacksDescriptor d(String::cast(key),
+ CallbacksDescriptor d(key,
value,
details.attributes(),
- details.index());
- descriptors->Set(next_descriptor++, &d, witness);
+ enumeration_index);
+ descriptors->Set(enumeration_index - 1, &d, witness);
} else {
UNREACHABLE();
}
@@ -12685,22 +12705,18 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
}
ASSERT(current_offset == number_of_fields);
- descriptors->Sort(witness);
- // Allocate new map.
- Object* new_map;
- { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
- if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
- }
+ descriptors->Sort();
+
+ MaybeObject* maybe_failure = new_map->InitializeDescriptors(descriptors);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ new_map->set_unused_property_fields(unused_property_fields);
// Transform the object.
- obj->set_map(Map::cast(new_map));
- obj->map()->set_instance_descriptors(descriptors);
- obj->map()->set_unused_property_fields(unused_property_fields);
+ obj->set_map(new_map);
- obj->set_properties(FixedArray::cast(fields));
+ obj->set_properties(fields);
ASSERT(obj->IsJSObject());
- descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
// Check that it really works.
ASSERT(obj->HasFastProperties());
@@ -12771,11 +12787,11 @@ Object* ObjectHashTable::Lookup(Object* key) {
// If the object does not have an identity hash, it was never used as a key.
{ MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
if (maybe_hash->ToObjectUnchecked()->IsUndefined()) {
- return GetHeap()->undefined_value();
+ return GetHeap()->the_hole_value();
}
}
int entry = FindEntry(key);
- if (entry == kNotFound) return GetHeap()->undefined_value();
+ if (entry == kNotFound) return GetHeap()->the_hole_value();
return get(EntryToIndex(entry) + 1);
}
@@ -12792,7 +12808,7 @@ MaybeObject* ObjectHashTable::Put(Object* key, Object* value) {
int entry = FindEntry(key);
// Check whether to perform removal operation.
- if (value->IsUndefined()) {
+ if (value->IsTheHole()) {
if (entry == kNotFound) return this;
RemoveEntry(entry);
return Shrink(key);