HandleScope inner;
Handle<String> key = Handle<String>(stream.GetKey());
int index = stream.GetFieldIndex();
- Handle<Object> value = Handle<Object>(from->properties()->get(index));
+ Handle<Object> value = Handle<Object>(from->FastPropertyAt(index));
SetProperty(to, key, value, details.attributes());
break;
}
// edi: constructor
// eax: initial map
__ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset));
+ __ shl(edi, kPointerSizeLog2);
// Make sure that the maximum heap object size will never cause us
// problem here, because it is always greater than the maximum
// instance size that can be represented in a byte.
// ebx: JSObject
// edi: start of next object
__ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
+ __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
+ // Calculate unused properties past the end of the in-object properties.
+ __ sub(edx, Operand(ecx));
__ test(edx, Operand(edx));
- // Done if no unused properties are to be allocated.
+ // Done if no extra properties are to be allocated.
__ j(zero, &allocated);
// Scale the number of elements by pointer size and add the header for
reinterpret_cast<Map*>(result)->set_map(meta_map());
reinterpret_cast<Map*>(result)->set_instance_type(instance_type);
reinterpret_cast<Map*>(result)->set_instance_size(instance_size);
+ reinterpret_cast<Map*>(result)->set_inobject_properties(0);
reinterpret_cast<Map*>(result)->set_unused_property_fields(0);
return result;
}
map->set_prototype(null_value());
map->set_constructor(null_value());
map->set_instance_size(instance_size);
+ map->set_inobject_properties(0);
map->set_instance_descriptors(empty_descriptor_array());
map->set_code_cache(empty_fixed_array());
map->set_unused_property_fields(0);
Object* Heap::AllocateInitialMap(JSFunction* fun) {
ASSERT(!fun->has_initial_map());
- // First create a new map.
- Object* map_obj = Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ // First create a new map with the expected number of properties being
+ // allocated in-object.
+ int expected_nof_properties = fun->shared()->expected_nof_properties();
+ Object* map_obj = Heap::AllocateMap(JS_OBJECT_TYPE,
+ JSObject::kHeaderSize + expected_nof_properties * kPointerSize);
if (map_obj->IsFailure()) return map_obj;
// Fetch or allocate prototype.
if (prototype->IsFailure()) return prototype;
}
Map* map = Map::cast(map_obj);
- map->set_unused_property_fields(fun->shared()->expected_nof_properties());
+ map->set_inobject_properties(expected_nof_properties);
+ map->set_unused_property_fields(expected_nof_properties);
map->set_prototype(prototype);
return map;
}
ASSERT(map->instance_type() != JS_FUNCTION_TYPE);
// Allocate the backing storage for the properties.
- Object* properties = AllocateFixedArray(map->unused_property_fields());
+ int prop_size = map->unused_property_fields() - map->inobject_properties();
+ Object* properties = AllocateFixedArray(prop_size);
if (properties->IsFailure()) return properties;
// Allocate the JSObject.
ASSERT(map->instance_size() == object->map()->instance_size());
// Allocate the backing storage for the properties.
- Object* properties = AllocateFixedArray(map->unused_property_fields());
+ int prop_size = map->unused_property_fields() - map->inobject_properties();
+ Object* properties = AllocateFixedArray(prop_size);
if (properties->IsFailure()) return properties;
// Reset the map for the object.
r.GetKey()->StringPrint();
PrintF(": ");
if (r.type() == FIELD) {
- properties()->get(r.GetFieldIndex())->ShortPrint();
+ FastPropertyAt(r.GetFieldIndex())->ShortPrint();
PrintF(" (field at offset %d)\n", r.GetFieldIndex());
} else if (r.type() == CONSTANT_FUNCTION) {
r.GetConstantFunction()->ShortPrint();
VerifyHeapPointer(elements());
if (HasFastProperties()) {
CHECK(map()->unused_property_fields() ==
- (properties()->length() - map()->NextFreePropertyIndex()));
+ (map()->inobject_properties() + properties()->length() -
+ map()->NextFreePropertyIndex()));
}
}
int JSObject::GetInternalFieldCount() {
ASSERT(1 << kPointerSizeLog2 == kPointerSize);
- return (Size() - GetHeaderSize()) >> kPointerSizeLog2;
+ // Make sure to adjust for the number of in-object properties. These
+ // properties do contribute to the size, but are not internal fields.
+ return ((Size() - GetHeaderSize()) >> kPointerSizeLog2) -
+ map()->inobject_properties();
}
Object* JSObject::GetInternalField(int index) {
ASSERT(index < GetInternalFieldCount() && index >= 0);
+ // Internal objects do follow immediately after the header, whereas in-object
+ // properties are at the end of the object. Therefore there is no need
+ // to adjust the index here.
return READ_FIELD(this, GetHeaderSize() + (kPointerSize * index));
}
void JSObject::SetInternalField(int index, Object* value) {
ASSERT(index < GetInternalFieldCount() && index >= 0);
+ // Internal objects do follow immediately after the header, whereas in-object
+ // properties are at the end of the object. Therefore there is no need
+ // to adjust the index here.
int offset = GetHeaderSize() + (kPointerSize * index);
WRITE_FIELD(this, offset, value);
WRITE_BARRIER(this, offset);
}
+// Access fast-case object properties at index. The use of these routines
+// is needed to correctly distinguish between properties stored in-object and
+// properties stored in the properties array.
+inline Object* JSObject::FastPropertyAt(int index) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ if (index < 0) {
+ int offset = map()->instance_size() + (index * kPointerSize);
+ return READ_FIELD(this, offset);
+ } else {
+ ASSERT(index < properties()->length());
+ return properties()->get(index);
+ }
+}
+
+
+inline Object* JSObject::FastPropertyAtPut(int index, Object* value) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ if (index < 0) {
+ int offset = map()->instance_size() + (index * kPointerSize);
+ WRITE_FIELD(this, offset, value);
+ WRITE_BARRIER(this, offset);
+ } else {
+ ASSERT(index < properties()->length());
+ properties()->set(index, value);
+ }
+ return value;
+}
+
+
void JSObject::InitializeBody(int object_size) {
for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
WRITE_FIELD(this, offset, Heap::undefined_value());
int Map::instance_size() {
- return READ_BYTE_FIELD(this, kInstanceSizeOffset);
+ return READ_BYTE_FIELD(this, kInstanceSizeOffset) << kPointerSizeLog2;
+}
+
+
+int Map::inobject_properties() {
+ return READ_BYTE_FIELD(this, kInObjectPropertiesOffset);
}
void Map::set_instance_size(int value) {
+ ASSERT((value & ~(kPointerSize - 1)) == value);
+ value >>= kPointerSizeLog2;
ASSERT(0 <= value && value < 256);
WRITE_BYTE_FIELD(this, kInstanceSizeOffset, static_cast<byte>(value));
}
+void Map::set_inobject_properties(int value) {
+ ASSERT(0 <= value && value < 256);
+ WRITE_BYTE_FIELD(this, kInObjectPropertiesOffset, static_cast<byte>(value));
+}
+
+
InstanceType Map::instance_type() {
return static_cast<InstanceType>(READ_BYTE_FIELD(this, kInstanceTypeOffset));
}
ASSERT(!value->IsTheHole() || result->IsReadOnly());
return value->IsTheHole() ? Heap::undefined_value() : value;
case FIELD:
- value = holder->properties()->get(result->GetFieldIndex());
+ value = holder->FastPropertyAt(result->GetFieldIndex());
ASSERT(!value->IsTheHole() || result->IsReadOnly());
return value->IsTheHole() ? Heap::undefined_value() : value;
case CONSTANT_FUNCTION:
String* name,
Object* value) {
int index = new_map->PropertyIndexFor(name);
- if (map()->unused_property_fields() > 0) {
- ASSERT(index < properties()->length());
- properties()->set(index, value);
- } else {
+ if (map()->unused_property_fields() == 0) {
ASSERT(map()->unused_property_fields() == 0);
int new_unused = new_map->unused_property_fields();
Object* values =
properties()->CopySize(properties()->length() + new_unused + 1);
if (values->IsFailure()) return values;
- FixedArray::cast(values)->set(index, value);
set_properties(FixedArray::cast(values));
}
set_map(new_map);
- return value;
+ return FastPropertyAtPut(index, value);
}
!old_descriptors->Contains(name) &&
(Top::context()->global_context()->object_function()->map() != map());
- ASSERT(index < properties()->length() ||
+ 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 = map()->Copy();
old_descriptors = DescriptorArray::cast(r);
}
-
if (map()->unused_property_fields() == 0) {
if (properties()->length() > kMaxFastProperties) {
Object* obj = NormalizeProperties();
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;
+ return FastPropertyAtPut(index, value);
}
if (new_properties) {
set_properties(FixedArray::cast(new_properties));
}
- properties()->set(index, new_value);
- return new_value;
+ return FastPropertyAtPut(index, new_value);
}
// Disallow caching for uninitialized constants. These can only
// occur as fields.
if (result->IsReadOnly() && result->type() == FIELD &&
- properties()->get(result->GetFieldIndex())->IsTheHole()) {
+ FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
result->DisallowCaching();
}
return;
property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
return value;
case FIELD:
- properties()->set(result->GetFieldIndex(), value);
- return value;
+ return FastPropertyAtPut(result->GetFieldIndex(), value);
case MAP_TRANSITION:
if (attributes == result->GetAttributes()) {
// Only use map transition if the attributes match.
property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
return value;
case FIELD:
- properties()->set(result->GetFieldIndex(), value);
- return value;
+ return FastPropertyAtPut(result->GetFieldIndex(), value);
case MAP_TRANSITION:
if (attributes == result->GetAttributes()) {
// Only use map transition if the attributes match.
case FIELD: {
PropertyDetails d =
PropertyDetails(details.attributes(), NORMAL, details.index());
- Object* value = properties()->get(r.GetFieldIndex());
+ Object* value = FastPropertyAt(r.GetFieldIndex());
Object* result = dictionary->AddStringEntry(r.GetKey(), value, d);
if (result->IsFailure()) return result;
dictionary = Dictionary::cast(result);
!r.eos();
r.advance()) {
if (r.type() == FIELD) {
- if (properties()->get(r.GetFieldIndex()) == value) {
+ if (FastPropertyAt(r.GetFieldIndex()) == value) {
return r.GetKey();
}
} else if (r.type() == CONSTANT_FUNCTION) {
// Don't copy descriptors, so map transitions always remain a forest.
Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
// 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());
Map::cast(result)->set_bit_field(bit_field());
Map::cast(result)->ClearCodeCache();
// Returns failure if allocation failed.
Object* TransformToFastProperties(int unused_property_fields);
+ // Access fast-case object properties at index.
+ inline Object* FastPropertyAt(int index);
+ inline Object* FastPropertyAtPut(int index, Object* value);
+
+
// initializes the body after properties slot, properties slot is
// initialized by set_properties
// Note: this call does not update write barrier, it is caller's
inline int instance_size();
inline void set_instance_size(int value);
+ // Count of properties allocated in the object.
+ inline int inobject_properties();
+ inline void set_inobject_properties(int value);
+
// instance type.
inline InstanceType instance_type();
inline void set_instance_type(InstanceType value);
#endif
// Layout description.
- static const int kInstanceAttributesOffset = HeapObject::kHeaderSize;
+ static const int kInstanceSizesOffset = HeapObject::kHeaderSize;
+ static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize;
static const int kPrototypeOffset = kInstanceAttributesOffset + kIntSize;
static const int kConstructorOffset = kPrototypeOffset + kPointerSize;
static const int kInstanceDescriptorsOffset =
static const int kCodeCacheOffset = kInstanceDescriptorsOffset + kPointerSize;
static const int kSize = kCodeCacheOffset + kIntSize;
+ // Byte offsets within kInstanceSizesOffset.
+ static const int kInstanceSizeOffset = kInstanceSizesOffset + 0;
+ 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 kInstanceSizeOffset = kInstanceAttributesOffset + 0;
- static const int kInstanceTypeOffset = kInstanceAttributesOffset + 1;
- static const int kUnusedPropertyFieldsOffset = kInstanceAttributesOffset + 2;
- static const int kBitFieldOffset = kInstanceAttributesOffset + 3;
+ static const int kInstanceTypeOffset = kInstanceAttributesOffset + 0;
+ static const int kUnusedPropertyFieldsOffset = kInstanceAttributesOffset + 1;
+ static const int kBitFieldOffset = kInstanceAttributesOffset + 2;
+ // The byte at position 3 is not in use at the moment.
// Bit positions for bit field.
static const int kUnused = 0; // To be used for marking recently used maps.
case FIELD:
value =
JSObject::cast(
- result->holder())->properties()->get(result->GetFieldIndex());
+ result->holder())->FastPropertyAt(result->GetFieldIndex());
if (value->IsTheHole()) {
return Heap::undefined_value();
}
key->ShortPrint();
}
Add(": ");
- Object* value = js_object->properties()->get(r.GetFieldIndex());
+ Object* value = js_object->FastPropertyAt(r.GetFieldIndex());
Add("%o\n", value);
}
}
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_ExtendStorage));
__ Jump(ic, RelocInfo::CODE_TARGET);
} else {
- // Get the properties array
- __ ldr(r1, FieldMemOperand(r3, JSObject::kPropertiesOffset));
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ if (index >= 0) {
+ // Get the properties array
+ __ ldr(r1, FieldMemOperand(r3, JSObject::kPropertiesOffset));
+ }
if (transition != NULL) {
// Update the map of the object; no write barrier updating is
__ str(ip, FieldMemOperand(r3, HeapObject::kMapOffset));
}
- // Write to the properties array.
- int offset = index * kPointerSize + Array::kHeaderSize;
- __ str(r0, FieldMemOperand(r1, offset));
-
- // Skip updating write barrier if storing a smi.
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &exit);
-
- // Update the write barrier for the array address.
- __ mov(r3, Operand(offset));
- __ RecordWrite(r1, r3, r2); // OK to clobber r2, since we return
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ str(r0, FieldMemOperand(r3, offset));
+
+ // Skip updating write barrier if storing a smi.
+ __ tst(r0, Operand(kSmiTagMask));
+ __ b(eq, &exit);
+
+ // Update the write barrier for the array address.
+ __ mov(r1, Operand(offset));
+ __ RecordWrite(r3, r1, r2);
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + Array::kHeaderSize;
+ __ str(r0, FieldMemOperand(r1, offset));
+
+ // Skip updating write barrier if storing a smi.
+ __ tst(r0, Operand(kSmiTagMask));
+ __ b(eq, &exit);
+
+ // Update the write barrier for the array address.
+ __ mov(r3, Operand(offset));
+ __ RecordWrite(r1, r3, r2); // OK to clobber r2, since we return
+ }
// Return the value (register r0).
__ bind(&exit);
// Check that the maps haven't changed.
Register reg = __ CheckMaps(object, r0, holder, r3, r1, &miss);
- // Get the properties array of the holder.
- __ ldr(r3, FieldMemOperand(reg, JSObject::kPropertiesOffset));
-
- // Return the value from the properties array.
- int offset = index * kPointerSize + Array::kHeaderSize;
- __ ldr(r0, FieldMemOperand(r3, offset));
+ // Adjust for the number of properties stored in the holder.
+ index -= holder->map()->inobject_properties();
+ if (index < 0) {
+ // Get the property straight out of the holder.
+ int offset = holder->map()->instance_size() + (index * kPointerSize);
+ __ ldr(r0, FieldMemOperand(reg, offset));
+ } else {
+ // Get the properties array of the holder.
+ __ ldr(r3, FieldMemOperand(reg, JSObject::kPropertiesOffset));
+ // Return the value from the properties array.
+ int offset = index * kPointerSize + Array::kHeaderSize;
+ __ ldr(r0, FieldMemOperand(r3, offset));
+ }
__ Ret();
// Handle load cache miss.
Register reg =
__ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
- // Get the properties array of the holder.
- __ mov(scratch1, FieldOperand(reg, JSObject::kPropertiesOffset));
-
- // Return the value from the properties array.
- int offset = index * kPointerSize + Array::kHeaderSize;
- __ mov(eax, FieldOperand(scratch1, offset));
+ // Adjust for the number of properties stored in the holder.
+ index -= holder->map()->inobject_properties();
+ if (index < 0) {
+ // Get the property straight out of the holder.
+ int offset = holder->map()->instance_size() + (index * kPointerSize);
+ __ mov(eax, FieldOperand(reg, offset));
+ } else {
+ // Get the properties array of the holder.
+ __ mov(scratch1, FieldOperand(reg, JSObject::kPropertiesOffset));
+ // Return the value from the properties array.
+ int offset = index * kPointerSize + Array::kHeaderSize;
+ __ mov(eax, FieldOperand(scratch1, offset));
+ }
__ ret(0);
}
return;
}
- // Get the properties array (optimistically).
- __ mov(scratch, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ if (index >= 0) {
+ // Get the properties array (optimistically).
+ __ mov(scratch, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ }
+
if (transition != NULL) {
// Update the map of the object; no write barrier updating is
// needed because the map is never in new space.
Immediate(Handle<Map>(transition)));
}
- // Write to the properties array.
- int offset = index * kPointerSize + Array::kHeaderSize;
- __ mov(FieldOperand(scratch, offset), eax);
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ mov(FieldOperand(receiver_reg, offset), eax);
- // Update the write barrier for the array address.
- // Pass the value being stored in the now unused name_reg.
- __ mov(name_reg, Operand(eax));
- __ RecordWrite(scratch, offset, name_reg, receiver_reg);
+ // Update the write barrier for the array address.
+ // Pass the value being stored in the now unused name_reg.
+ __ mov(name_reg, Operand(eax));
+ __ RecordWrite(receiver_reg, offset, name_reg, scratch);
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + Array::kHeaderSize;
+ __ mov(FieldOperand(scratch, offset), eax);
+
+ // Update the write barrier for the array address.
+ // Pass the value being stored in the now unused name_reg.
+ __ mov(name_reg, Operand(eax));
+ __ RecordWrite(scratch, offset, name_reg, receiver_reg);
+ }
// Return the value (register eax).
__ ret(0);