}
+Handle<Map> Factory::CopyMap(Handle<Map> src,
+ int extra_inobject_properties) {
+ Handle<Map> copy = CopyMap(src);
+ // Check that we do not overflow the instance size when adding the
+ // extra inobject properties.
+ int instance_size_delta = extra_inobject_properties * kPointerSize;
+ int max_instance_size_delta =
+ JSObject::kMaxInstanceSize - copy->instance_size();
+ if (instance_size_delta > max_instance_size_delta) {
+ // If the instance size overflows, we allocate as many properties
+ // as we can as inobject properties.
+ instance_size_delta = max_instance_size_delta;
+ extra_inobject_properties = max_instance_size_delta >> kPointerSizeLog2;
+ }
+ // Adjust the map with the extra inobject properties.
+ int inobject_properties =
+ copy->inobject_properties() + extra_inobject_properties;
+ copy->set_inobject_properties(inobject_properties);
+ copy->set_unused_property_fields(inobject_properties);
+ copy->set_instance_size(copy->instance_size() + instance_size_delta);
+ return copy;
+}
+
Handle<Map> Factory::CopyMapDropTransitions(Handle<Map> src) {
CALL_HEAP_FUNCTION(src->CopyDropTransitions(), Map);
}
}
-Handle<JSObject> Factory::NewObjectLiteral(int expected_number_of_properties) {
- Handle<Map> map = Handle<Map>(Top::object_function()->initial_map());
- map = Factory::CopyMap(map);
- map->set_instance_descriptors(Heap::empty_descriptor_array());
- map->set_unused_property_fields(expected_number_of_properties);
- CALL_HEAP_FUNCTION(Heap::AllocateJSObjectFromMap(*map, TENURED),
- JSObject);
-}
-
-
Handle<JSArray> Factory::NewArrayLiteral(int length) {
return NewJSArrayWithElements(NewFixedArray(length), TENURED);
}
if (result->IsMap()) return Handle<Map>::cast(result);
// Create a new map and add it to the cache.
Handle<Map> map =
- CopyMap(Handle<Map>(context->object_function()->initial_map()));
+ CopyMap(Handle<Map>(context->object_function()->initial_map()),
+ keys->length());
AddToMapCache(context, keys, map);
return Handle<Map>(map);
}
static Handle<Map> CopyMap(Handle<Map> map);
+ // Copy the map adding more inobject properties if possible without
+ // overflowing the instance size.
+ static Handle<Map> CopyMap(Handle<Map> map, int extra_inobject_props);
+
static Handle<Map> CopyMapDropTransitions(Handle<Map> map);
static Handle<FixedArray> CopyFixedArray(Handle<FixedArray> array);
// runtime.
static Handle<JSObject> NewJSObjectFromMap(Handle<Map> map);
- // Allocate a JS object representing an object literal. The object is
- // pretenured (allocated directly in the old generation).
- static Handle<JSObject> NewObjectLiteral(int expected_number_of_properties);
-
// Allocate a JS array representing an array literal. The array is
// pretenured (allocated directly in the old generation).
static Handle<JSArray> NewArrayLiteral(int length);
}
-void NormalizeProperties(Handle<JSObject> object) {
- CALL_HEAP_FUNCTION_VOID(object->NormalizeProperties());
+void NormalizeProperties(Handle<JSObject> object,
+ PropertyNormalizationMode mode) {
+ CALL_HEAP_FUNCTION_VOID(object->NormalizeProperties(mode));
}
// Normalize the properties of object to avoid n^2 behavior
// when extending the object multiple properties.
unused_property_fields_ = object->map()->unused_property_fields();
- NormalizeProperties(object_);
+ NormalizeProperties(object_, KEEP_INOBJECT_PROPERTIES);
has_been_transformed_ = true;
} else {
// an object of expected type, or the handle is an error if running out
// of space or encounting an internal error.
-void NormalizeProperties(Handle<JSObject> object);
+void NormalizeProperties(Handle<JSObject> object,
+ PropertyNormalizationMode mode);
void NormalizeElements(Handle<JSObject> object);
void TransformToFastProperties(Handle<JSObject> object,
int unused_property_fields);
Object* Heap::AllocateFixedArray(int length) {
+ if (length == 0) return empty_fixed_array();
Object* result = AllocateRawFixedArray(length);
if (!result->IsFailure()) {
// Initialize header.
namespace v8 { namespace internal {
+#define FIELD_ADDR(p, offset) \
+ (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
+
+
+#define WRITE_FIELD(p, offset, value) \
+ (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
+
+
+#define WRITE_INT_FIELD(p, offset, value) \
+ (*reinterpret_cast<int*>(FIELD_ADDR(p, offset)) = value)
+
+
+#define WRITE_BARRIER(object, offset) \
+ Heap::RecordWrite(object->address(), offset);
+
+
// Getters and setters are stored in a fixed array property. These are
// constants for their indices.
const int kGetterIndex = 0;
// Normalize the object if the name is not a real identifier.
StringInputBuffer buffer(name);
if (!Scanner::IsIdentifier(&buffer)) {
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
return AddSlowProperty(name, value, attributes);
}
if (map()->unused_property_fields() == 0) {
if (properties()->length() > kMaxFastProperties) {
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
return AddSlowProperty(name, value, attributes);
}
} else {
// Normalize the object to prevent very large instance descriptors.
// This eliminates unwanted N^2 allocation and lookup behavior.
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
}
}
PropertyAttributes attributes) {
if (map()->unused_property_fields() == 0 &&
properties()->length() > kMaxFastProperties) {
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
return ReplaceSlowProperty(name, new_value, attributes);
}
}
-Object* JSObject::NormalizeProperties() {
+Object* JSObject::NormalizeProperties(PropertyNormalizationMode mode) {
if (!HasFastProperties()) return this;
// Allocate new content
// Allocate new map.
obj = map()->Copy();
if (obj->IsFailure()) return obj;
+ Map* new_map = Map::cast(obj);
+
+ // Clear inobject properties if needed by adjusting the instance
+ // size and putting in a filler or byte array instead of the
+ // inobject properties.
+ if (mode == CLEAR_INOBJECT_PROPERTIES && map()->inobject_properties() > 0) {
+ int instance_size_delta = map()->inobject_properties() * kPointerSize;
+ int new_instance_size = map()->instance_size() - instance_size_delta;
+ new_map->set_inobject_properties(0);
+ new_map->set_instance_size(new_instance_size);
+ if (instance_size_delta == kPointerSize) {
+ WRITE_FIELD(this, new_instance_size, Heap::one_word_filler_map());
+ } else {
+ int byte_array_length = ByteArray::LengthFor(instance_size_delta);
+ int byte_array_length_offset = new_instance_size + kPointerSize;
+ WRITE_FIELD(this, new_instance_size, Heap::byte_array_map());
+ WRITE_INT_FIELD(this, byte_array_length_offset, byte_array_length);
+ }
+ WRITE_BARRIER(this, new_instance_size);
+ }
+ new_map->set_unused_property_fields(0);
// We have now sucessfully allocated all the necessary objects.
// Changes can now be made with the guarantee that all of them take effect.
- set_map(Map::cast(obj));
+ set_map(new_map);
map()->set_instance_descriptors(Heap::empty_descriptor_array());
- map()->set_unused_property_fields(0);
set_properties(dictionary);
Counters::props_to_dictionary.Increment();
if (!result.IsValid()) return Heap::true_value();
// Normalize object if needed.
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
ASSERT(!HasFastProperties());
return JSObject::cast(this)->DeleteLazyProperty(&result, name);
}
// Normalize object if needed.
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
// Make sure the properties are normalized before removing the entry.
Dictionary* dictionary = property_dictionary();
}
// Normalize object to make this operation simple.
- Object* ok = NormalizeProperties();
+ Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (ok->IsFailure()) return ok;
// Allocate the fixed array to hold getter and setter.
if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
- int number_of_allocated_fields = number_of_fields + unused_property_fields;
+ int inobject_props = obj->map()->inobject_properties();
+ int number_of_allocated_fields =
+ number_of_fields + unused_property_fields - inobject_props;
// Allocate the fixed array for the fields.
Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields);
if (key->IsFailure()) return key;
PropertyDetails details = DetailsAt(i);
PropertyType type = details.type();
+
if (value->IsJSFunction()) {
ConstantFunctionDescriptor d(String::cast(key),
JSFunction::cast(value),
details.index());
w.Write(&d);
} else if (type == NORMAL) {
- FixedArray::cast(fields)->set(current_offset, value);
+ if (current_offset < inobject_props) {
+ obj->InObjectPropertyAtPut(current_offset,
+ value,
+ UPDATE_WRITE_BARRIER);
+ } else {
+ int offset = current_offset - inobject_props;
+ FixedArray::cast(fields)->set(offset, value);
+ }
FieldDescriptor d(String::cast(key),
current_offset++,
details.attributes(),
ASSERT(obj->IsJSObject());
descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
- // Check it really works.
+ // Check that it really works.
ASSERT(obj->HasFastProperties());
+
return obj;
}
uint32_t value_;
};
+
// Setter that skips the write barrier if mode is SKIP_WRITE_BARRIER.
enum WriteBarrierMode { SKIP_WRITE_BARRIER, UPDATE_WRITE_BARRIER };
+
+// PropertyNormalizationMode is used to specify wheter or not to
+// keep inobject properties when normalizing properties of a
+// JSObject.
+enum PropertyNormalizationMode {
+ CLEAR_INOBJECT_PROPERTIES,
+ KEEP_INOBJECT_PROPERTIES
+};
+
+
// All Maps have a field instance_type containing a InstanceType.
// It describes the type of the instances.
//
inline void set_##name(bool value); \
-#define DECL_ACCESSORS(name, type) \
- inline type* name(); \
- inline void set_##name(type* value, \
+#define DECL_ACCESSORS(name, type) \
+ inline type* name(); \
+ inline void set_##name(type* value, \
WriteBarrierMode mode = UPDATE_WRITE_BARRIER); \
// Convert the object to use the canonical dictionary
// representation.
- Object* NormalizeProperties();
+ Object* NormalizeProperties(PropertyNormalizationMode mode);
Object* NormalizeElements();
// Transform slow named properties to fast variants.
// - How to iterate over an object (for garbage collection)
class Map: public HeapObject {
public:
- // instance size.
+ // Instance size.
inline int instance_size();
inline void set_instance_size(int value);
inline int inobject_properties();
inline void set_inobject_properties(int value);
- // instance type.
+ // Instance type.
inline InstanceType instance_type();
inline void set_instance_type(InstanceType value);
- // tells how many unused property fields are available in the instance.
- // (only used for JSObject in fast mode).
+ // Tells how many unused property fields are available in the
+ // instance (only used for JSObject in fast mode).
inline int unused_property_fields();
inline void set_unused_property_fields(int value);
- // bit field.
+ // Bit field.
inline byte bit_field();
inline void set_bit_field(byte value);
static const int kInObjectPropertiesOffset = kInstanceSizesOffset + 1;
// The bytes at positions 2 and 3 are not in use at the moment.
-
// Byte offsets within kInstanceAttributesOffset attributes.
static const int kInstanceTypeOffset = kInstanceAttributesOffset + 0;
static const int kUnusedPropertyFieldsOffset = kInstanceAttributesOffset + 1;
Handle<Context> context,
Handle<FixedArray> constant_properties,
bool* is_result_from_cache) {
+ int number_of_properties = constant_properties->length() / 2;
if (FLAG_canonicalize_object_literal_maps) {
// First find prefix of consecutive symbol keys.
- int number_of_properties = constant_properties->length()/2;
int number_of_symbol_keys = 0;
while ((number_of_symbol_keys < number_of_properties) &&
(constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
}
}
*is_result_from_cache = false;
- return Handle<Map>(context->object_function()->initial_map());
+ return Factory::CopyMap(
+ Handle<Map>(context->object_function()->initial_map()),
+ number_of_properties);
}
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Make sure that we can create large object literals.
-var nofProperties = 150;
+// Test that we can create object literals of various sizes.
+function testLiteral(size) {
-// Build large object literal string.
-var literal = "var o = { ";
-
-for (var i = 0; i < nofProperties; i++) {
- if (i > 0) literal += ",";
- literal += ("a" + i + ":" + i);
-}
-literal += "}";
+ // Build object-literal string.
+ var literal = "var o = { ";
+ for (var i = 0; i < size; i++) {
+ if (i > 0) literal += ",";
+ literal += ("a" + i + ":" + i);
+ }
+ literal += "}";
-// Create the large object literal
-eval(literal);
+ // Create the object literal.
+ eval(literal);
-// Check that the properties have the expected values.
-for (var i = 0; i < nofProperties; i++) {
- assertEquals(o["a"+i], i);
+ // Check that the properties have the expected values.
+ for (var i = 0; i < size; i++) {
+ assertEquals(i, o["a"+i]);
+ }
}
+// The sizes to test.
+var sizes = [0, 1, 2, 100, 200, 400, 1000];
+
+// Run the test.
+for (var i = 0; i < sizes.length; i++) {
+ testLiteral(sizes[i]);
+}
--- /dev/null
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-gc
+
+// Test that the clearing of object literal when normalizing objects
+// works. In particular, test that the garbage collector handles the
+// normalized object literals correctly.
+function testLiteral(size) {
+
+ // Build object-literal string.
+ var literal = "var o = { ";
+
+ for (var i = 0; i < size; i++) {
+ if (i > 0) literal += ",";
+ literal += ("a" + i + ":" + i);
+ }
+ literal += "}";
+
+ // Create the object literal.
+ eval(literal);
+
+ // Force normalization of the properties.
+ delete o["a" + (size - 1)];
+
+ // Perform GC.
+ gc();
+
+ // Check that the properties have the expected values.
+ for (var i = 0; i < size - 1; i++) {
+ assertEquals(i, o["a"+i]);
+ }
+}
+
+// The sizes to test.
+var sizes = [0, 1, 2, 100, 200, 400, 1000];
+
+// Run the test.
+for (var i = 0; i < sizes.length; i++) {
+ testLiteral(sizes[i]);
+}
+