Ensure that all descriptors have a valid enumeration index, and replace NextEnumIndex with LastAdded.
The LastAdded points to the descriptor that was last added to the array. From the descriptor we can deduce the NextEnumerationIndex. This allows us to quickly find the property that we are transitioning to, which is necessary for transition-intensive code, eg JSON parsing. Review URL: https://chromiumcodereview.appspot.com/10695120 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12042 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
dcf83a2114
commit
b008d99b11
@ -1142,7 +1142,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
||||
// We got a map in register r0. Get the enumeration cache from it.
|
||||
__ bind(&use_cache);
|
||||
__ LoadInstanceDescriptors(r0, r1, r2);
|
||||
__ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
|
||||
__ ldr(r1, FieldMemOperand(r1, DescriptorArray::kLastAddedOffset));
|
||||
__ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
|
||||
|
||||
// Set up the four remaining stack slots.
|
||||
|
@ -5360,7 +5360,7 @@ void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
|
||||
Register scratch = ToRegister(instr->scratch());
|
||||
__ LoadInstanceDescriptors(map, result, scratch);
|
||||
__ ldr(result,
|
||||
FieldMemOperand(result, DescriptorArray::kEnumerationIndexOffset));
|
||||
FieldMemOperand(result, DescriptorArray::kLastAddedOffset));
|
||||
__ ldr(result,
|
||||
FieldMemOperand(result, FixedArray::SizeFor(instr->idx())));
|
||||
__ cmp(result, Operand(0));
|
||||
|
@ -3723,7 +3723,7 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
|
||||
// Check that there is an enum cache in the non-empty instance
|
||||
// descriptors (r3). This is the case if the next enumeration
|
||||
// index field does not contain a smi.
|
||||
ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumerationIndexOffset));
|
||||
ldr(r3, FieldMemOperand(r3, DescriptorArray::kLastAddedOffset));
|
||||
JumpIfSmi(r3, call_runtime);
|
||||
|
||||
// For all objects but the receiver, check that the cache is empty.
|
||||
|
@ -390,25 +390,32 @@ Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor(
|
||||
|
||||
DescriptorArray::WhitenessWitness witness(*descriptors);
|
||||
|
||||
int index = 0;
|
||||
|
||||
{ // Add length.
|
||||
Handle<Foreign> f(factory()->NewForeign(&Accessors::FunctionLength));
|
||||
CallbacksDescriptor d(*factory()->length_symbol(), *f, attribs);
|
||||
descriptors->Set(0, &d, witness);
|
||||
CallbacksDescriptor d(*factory()->length_symbol(), *f, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
{ // Add name.
|
||||
Handle<Foreign> f(factory()->NewForeign(&Accessors::FunctionName));
|
||||
CallbacksDescriptor d(*factory()->name_symbol(), *f, attribs);
|
||||
descriptors->Set(1, &d, witness);
|
||||
CallbacksDescriptor d(*factory()->name_symbol(), *f, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
{ // Add arguments.
|
||||
Handle<Foreign> f(factory()->NewForeign(&Accessors::FunctionArguments));
|
||||
CallbacksDescriptor d(*factory()->arguments_symbol(), *f, attribs);
|
||||
descriptors->Set(2, &d, witness);
|
||||
CallbacksDescriptor d(
|
||||
*factory()->arguments_symbol(), *f, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
{ // Add caller.
|
||||
Handle<Foreign> f(factory()->NewForeign(&Accessors::FunctionCaller));
|
||||
CallbacksDescriptor d(*factory()->caller_symbol(), *f, attribs);
|
||||
descriptors->Set(3, &d, witness);
|
||||
CallbacksDescriptor d(*factory()->caller_symbol(), *f, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
if (prototypeMode != DONT_ADD_PROTOTYPE) {
|
||||
// Add prototype.
|
||||
@ -416,9 +423,11 @@ Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor(
|
||||
attribs = static_cast<PropertyAttributes>(attribs & ~READ_ONLY);
|
||||
}
|
||||
Handle<Foreign> f(factory()->NewForeign(&Accessors::FunctionPrototype));
|
||||
CallbacksDescriptor d(*factory()->prototype_symbol(), *f, attribs);
|
||||
descriptors->Set(4, &d, witness);
|
||||
CallbacksDescriptor d(
|
||||
*factory()->prototype_symbol(), *f, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
}
|
||||
|
||||
descriptors->Sort(witness);
|
||||
return descriptors;
|
||||
}
|
||||
@ -533,25 +542,32 @@ Handle<DescriptorArray> Genesis::ComputeStrictFunctionInstanceDescriptor(
|
||||
|
||||
DescriptorArray::WhitenessWitness witness(*descriptors);
|
||||
|
||||
int index = 0;
|
||||
{ // Add length.
|
||||
Handle<Foreign> f(factory()->NewForeign(&Accessors::FunctionLength));
|
||||
CallbacksDescriptor d(*factory()->length_symbol(), *f, attribs);
|
||||
descriptors->Set(0, &d, witness);
|
||||
CallbacksDescriptor d(*factory()->length_symbol(), *f, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
{ // Add name.
|
||||
Handle<Foreign> f(factory()->NewForeign(&Accessors::FunctionName));
|
||||
CallbacksDescriptor d(*factory()->name_symbol(), *f, attribs);
|
||||
descriptors->Set(1, &d, witness);
|
||||
CallbacksDescriptor d(*factory()->name_symbol(), *f, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
{ // Add arguments.
|
||||
Handle<AccessorPair> arguments(factory()->NewAccessorPair());
|
||||
CallbacksDescriptor d(*factory()->arguments_symbol(), *arguments, attribs);
|
||||
descriptors->Set(2, &d, witness);
|
||||
CallbacksDescriptor d(
|
||||
*factory()->arguments_symbol(), *arguments, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
{ // Add caller.
|
||||
Handle<AccessorPair> caller(factory()->NewAccessorPair());
|
||||
CallbacksDescriptor d(*factory()->caller_symbol(), *caller, attribs);
|
||||
descriptors->Set(3, &d, witness);
|
||||
CallbacksDescriptor d(
|
||||
*factory()->caller_symbol(), *caller, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
|
||||
if (prototypeMode != DONT_ADD_PROTOTYPE) {
|
||||
@ -560,8 +576,9 @@ Handle<DescriptorArray> Genesis::ComputeStrictFunctionInstanceDescriptor(
|
||||
attribs = static_cast<PropertyAttributes>(attribs | READ_ONLY);
|
||||
}
|
||||
Handle<Foreign> f(factory()->NewForeign(&Accessors::FunctionPrototype));
|
||||
CallbacksDescriptor d(*factory()->prototype_symbol(), *f, attribs);
|
||||
descriptors->Set(4, &d, witness);
|
||||
CallbacksDescriptor d(
|
||||
*factory()->prototype_symbol(), *f, attribs, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
}
|
||||
|
||||
descriptors->Sort(witness);
|
||||
@ -950,38 +967,42 @@ bool Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
|
||||
DescriptorArray::WhitenessWitness witness(*descriptors);
|
||||
PropertyAttributes final =
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
|
||||
int enum_index = 0;
|
||||
int index = 0;
|
||||
{
|
||||
// ECMA-262, section 15.10.7.1.
|
||||
FieldDescriptor field(heap->source_symbol(),
|
||||
JSRegExp::kSourceFieldIndex,
|
||||
final,
|
||||
enum_index++);
|
||||
descriptors->Set(0, &field, witness);
|
||||
index + 1);
|
||||
descriptors->Set(index, &field, witness);
|
||||
++index;
|
||||
}
|
||||
{
|
||||
// ECMA-262, section 15.10.7.2.
|
||||
FieldDescriptor field(heap->global_symbol(),
|
||||
JSRegExp::kGlobalFieldIndex,
|
||||
final,
|
||||
enum_index++);
|
||||
descriptors->Set(1, &field, witness);
|
||||
index + 1);
|
||||
descriptors->Set(index, &field, witness);
|
||||
++index;
|
||||
}
|
||||
{
|
||||
// ECMA-262, section 15.10.7.3.
|
||||
FieldDescriptor field(heap->ignore_case_symbol(),
|
||||
JSRegExp::kIgnoreCaseFieldIndex,
|
||||
final,
|
||||
enum_index++);
|
||||
descriptors->Set(2, &field, witness);
|
||||
index + 1);
|
||||
descriptors->Set(index, &field, witness);
|
||||
++index;
|
||||
}
|
||||
{
|
||||
// ECMA-262, section 15.10.7.4.
|
||||
FieldDescriptor field(heap->multiline_symbol(),
|
||||
JSRegExp::kMultilineFieldIndex,
|
||||
final,
|
||||
enum_index++);
|
||||
descriptors->Set(3, &field, witness);
|
||||
index + 1);
|
||||
descriptors->Set(index, &field, witness);
|
||||
++index;
|
||||
}
|
||||
{
|
||||
// ECMA-262, section 15.10.7.5.
|
||||
@ -990,10 +1011,9 @@ bool Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
|
||||
FieldDescriptor field(heap->last_index_symbol(),
|
||||
JSRegExp::kLastIndexFieldIndex,
|
||||
writable,
|
||||
enum_index++);
|
||||
descriptors->Set(4, &field, witness);
|
||||
index + 1);
|
||||
descriptors->Set(index, &field, witness);
|
||||
}
|
||||
descriptors->SetNextEnumerationIndex(enum_index);
|
||||
descriptors->Sort(witness);
|
||||
|
||||
initial_map->set_inobject_properties(5);
|
||||
@ -1138,17 +1158,26 @@ bool Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
|
||||
// Create the descriptor array for the arguments object.
|
||||
Handle<DescriptorArray> descriptors = factory->NewDescriptorArray(3);
|
||||
DescriptorArray::WhitenessWitness witness(*descriptors);
|
||||
int index = 0;
|
||||
{ // length
|
||||
FieldDescriptor d(*factory->length_symbol(), 0, DONT_ENUM);
|
||||
descriptors->Set(0, &d, witness);
|
||||
FieldDescriptor d(*factory->length_symbol(), 0, DONT_ENUM, index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
{ // callee
|
||||
CallbacksDescriptor d(*factory->callee_symbol(), *callee, attributes);
|
||||
descriptors->Set(1, &d, witness);
|
||||
CallbacksDescriptor d(*factory->callee_symbol(),
|
||||
*callee,
|
||||
attributes,
|
||||
index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
++index;
|
||||
}
|
||||
{ // caller
|
||||
CallbacksDescriptor d(*factory->caller_symbol(), *caller, attributes);
|
||||
descriptors->Set(2, &d, witness);
|
||||
CallbacksDescriptor d(*factory->caller_symbol(),
|
||||
*caller,
|
||||
attributes,
|
||||
index + 1);
|
||||
descriptors->Set(index, &d, witness);
|
||||
}
|
||||
descriptors->Sort(witness);
|
||||
|
||||
@ -1741,26 +1770,27 @@ bool Genesis::InstallNatives() {
|
||||
JSFunction* array_function = global_context()->array_function();
|
||||
Handle<DescriptorArray> array_descriptors(
|
||||
array_function->initial_map()->instance_descriptors());
|
||||
int index = array_descriptors->SearchWithCache(heap()->length_symbol());
|
||||
int old = array_descriptors->SearchWithCache(heap()->length_symbol());
|
||||
MaybeObject* copy_result =
|
||||
reresult_descriptors->CopyFrom(0, *array_descriptors, index, witness);
|
||||
reresult_descriptors->CopyFrom(0, *array_descriptors, old, witness);
|
||||
if (copy_result->IsFailure()) return false;
|
||||
|
||||
int enum_index = 0;
|
||||
int index = 1;
|
||||
{
|
||||
FieldDescriptor index_field(heap()->index_symbol(),
|
||||
JSRegExpResult::kIndexIndex,
|
||||
NONE,
|
||||
enum_index++);
|
||||
reresult_descriptors->Set(1, &index_field, witness);
|
||||
index + 1);
|
||||
reresult_descriptors->Set(index, &index_field, witness);
|
||||
++index;
|
||||
}
|
||||
|
||||
{
|
||||
FieldDescriptor input_field(heap()->input_symbol(),
|
||||
JSRegExpResult::kInputIndex,
|
||||
NONE,
|
||||
enum_index++);
|
||||
reresult_descriptors->Set(2, &input_field, witness);
|
||||
index + 1);
|
||||
reresult_descriptors->Set(index, &input_field, witness);
|
||||
}
|
||||
reresult_descriptors->Sort(witness);
|
||||
|
||||
|
@ -892,7 +892,7 @@ MUST_USE_RESULT static inline MaybeObject* DoCopyInsert(
|
||||
String* key,
|
||||
Object* value,
|
||||
PropertyAttributes attributes) {
|
||||
CallbacksDescriptor desc(key, value, attributes);
|
||||
CallbacksDescriptor desc(key, value, attributes, 0);
|
||||
MaybeObject* obj = array->CopyInsert(&desc);
|
||||
return obj;
|
||||
}
|
||||
@ -936,6 +936,8 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
|
||||
// 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.
|
||||
int added_descriptor_count = descriptor_count;
|
||||
int next_enum = array->NextEnumerationIndex();
|
||||
for (int i = nof_callbacks - 1; i >= 0; i--) {
|
||||
Handle<AccessorInfo> entry =
|
||||
Handle<AccessorInfo>(AccessorInfo::cast(callbacks.get(i)));
|
||||
@ -943,19 +945,26 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
|
||||
Handle<String> key =
|
||||
SymbolFromString(Handle<String>(String::cast(entry->name())));
|
||||
// Check if a descriptor with this name already exists before writing.
|
||||
if (LinearSearch(*result, EXPECT_UNSORTED, *key, descriptor_count) ==
|
||||
if (LinearSearch(*result, EXPECT_UNSORTED, *key, added_descriptor_count) ==
|
||||
DescriptorArray::kNotFound) {
|
||||
CallbacksDescriptor desc(*key, *entry, entry->property_attributes());
|
||||
result->Set(descriptor_count, &desc, witness);
|
||||
descriptor_count++;
|
||||
CallbacksDescriptor desc(*key,
|
||||
*entry,
|
||||
entry->property_attributes(),
|
||||
next_enum++);
|
||||
result->Set(added_descriptor_count, &desc, witness);
|
||||
added_descriptor_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the old descriptor array if there were no new elements.
|
||||
if (added_descriptor_count == descriptor_count) return array;
|
||||
|
||||
// If duplicates were detected, allocate a result of the right size
|
||||
// and transfer the elements.
|
||||
if (descriptor_count < result->length()) {
|
||||
Handle<DescriptorArray> new_result = NewDescriptorArray(descriptor_count);
|
||||
for (int i = 0; i < descriptor_count; i++) {
|
||||
if (added_descriptor_count < result->length()) {
|
||||
Handle<DescriptorArray> new_result =
|
||||
NewDescriptorArray(added_descriptor_count);
|
||||
for (int i = 0; i < added_descriptor_count; i++) {
|
||||
DescriptorArray::CopyFrom(new_result, i, result, i, witness);
|
||||
}
|
||||
result = new_result;
|
||||
@ -963,6 +972,7 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
|
||||
|
||||
// Sort the result before returning.
|
||||
result->Sort(witness);
|
||||
ASSERT(result->NextEnumerationIndex() == next_enum);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -3883,11 +3883,9 @@ MaybeObject* Heap::AllocateInitialMap(JSFunction* fun) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
String* name = fun->shared()->GetThisPropertyAssignmentName(i);
|
||||
ASSERT(name->IsSymbol());
|
||||
FieldDescriptor field(name, i, NONE);
|
||||
field.SetEnumerationIndex(i);
|
||||
FieldDescriptor field(name, i, NONE, i + 1);
|
||||
descriptors->Set(i, &field, witness);
|
||||
}
|
||||
descriptors->SetNextEnumerationIndex(count);
|
||||
descriptors->SortUnchecked(witness);
|
||||
|
||||
// The descriptors may contain duplicates because the compiler does not
|
||||
|
@ -1092,7 +1092,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
||||
// We got a map in register eax. Get the enumeration cache from it.
|
||||
__ bind(&use_cache);
|
||||
__ LoadInstanceDescriptors(eax, ecx);
|
||||
__ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset));
|
||||
__ mov(ecx, FieldOperand(ecx, DescriptorArray::kLastAddedOffset));
|
||||
__ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset));
|
||||
|
||||
// Set up the four remaining stack slots.
|
||||
|
@ -5293,7 +5293,7 @@ void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
__ LoadInstanceDescriptors(map, result);
|
||||
__ mov(result,
|
||||
FieldOperand(result, DescriptorArray::kEnumerationIndexOffset));
|
||||
FieldOperand(result, DescriptorArray::kLastAddedOffset));
|
||||
__ mov(result,
|
||||
FieldOperand(result, FixedArray::SizeFor(instr->idx())));
|
||||
__ test(result, result);
|
||||
|
@ -2901,7 +2901,7 @@ void MacroAssembler::CheckEnumCache(Label* call_runtime) {
|
||||
// Check that there is an enum cache in the non-empty instance
|
||||
// descriptors (edx). This is the case if the next enumeration
|
||||
// index field does not contain a smi.
|
||||
mov(edx, FieldOperand(edx, DescriptorArray::kEnumerationIndexOffset));
|
||||
mov(edx, FieldOperand(edx, DescriptorArray::kLastAddedOffset));
|
||||
JumpIfSmi(edx, call_runtime);
|
||||
|
||||
// For all objects but the receiver, check that the cache is empty.
|
||||
|
@ -1518,8 +1518,7 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
|
||||
|
||||
Handle<Map> transition(Map::cast(value));
|
||||
DescriptorArray* target_descriptors = transition->instance_descriptors();
|
||||
int descriptor = target_descriptors->SearchWithCache(*name);
|
||||
ASSERT(descriptor != DescriptorArray::kNotFound);
|
||||
int descriptor = target_descriptors->LastAdded();
|
||||
PropertyDetails details = target_descriptors->GetDetails(descriptor);
|
||||
|
||||
if (details.type() != FIELD || details.attributes() != NONE) return;
|
||||
@ -1990,8 +1989,7 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
|
||||
|
||||
Handle<Map> transition(Map::cast(value));
|
||||
DescriptorArray* target_descriptors = transition->instance_descriptors();
|
||||
int descriptor = target_descriptors->SearchWithCache(*name);
|
||||
ASSERT(descriptor != DescriptorArray::kNotFound);
|
||||
int descriptor = target_descriptors->LastAdded();
|
||||
PropertyDetails details = target_descriptors->GetDetails(descriptor);
|
||||
|
||||
if (details.type() == FIELD && details.attributes() == NONE) {
|
||||
|
@ -2095,6 +2095,7 @@ void DescriptorArray::Set(int descriptor_number,
|
||||
const WhitenessWitness&) {
|
||||
// Range check.
|
||||
ASSERT(descriptor_number < number_of_descriptors());
|
||||
ASSERT(desc->GetDetails().index() > 0);
|
||||
|
||||
NoIncrementalWriteBarrierSet(this,
|
||||
ToKeyIndex(descriptor_number),
|
||||
|
@ -1546,7 +1546,7 @@ MaybeObject* JSObject::AddFastProperty(String* name,
|
||||
int index = map()->NextFreePropertyIndex();
|
||||
|
||||
// Allocate new instance descriptors with (name, index) added
|
||||
FieldDescriptor new_field(name, index, attributes);
|
||||
FieldDescriptor new_field(name, index, attributes, 0);
|
||||
DescriptorArray* new_descriptors;
|
||||
{ MaybeObject* maybe_new_descriptors =
|
||||
old_descriptors->CopyInsert(&new_field);
|
||||
@ -1555,18 +1555,11 @@ MaybeObject* JSObject::AddFastProperty(String* name,
|
||||
}
|
||||
}
|
||||
|
||||
// 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->SearchWithCache(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;
|
||||
// Only allow map transition if the object isn't the global object.
|
||||
bool allow_map_transition =
|
||||
can_insert_transition &&
|
||||
(isolate->context()->global_context()->object_function()->map() != map());
|
||||
|
||||
ASSERT(old_descriptors->Search(name) == DescriptorArray::kNotFound);
|
||||
ASSERT(index < map()->inobject_properties() ||
|
||||
(index - map()->inobject_properties()) < properties()->length() ||
|
||||
map()->unused_property_fields() == 0);
|
||||
@ -1622,7 +1615,7 @@ MaybeObject* JSObject::AddConstantFunctionProperty(
|
||||
JSFunction* function,
|
||||
PropertyAttributes attributes) {
|
||||
// Allocate new instance descriptors with (name, function) added
|
||||
ConstantFunctionDescriptor d(name, function, attributes);
|
||||
ConstantFunctionDescriptor d(name, function, attributes, 0);
|
||||
DescriptorArray* new_descriptors;
|
||||
{ MaybeObject* maybe_new_descriptors =
|
||||
map()->instance_descriptors()->CopyInsert(&d);
|
||||
@ -1865,7 +1858,7 @@ MaybeObject* JSObject::ConvertDescriptorToField(String* name,
|
||||
}
|
||||
|
||||
int index = map()->NextFreePropertyIndex();
|
||||
FieldDescriptor new_field(name, index, attributes);
|
||||
FieldDescriptor new_field(name, index, attributes, 0);
|
||||
// Make a new DescriptorArray replacing an entry with FieldDescriptor.
|
||||
Object* descriptors_unchecked;
|
||||
{ MaybeObject* maybe_descriptors_unchecked =
|
||||
@ -2938,7 +2931,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
||||
|
||||
Map* transition_map = Map::cast(transition);
|
||||
DescriptorArray* descriptors = transition_map->instance_descriptors();
|
||||
int descriptor = descriptors->SearchWithCache(*name);
|
||||
int descriptor = descriptors->LastAdded();
|
||||
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||
ASSERT(details.type() == FIELD || details.type() == CONSTANT_FUNCTION);
|
||||
|
||||
@ -3062,7 +3055,7 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
||||
|
||||
Map* transition_map = Map::cast(transition);
|
||||
DescriptorArray* descriptors = transition_map->instance_descriptors();
|
||||
int descriptor = descriptors->Search(name);
|
||||
int descriptor = descriptors->LastAdded();
|
||||
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||
ASSERT(details.type() == FIELD || details.type() == CONSTANT_FUNCTION);
|
||||
|
||||
@ -4610,7 +4603,7 @@ static MaybeObject* CreateFreshAccessor(JSObject* obj,
|
||||
|
||||
// step 2: create a copy of the descriptors, incl. the new getter/setter pair
|
||||
Map* map1 = obj->map();
|
||||
CallbacksDescriptor callbacks_descr2(name, accessors2, attributes);
|
||||
CallbacksDescriptor callbacks_descr2(name, accessors2, attributes, 0);
|
||||
DescriptorArray* descriptors2;
|
||||
{ MaybeObject* maybe_descriptors2 =
|
||||
map1->instance_descriptors()->CopyInsert(&callbacks_descr2);
|
||||
@ -4657,7 +4650,7 @@ static bool TransitionToSameAccessor(Object* map,
|
||||
Object* accessor,
|
||||
PropertyAttributes attributes ) {
|
||||
DescriptorArray* descs = Map::cast(map)->instance_descriptors();
|
||||
int number = descs->SearchWithCache(name);
|
||||
int number = descs->LastAdded();
|
||||
ASSERT(number != DescriptorArray::kNotFound);
|
||||
Object* target_accessor =
|
||||
AccessorPair::cast(descs->GetCallbacksObject(number))->get(component);
|
||||
@ -4681,7 +4674,7 @@ static MaybeObject* NewCallbackTransition(JSObject* obj,
|
||||
|
||||
// step 2: create a copy of the descriptors, incl. the new getter/setter pair
|
||||
Map* map2 = obj->map();
|
||||
CallbacksDescriptor callbacks_descr3(name, accessors3, attributes);
|
||||
CallbacksDescriptor callbacks_descr3(name, accessors3, attributes, 0);
|
||||
DescriptorArray* descriptors3;
|
||||
{ MaybeObject* maybe_descriptors3 =
|
||||
map2->instance_descriptors()->CopyInsert(&callbacks_descr3);
|
||||
@ -5817,8 +5810,7 @@ MaybeObject* DescriptorArray::Allocate(int number_of_descriptors,
|
||||
if (!maybe_array->To(&result)) return maybe_array;
|
||||
}
|
||||
|
||||
result->set(kEnumerationIndexIndex,
|
||||
Smi::FromInt(PropertyDetails::kInitialIndex));
|
||||
result->set(kLastAddedIndex, Smi::FromInt(-1));
|
||||
result->set(kTransitionsIndex, Smi::FromInt(0));
|
||||
return result;
|
||||
}
|
||||
@ -5830,9 +5822,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(kLastAddedIndex))->
|
||||
set(kEnumCacheBridgeCacheIndex, new_cache);
|
||||
FixedArray::cast(get(kEnumerationIndexIndex))->
|
||||
FixedArray::cast(get(kLastAddedIndex))->
|
||||
set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
|
||||
} else {
|
||||
if (IsEmpty()) return; // Do nothing for empty descriptor array.
|
||||
@ -5841,9 +5833,9 @@ void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
|
||||
FixedArray::cast(bridge_storage)->
|
||||
set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
|
||||
NoWriteBarrierSet(FixedArray::cast(bridge_storage),
|
||||
kEnumCacheBridgeEnumIndex,
|
||||
get(kEnumerationIndexIndex));
|
||||
set(kEnumerationIndexIndex, bridge_storage);
|
||||
kEnumCacheBridgeLastAdded,
|
||||
get(kLastAddedIndex));
|
||||
set(kLastAddedIndex, bridge_storage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5910,14 +5902,11 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor) {
|
||||
|
||||
// Set the enumeration index in the descriptors and set the enumeration index
|
||||
// in the result.
|
||||
int enumeration_index = NextEnumerationIndex();
|
||||
if (keep_enumeration_index) {
|
||||
descriptor->SetEnumerationIndex(GetDetails(index).index());
|
||||
} else {
|
||||
descriptor->SetEnumerationIndex(enumeration_index);
|
||||
++enumeration_index;
|
||||
descriptor->SetEnumerationIndex(NextEnumerationIndex());
|
||||
}
|
||||
new_descriptors->SetNextEnumerationIndex(enumeration_index);
|
||||
|
||||
// Copy the descriptors, inserting or replacing a descriptor.
|
||||
int to_index = 0;
|
||||
@ -5939,6 +5928,11 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor) {
|
||||
|
||||
ASSERT(insertion_index < new_descriptors->number_of_descriptors());
|
||||
new_descriptors->Set(insertion_index, descriptor, witness);
|
||||
if (!replacing) {
|
||||
new_descriptors->SetLastAdded(insertion_index);
|
||||
} else {
|
||||
new_descriptors->SetLastAdded(LastAdded());
|
||||
}
|
||||
|
||||
ASSERT(to_index == new_descriptors->number_of_descriptors());
|
||||
SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
|
||||
@ -5964,8 +5958,9 @@ MaybeObject* DescriptorArray::Copy(SharedMode shared_mode) {
|
||||
new_descriptors->CopyFrom(i, this, i, witness);
|
||||
if (copy_result->IsFailure()) return copy_result;
|
||||
}
|
||||
new_descriptors->SetLastAdded(LastAdded());
|
||||
}
|
||||
new_descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
|
||||
|
||||
return new_descriptors;
|
||||
}
|
||||
|
||||
@ -5976,6 +5971,8 @@ MaybeObject* DescriptorArray::Copy(SharedMode shared_mode) {
|
||||
void DescriptorArray::SortUnchecked(const WhitenessWitness& witness) {
|
||||
// In-place heap sort.
|
||||
int len = number_of_descriptors();
|
||||
// Nothing to sort.
|
||||
if (len == 0) return;
|
||||
|
||||
// Bottom-up max-heap construction.
|
||||
// Index of the last node with children
|
||||
@ -6023,6 +6020,19 @@ void DescriptorArray::SortUnchecked(const WhitenessWitness& witness) {
|
||||
parent_index = child_index;
|
||||
}
|
||||
}
|
||||
|
||||
int last_enum_index = -1;
|
||||
int last_added = -1;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
int current_enum = GetDetails(i).index();
|
||||
if (current_enum > last_enum_index) {
|
||||
last_added = i;
|
||||
last_enum_index = current_enum;
|
||||
}
|
||||
}
|
||||
SetLastAdded(last_added);
|
||||
|
||||
ASSERT(LastAdded() != -1);
|
||||
}
|
||||
|
||||
|
||||
@ -12720,7 +12730,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
|
||||
JSFunction::cast(value),
|
||||
details.attributes(),
|
||||
details.index());
|
||||
descriptors->Set(next_descriptor++, &d, witness);
|
||||
descriptors->Set(next_descriptor, &d, witness);
|
||||
} else if (type == NORMAL) {
|
||||
if (current_offset < inobject_props) {
|
||||
obj->InObjectPropertyAtPut(current_offset,
|
||||
@ -12734,7 +12744,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
|
||||
current_offset++,
|
||||
details.attributes(),
|
||||
details.index());
|
||||
descriptors->Set(next_descriptor++, &d, witness);
|
||||
descriptors->Set(next_descriptor, &d, witness);
|
||||
} else if (type == CALLBACKS) {
|
||||
if (value->IsAccessorPair()) {
|
||||
MaybeObject* maybe_copy =
|
||||
@ -12745,10 +12755,11 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
|
||||
value,
|
||||
details.attributes(),
|
||||
details.index());
|
||||
descriptors->Set(next_descriptor++, &d, witness);
|
||||
descriptors->Set(next_descriptor, &d, witness);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
++next_descriptor;
|
||||
}
|
||||
}
|
||||
ASSERT(current_offset == number_of_fields);
|
||||
@ -12768,7 +12779,6 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
|
||||
obj->set_properties(FixedArray::cast(fields));
|
||||
ASSERT(obj->IsJSObject());
|
||||
|
||||
descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
|
||||
// Check that it really works.
|
||||
ASSERT(obj->HasFastProperties());
|
||||
|
||||
|
@ -2476,37 +2476,44 @@ class DescriptorArray: public FixedArray {
|
||||
|
||||
inline int number_of_entries() { return number_of_descriptors(); }
|
||||
|
||||
int NextEnumerationIndex() {
|
||||
if (IsEmpty()) return PropertyDetails::kInitialIndex;
|
||||
Object* obj = get(kEnumerationIndexIndex);
|
||||
int LastAdded() {
|
||||
ASSERT(!IsEmpty());
|
||||
Object* obj = get(kLastAddedIndex);
|
||||
if (obj->IsSmi()) {
|
||||
return Smi::cast(obj)->value();
|
||||
} else {
|
||||
Object* index = FixedArray::cast(obj)->get(kEnumCacheBridgeEnumIndex);
|
||||
Object* index = FixedArray::cast(obj)->get(kEnumCacheBridgeLastAdded);
|
||||
return Smi::cast(index)->value();
|
||||
}
|
||||
}
|
||||
|
||||
// Set next enumeration index and flush any enum cache.
|
||||
void SetNextEnumerationIndex(int value) {
|
||||
if (!IsEmpty()) {
|
||||
set(kEnumerationIndexIndex, Smi::FromInt(value));
|
||||
int NextEnumerationIndex() {
|
||||
if (number_of_descriptors() == 0) {
|
||||
return PropertyDetails::kInitialIndex;
|
||||
}
|
||||
return GetDetails(LastAdded()).index() + 1;
|
||||
}
|
||||
|
||||
// Set index of the last added descriptor and flush any enum cache.
|
||||
void SetLastAdded(int index) {
|
||||
ASSERT(!IsEmpty() || index > 0);
|
||||
set(kLastAddedIndex, Smi::FromInt(index));
|
||||
}
|
||||
|
||||
bool HasEnumCache() {
|
||||
return !IsEmpty() && !get(kEnumerationIndexIndex)->IsSmi();
|
||||
return !IsEmpty() && !get(kLastAddedIndex)->IsSmi();
|
||||
}
|
||||
|
||||
Object* GetEnumCache() {
|
||||
ASSERT(HasEnumCache());
|
||||
FixedArray* bridge = FixedArray::cast(get(kEnumerationIndexIndex));
|
||||
FixedArray* bridge = FixedArray::cast(get(kLastAddedIndex));
|
||||
return bridge->get(kEnumCacheBridgeCacheIndex);
|
||||
}
|
||||
|
||||
Object** GetEnumCacheSlot() {
|
||||
ASSERT(HasEnumCache());
|
||||
return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
|
||||
kEnumerationIndexOffset);
|
||||
kLastAddedOffset);
|
||||
}
|
||||
|
||||
Object** GetTransitionsSlot() {
|
||||
@ -2604,27 +2611,27 @@ class DescriptorArray: public FixedArray {
|
||||
static const int kNotFound = -1;
|
||||
|
||||
static const int kBackPointerStorageIndex = 0;
|
||||
static const int kEnumerationIndexIndex = 1;
|
||||
static const int kLastAddedIndex = 1;
|
||||
static const int kTransitionsIndex = 2;
|
||||
static const int kFirstIndex = 3;
|
||||
|
||||
// The length of the "bridge" to the enum cache.
|
||||
static const int kEnumCacheBridgeLength = 3;
|
||||
static const int kEnumCacheBridgeEnumIndex = 0;
|
||||
static const int kEnumCacheBridgeLastAdded = 0;
|
||||
static const int kEnumCacheBridgeCacheIndex = 1;
|
||||
static const int kEnumCacheBridgeIndicesCacheIndex = 2;
|
||||
|
||||
// Layout description.
|
||||
static const int kBackPointerStorageOffset = FixedArray::kHeaderSize;
|
||||
static const int kEnumerationIndexOffset = kBackPointerStorageOffset +
|
||||
kPointerSize;
|
||||
static const int kTransitionsOffset = kEnumerationIndexOffset + kPointerSize;
|
||||
static const int kLastAddedOffset = kBackPointerStorageOffset +
|
||||
kPointerSize;
|
||||
static const int kTransitionsOffset = kLastAddedOffset + kPointerSize;
|
||||
static const int kFirstOffset = kTransitionsOffset + kPointerSize;
|
||||
|
||||
// Layout description for the bridge array.
|
||||
static const int kEnumCacheBridgeEnumOffset = FixedArray::kHeaderSize;
|
||||
static const int kEnumCacheBridgeLastAddedOffset = FixedArray::kHeaderSize;
|
||||
static const int kEnumCacheBridgeCacheOffset =
|
||||
kEnumCacheBridgeEnumOffset + kPointerSize;
|
||||
kEnumCacheBridgeLastAddedOffset + kPointerSize;
|
||||
|
||||
// Layout of descriptor.
|
||||
static const int kDescriptorKey = 0;
|
||||
@ -3090,6 +3097,7 @@ class Dictionary: public HashTable<Shape, Key> {
|
||||
|
||||
// Accessors for next enumeration index.
|
||||
void SetNextEnumerationIndex(int index) {
|
||||
ASSERT(index != 0);
|
||||
this->set(kNextEnumerationIndexIndex, Smi::FromInt(index));
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ class Descriptor BASE_EMBEDDED {
|
||||
Object* value,
|
||||
PropertyAttributes attributes,
|
||||
PropertyType type,
|
||||
int index = 0)
|
||||
int index)
|
||||
: key_(key),
|
||||
value_(value),
|
||||
details_(attributes, type, index) { }
|
||||
@ -106,7 +106,7 @@ class FieldDescriptor: public Descriptor {
|
||||
FieldDescriptor(String* key,
|
||||
int field_index,
|
||||
PropertyAttributes attributes,
|
||||
int index = 0)
|
||||
int index)
|
||||
: Descriptor(key, Smi::FromInt(field_index), attributes, FIELD, index) {}
|
||||
};
|
||||
|
||||
@ -116,7 +116,7 @@ class ConstantFunctionDescriptor: public Descriptor {
|
||||
ConstantFunctionDescriptor(String* key,
|
||||
JSFunction* function,
|
||||
PropertyAttributes attributes,
|
||||
int index = 0)
|
||||
int index)
|
||||
: Descriptor(key, function, attributes, CONSTANT_FUNCTION, index) {}
|
||||
};
|
||||
|
||||
@ -126,7 +126,7 @@ class CallbacksDescriptor: public Descriptor {
|
||||
CallbacksDescriptor(String* key,
|
||||
Object* foreign,
|
||||
PropertyAttributes attributes,
|
||||
int index = 0)
|
||||
int index)
|
||||
: Descriptor(key, foreign, attributes, CALLBACKS, index) {}
|
||||
};
|
||||
|
||||
|
@ -177,8 +177,7 @@ Map* TransitionArray::GetTargetMap(int transition_number) {
|
||||
PropertyDetails TransitionArray::GetTargetDetails(int transition_number) {
|
||||
Map* map = GetTargetMap(transition_number);
|
||||
DescriptorArray* descriptors = map->instance_descriptors();
|
||||
String* key = GetKey(transition_number);
|
||||
int descriptor = descriptors->SearchWithCache(key);
|
||||
int descriptor = descriptors->LastAdded();
|
||||
ASSERT(descriptor != DescriptorArray::kNotFound);
|
||||
return descriptors->GetDetails(descriptor);
|
||||
}
|
||||
|
@ -1109,7 +1109,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
||||
// We got a map in register rax. Get the enumeration cache from it.
|
||||
__ bind(&use_cache);
|
||||
__ LoadInstanceDescriptors(rax, rcx);
|
||||
__ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset));
|
||||
__ movq(rcx, FieldOperand(rcx, DescriptorArray::kLastAddedOffset));
|
||||
__ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset));
|
||||
|
||||
// Set up the four remaining stack slots.
|
||||
|
@ -5005,7 +5005,7 @@ void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
__ LoadInstanceDescriptors(map, result);
|
||||
__ movq(result,
|
||||
FieldOperand(result, DescriptorArray::kEnumerationIndexOffset));
|
||||
FieldOperand(result, DescriptorArray::kLastAddedOffset));
|
||||
__ movq(result,
|
||||
FieldOperand(result, FixedArray::SizeFor(instr->idx())));
|
||||
Condition cc = masm()->CheckSmi(result);
|
||||
|
@ -4473,7 +4473,7 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
|
||||
// Check that there is an enum cache in the non-empty instance
|
||||
// descriptors (rdx). This is the case if the next enumeration
|
||||
// index field does not contain a smi.
|
||||
movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset));
|
||||
movq(rdx, FieldOperand(rdx, DescriptorArray::kLastAddedOffset));
|
||||
JumpIfSmi(rdx, call_runtime);
|
||||
|
||||
// For all objects but the receiver, check that the cache is empty.
|
||||
|
Loading…
Reference in New Issue
Block a user