Commit 4f7b6654 authored by whesse@chromium.org's avatar whesse@chromium.org

This change rewrites some of the code to add properties to an object.

It removes the ReplaceConstantFunction code, and replaces it with
new ConvertDescriptorToField code, that is also used in other places.
Functions CopyRemove and CopyReplace on DescriptorArray are removed.
Function AddFastProperty is simplified by removing the 
CONSTANT_TRANSITION case.
Review URL: http://codereview.chromium.org/6528

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@475 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 76b1efea
......@@ -964,21 +964,7 @@ Object* JSObject::AddFastProperty(String* name,
return AddSlowProperty(name, value, attributes);
}
// Replace a CONSTANT_TRANSITION flag with a transition.
// Do this by removing it, and the standard code for adding a map transition
// will then run.
DescriptorArray* old_descriptors = map()->instance_descriptors();
int old_name_index = old_descriptors->Search(name);
bool constant_transition = false; // Only used in assertions.
if (old_name_index != DescriptorArray::kNotFound && CONSTANT_TRANSITION ==
PropertyDetails(old_descriptors->GetDetails(old_name_index)).type()) {
constant_transition = true;
Object* r = old_descriptors->CopyRemove(name);
if (r->IsFailure()) return r;
old_descriptors = DescriptorArray::cast(r);
old_name_index = DescriptorArray::kNotFound;
}
// Compute the new index for new field.
int index = map()->NextFreePropertyIndex();
......@@ -993,64 +979,43 @@ Object* JSObject::AddFastProperty(String* name,
bool allow_map_transition =
!old_descriptors->Contains(name) &&
(Top::context()->global_context()->object_function()->map() != map());
ASSERT(allow_map_transition || !constant_transition);
if (map()->unused_property_fields() > 0) {
ASSERT(index < properties()->length());
// Allocate a new map for the object.
Object* r = map()->Copy();
ASSERT(index < properties()->length() ||
map()->unused_property_fields() == 0);
// Allocate a new map for the object.
Object* r = map()->Copy();
if (r->IsFailure()) return 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 = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
if (r->IsFailure()) return 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 = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
if (r->IsFailure()) return r;
old_descriptors = DescriptorArray::cast(r);
}
// We have now allocated all the necessary objects.
// All the changes can be applied at once, so they are atomic.
map()->set_instance_descriptors(old_descriptors);
new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
set_map(new_map);
properties()->set(index, value);
} else {
ASSERT(map()->unused_property_fields() == 0);
old_descriptors = DescriptorArray::cast(r);
}
if (map()->unused_property_fields() == 0) {
if (properties()->length() > kMaxFastProperties) {
Object* obj = NormalizeProperties();
if (obj->IsFailure()) return obj;
return AddSlowProperty(name, value, attributes);
}
static const int kExtraFields = 3;
// Make room for the new value
Object* values =
properties()->CopySize(properties()->length() + kExtraFields);
properties()->CopySize(properties()->length() + kFieldsAdded);
if (values->IsFailure()) return values;
FixedArray::cast(values)->set(index, value);
// Allocate a new map for the object.
Object* r = map()->Copy();
if (r->IsFailure()) return r;
Map* new_map = Map::cast(r);
if (allow_map_transition) {
MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
// Allocate new instance descriptors for the old map with map transition.
Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
if (r->IsFailure()) return r;
old_descriptors = DescriptorArray::cast(r);
}
// We have now allocated all the necessary objects.
// All changes can be done at once, atomically.
map()->set_instance_descriptors(old_descriptors);
new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
new_map->set_unused_property_fields(kExtraFields - 1);
set_map(new_map);
set_properties(FixedArray::cast(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.
map()->set_instance_descriptors(old_descriptors);
new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
set_map(new_map);
properties()->set(index, value);
return value;
}
......@@ -1104,74 +1069,6 @@ Object* JSObject::AddConstantFunctionProperty(String* name,
}
Object* JSObject::ReplaceConstantFunctionProperty(String* name,
Object* value) {
// There are two situations to handle here:
// 1: Replace a constant function with another function.
// 2: Replace a constant function with an object.
if (value->IsJSFunction()) {
JSFunction* function = JSFunction::cast(value);
Object* new_map = map()->CopyDropTransitions();
if (new_map->IsFailure()) return new_map;
set_map(Map::cast(new_map));
// Replace the function entry
int index = map()->instance_descriptors()->Search(name);
ASSERT(index != DescriptorArray::kNotFound);
map()->instance_descriptors()->ReplaceConstantFunction(index, function);
} else {
// Allocate new instance descriptors with updated property index.
int index = map()->NextFreePropertyIndex();
Object* new_descriptors =
map()->instance_descriptors()->CopyReplace(name, index, NONE);
if (new_descriptors->IsFailure()) return new_descriptors;
if (map()->unused_property_fields() > 0) {
ASSERT(index < properties()->length());
// Allocate a new map for the object.
Object* new_map = map()->Copy();
if (new_map->IsFailure()) return new_map;
Map::cast(new_map)->
set_instance_descriptors(DescriptorArray::cast(new_descriptors));
Map::cast(new_map)->
set_unused_property_fields(map()->unused_property_fields()-1);
set_map(Map::cast(new_map));
properties()->set(index, value);
} else {
ASSERT(map()->unused_property_fields() == 0);
static const int kFastNofProperties = 20;
if (properties()->length() > kFastNofProperties) {
Object* obj = NormalizeProperties();
if (obj->IsFailure()) return obj;
return SetProperty(name, value, NONE);
}
static const int kExtraFields = 5;
// Make room for the more properties.
Object* values =
properties()->CopySize(properties()->length() + kExtraFields);
if (values->IsFailure()) return values;
FixedArray::cast(values)->set(index, value);
// Allocate a new map for the object.
Object* new_map = map()->Copy();
if (new_map->IsFailure()) return new_map;
Map::cast(new_map)->
set_instance_descriptors(DescriptorArray::cast(new_descriptors));
Map::cast(new_map)->
set_unused_property_fields(kExtraFields - 1);
set_map(Map::cast(new_map));
set_properties(FixedArray::cast(values));
}
}
return value;
}
// Add property in slow mode
Object* JSObject::AddSlowProperty(String* name,
Object* value,
......@@ -1223,6 +1120,103 @@ Object* JSObject::SetPropertyPostInterceptor(String* name,
}
Object* JSObject::ReplaceSlowProperty(String* name,
Object* value,
PropertyAttributes attributes) {
Dictionary* dictionary = property_dictionary();
PropertyDetails old_details =
dictionary->DetailsAt(dictionary->FindStringEntry(name));
int new_index = old_details.index();
if (old_details.IsTransition()) new_index = 0;
PropertyDetails new_details(attributes, NORMAL, old_details.index());
Object* result =
property_dictionary()->SetOrAddStringEntry(name, value, new_details);
if (result->IsFailure()) return result;
if (property_dictionary() != result) {
set_properties(Dictionary::cast(result));
}
return value;
}
Object* JSObject::ConvertDescriptorToFieldAndMapTransition(
String* name,
Object* new_value,
PropertyAttributes attributes) {
Map* old_map = map();
Object* result = ConvertDescriptorToField(name, new_value, attributes);
if (result->IsFailure()) return 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() == Top::context()->global_context()->object_function()->map()) {
return result;
}
MapTransitionDescriptor transition(name,
map(),
attributes);
Object* new_descriptors =
old_map->instance_descriptors()->
CopyInsert(&transition, KEEP_TRANSITIONS);
if (new_descriptors->IsFailure()) return result; // Yes, return _result_.
old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
return result;
}
Object* JSObject::ConvertDescriptorToField(String* name,
Object* new_value,
PropertyAttributes attributes) {
if (map()->unused_property_fields() == 0 &&
properties()->length() > kMaxFastProperties) {
Object* obj = NormalizeProperties();
if (obj->IsFailure()) return 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 = map()->instance_descriptors()->
CopyInsert(&new_field, REMOVE_TRANSITIONS);
if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
DescriptorArray* new_descriptors =
DescriptorArray::cast(descriptors_unchecked);
// Make a new map for the object.
Object* new_map_unchecked = map()->Copy();
if (new_map_unchecked->IsFailure()) return new_map_unchecked;
Map* new_map = Map::cast(new_map_unchecked);
new_map->set_instance_descriptors(new_descriptors);
// Make new properties array if necessary.
FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
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_unchecked =
properties()->CopySize(properties()->length() + kFieldsAdded);
if (new_properties_unchecked->IsFailure()) return new_properties_unchecked;
new_properties = FixedArray::cast(new_properties_unchecked);
}
// 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));
}
properties()->set(index, new_value);
return new_value;
}
Object* JSObject::SetPropertyWithInterceptor(String* name,
Object* value,
PropertyAttributes attributes) {
......@@ -1528,13 +1522,12 @@ Object* JSObject::SetProperty(LookupResult* result,
return AddFastPropertyUsingMap(result->GetTransitionMap(),
name,
value);
} else {
return AddFastProperty(name, value, attributes);
}
return ConvertDescriptorToField(name, value, attributes);
case CONSTANT_FUNCTION:
if (value == result->GetConstantFunction()) return value;
// Only replace the function if necessary.
return ReplaceConstantFunctionProperty(name, value);
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case CALLBACKS:
return SetPropertyWithCallback(result->GetCallbackObject(),
name,
......@@ -1545,10 +1538,9 @@ Object* JSObject::SetProperty(LookupResult* result,
case CONSTANT_TRANSITION:
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
// if the value is a function.
// AddProperty has been extended to do this, in this case.
return AddFastProperty(name, value, attributes);
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case NULL_DESCRIPTOR:
UNREACHABLE();
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
default:
UNREACHABLE();
}
......@@ -1580,33 +1572,14 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(result, name, value);
}
/*
REMOVED FROM CLONE
if (result->IsNotFound() || !result->IsProperty()) {
// We could not find a local property so let's check whether there is an
// accessor that wants to handle the property.
LookupResult accessor_result;
LookupCallbackSetterInPrototypes(name, &accessor_result);
if (accessor_result.IsValid()) {
return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
name,
value,
accessor_result.holder());
}
}
*/
// Check for accessor in prototype chain removed here in clone.
if (result->IsNotFound()) {
return AddProperty(name, value, attributes);
}
if (!result->IsLoaded()) {
return SetLazyProperty(result, name, value, attributes);
}
/*
REMOVED FROM CLONE
if (result->IsReadOnly() && result->IsProperty()) 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.
// Check of IsReadOnly removed from here in clone.
switch (result->type()) {
case NORMAL:
property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
......@@ -1621,12 +1594,12 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
name,
value);
} else {
return AddFastProperty(name, value, attributes);
return ConvertDescriptorToField(name, value, attributes);
}
case CONSTANT_FUNCTION:
if (value == result->GetConstantFunction()) return value;
// Only replace the function if necessary.
return ReplaceConstantFunctionProperty(name, value);
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case CALLBACKS:
return SetPropertyWithCallback(result->GetCallbackObject(),
name,
......@@ -1637,10 +1610,9 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
case CONSTANT_TRANSITION:
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
// if the value is a function.
// AddProperty has been extended to do this, in this case.
return AddFastProperty(name, value, attributes);
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case NULL_DESCRIPTOR:
UNREACHABLE();
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
default:
UNREACHABLE();
}
......@@ -2663,14 +2635,6 @@ void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
}
void DescriptorArray::ReplaceConstantFunction(int descriptor_number,
JSFunction* value) {
ASSERT(!Heap::InNewSpace(value));
FixedArray* content_array = GetContentArray();
fast_set(content_array, ToValueIndex(descriptor_number), value);
}
Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
TransitionFlag transition_flag) {
// Transitions are only kept when inserting another transition.
......@@ -2771,69 +2735,6 @@ Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
}
Object* DescriptorArray::CopyReplace(String* name,
int index,
PropertyAttributes attributes) {
// Allocate the new descriptor array.
Object* result = DescriptorArray::Allocate(number_of_descriptors());
if (result->IsFailure()) return result;
// Make sure only symbols are added to the instance descriptor.
if (!name->IsSymbol()) {
Object* result = Heap::LookupSymbol(name);
if (result->IsFailure()) return result;
name = String::cast(result);
}
DescriptorWriter w(DescriptorArray::cast(result));
for (DescriptorReader r(this); !r.eos(); r.advance()) {
if (r.Equals(name)) {
FieldDescriptor d(name, index, attributes);
d.SetEnumerationIndex(r.GetDetails().index());
w.Write(&d);
} else {
w.WriteFrom(&r);
}
}
// Copy the next enumeration index.
DescriptorArray::cast(result)->
SetNextEnumerationIndex(NextEnumerationIndex());
ASSERT(w.eos());
return result;
}
Object* DescriptorArray::CopyRemove(String* name) {
if (!name->IsSymbol()) {
Object* result = Heap::LookupSymbol(name);
if (result->IsFailure()) return result;
name = String::cast(result);
}
ASSERT(name->IsSymbol());
Object* result = Allocate(number_of_descriptors() - 1);
if (result->IsFailure()) return result;
DescriptorArray* new_descriptors = DescriptorArray::cast(result);
// Set the enumeration index in the descriptors and set the enumeration index
// in the result.
new_descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
// Write the old content and the descriptor information
DescriptorWriter w(new_descriptors);
DescriptorReader r(this);
while (!r.eos()) {
if (r.GetKey() != name) { // Both are symbols; object identity suffices.
w.WriteFrom(&r);
}
r.advance();
}
ASSERT(w.eos());
return new_descriptors;
}
Object* DescriptorArray::RemoveTransitions() {
// Remove all transitions. Return a copy of the array with all transitions
// removed, or a Failure object if the new array could not be allocated.
......
......@@ -1303,9 +1303,26 @@ class JSObject: public HeapObject {
JSFunction* function,
PropertyAttributes attributes);
// Replace a constant function property on a fast-case object.
Object* ReplaceConstantFunctionProperty(String* name,
Object* value);
Object* ReplaceSlowProperty(String* name,
Object* value,
PropertyAttributes attributes);
// Converts a descriptor of any other type to a real field,
// backed by the properties array. Descriptors of visible
// types, such as CONSTANT_FUNCTION, keep their enumeration order.
// Converts the descriptor on the original object's map to a
// map transition, and the the new field is on the object's new map.
Object* ConvertDescriptorToFieldAndMapTransition(
String* name,
Object* new_value,
PropertyAttributes attributes);
// Converts a descriptor of any other type to a real field,
// backed by the properties array. Descriptors of visible
// types, such as CONSTANT_FUNCTION, keep their enumeration order.
Object* ConvertDescriptorToField(String* name,
Object* new_value,
PropertyAttributes attributes);
// Add a property to a fast-case object.
Object* AddFastProperty(String* name,
......@@ -1377,6 +1394,10 @@ class JSObject: public HeapObject {
static const uint32_t kMaxGap = 1024;
static const int kMaxFastElementsLength = 5000;
static const int kMaxFastProperties = 8;
// When extending the backing storage for property values, we increase
// its size by more than the 1 entry necessary, so sequentially adding fields
// to the same object requires fewer allocations and copies.
static const int kFieldsAdded = 3;
// Layout description.
static const int kPropertiesOffset = HeapObject::kHeaderSize;
......@@ -1562,7 +1583,6 @@ class DescriptorArray: public FixedArray {
inline void Get(int descriptor_number, Descriptor* desc);
inline void Set(int descriptor_number, Descriptor* desc);
void ReplaceConstantFunction(int descriptor_number, JSFunction* value);
// Copy the descriptor array, insert a new descriptor and optionally
// remove map transitions. If the descriptor is already present, it is
......@@ -1572,20 +1592,6 @@ class DescriptorArray: public FixedArray {
// a transition, they must not be removed. All null descriptors are removed.
Object* CopyInsert(Descriptor* descriptor, TransitionFlag transition_flag);
// Makes a copy of the descriptor array with the descriptor with key name
// removed. If name is the empty string, the descriptor array is copied.
// Transitions are removed if TransitionFlag is REMOVE_TRANSITIONS.
// All null descriptors are removed.
Object* CopyRemove(TransitionFlag remove_transitions, String* name);
// Copy the descriptor array, replace the property index and attributes
// of the named property, but preserve its enumeration index.
Object* CopyReplace(String* name, int index, PropertyAttributes attributes);
// Copy the descriptor array, removing the property index and attributes
// of the named property.
Object* CopyRemove(String* name);
// Remove all transitions. Return a copy of the array with all transitions
// removed, or a Failure object if the new array could not be allocated.
Object* RemoveTransitions();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment