Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(254)

Unified Diff: src/objects.cc

Issue 35413006: Correct handling of arrays with callbacks in the prototype chain. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Test fixes Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« src/objects.h ('K') | « src/objects.h ('k') | src/objects-inl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/objects.cc
diff --git a/src/objects.cc b/src/objects.cc
index 414758f0e8350ec9c8be5c2cbd21453dca22a398..6527cae2cc2b76c8d6127fa12c43426c3682cf5e 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -2948,6 +2948,30 @@ Handle<Object> JSReceiver::SetPropertyWithDefinedSetter(
}
+bool Map::MayHaveIndexedCallbacksInPrototypeChain() {
+ Heap* heap = GetHeap();
+
+ if (has_element_callbacks()) {
+ return true;
+ }
+
+ for (Object* pt = prototype();
danno 2013/10/30 12:17:45 don't use the abbreviation, use the variable name
mvstanton 2013/10/30 18:22:28 Done.
+ pt != heap->null_value();
+ pt = pt->GetPrototype(GetIsolate())) {
+ if (pt->IsJSProxy()) {
+ // Be conservative, don't walk into proxies.
+ return true;
+ }
+
+ if (JSObject::cast(pt)->map()->has_element_callbacks()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes(
uint32_t index,
Object* value,
@@ -4478,9 +4502,10 @@ Handle<Map> NormalizedMapCache::Get(Handle<NormalizedMapCache> cache,
if (result->IsMap() &&
Handle<Map>::cast(result)->EquivalentToForNormalization(obj->map(),
mode)) {
+ Handle<Map> result_map = Handle<Map>::cast(result);
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) {
- Handle<Map>::cast(result)->SharedMapVerify();
+ result_map->SharedMapVerify();
}
#endif
#ifdef ENABLE_SLOW_ASSERTS
@@ -4492,17 +4517,36 @@ Handle<Map> NormalizedMapCache::Get(Handle<NormalizedMapCache> cache,
SHARED_NORMALIZED_MAP);
ASSERT(memcmp(fresh->address(),
- Handle<Map>::cast(result)->address(),
- Map::kCodeCacheOffset) == 0);
+ result_map->address(),
+ Map::kTransitionsOrBackPointerOffset) == 0);
STATIC_ASSERT(Map::kDependentCodeOffset ==
- Map::kCodeCacheOffset + kPointerSize);
- int offset = Map::kDependentCodeOffset + kPointerSize;
- ASSERT(memcmp(fresh->address() + offset,
- Handle<Map>::cast(result)->address() + offset,
- Map::kSize - offset) == 0);
+ Map::kTransitionsOrBackPointerOffset + 3 * kPointerSize);
+
+ // The final fields to look at are flag fields 3 and 4. Field 3
danno 2013/10/30 12:17:45 Is the logic needed? I thought the conclusion was
+ // needs special attention.
+ STATIC_ASSERT(Map::kDependentCodeOffset + kPointerSize ==
+ Map::kBitField3Offset);
+ STATIC_ASSERT(Map::kSize - (2 * kPointerSize) == Map::kBitField3Offset);
+ STATIC_ASSERT(Map::kSize - kPointerSize == Map::kBitField4Offset);
+
+ int mask = Map::EnumLengthBits::kMask |
+ Map::NumberOfOwnDescriptorsBits::kMask |
+ Map::IsShared::kMask |
+ Map::FunctionWithPrototype::kMask |
+ Map::DictionaryMap::kMask |
+ // OwnsDescriptors::kMask |
+ Map::IsObserved::kMask |
+ Map::Deprecated::kMask |
+ Map::IsFrozen::kMask |
+ Map::IsUnstable::kMask |
+ Map::IsMigrationTarget::kMask;
+ int fresh_field3 = fresh->bit_field3() & mask;
+ int result_field3 = result_map->bit_field3() & mask;
+ ASSERT(fresh_field3 == result_field3);
+ ASSERT(fresh->bit_field4() == result_map->bit_field4());
}
#endif
- return Handle<Map>::cast(result);
+ return result_map;
}
Isolate* isolate = cache->GetIsolate();
@@ -4703,39 +4747,15 @@ Handle<SeededNumberDictionary> JSObject::NormalizeElements(
MaybeObject* JSObject::NormalizeElements() {
- ASSERT(!HasExternalArrayElements());
-
- // Find the backing store.
- FixedArrayBase* array = FixedArrayBase::cast(elements());
- Map* old_map = array->map();
- bool is_arguments =
- (old_map == old_map->GetHeap()->non_strict_arguments_elements_map());
- if (is_arguments) {
- array = FixedArrayBase::cast(FixedArray::cast(array)->get(1));
- }
- if (array->IsDictionary()) return array;
-
- ASSERT(HasFastSmiOrObjectElements() ||
- HasFastDoubleElements() ||
- HasFastArgumentsElements());
- // Compute the effective length and allocate a new backing store.
- int length = IsJSArray()
- ? Smi::cast(JSArray::cast(this)->length())->value()
- : array->length();
- int old_capacity = 0;
- int used_elements = 0;
- GetElementsCapacityAndUsage(&old_capacity, &used_elements);
SeededNumberDictionary* dictionary;
- MaybeObject* maybe_dictionary =
- SeededNumberDictionary::Allocate(GetHeap(), used_elements);
- if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+ FixedArrayBase* array = GetElements();
danno 2013/10/30 12:17:45 I'd call this GetElementBackingStore(), and you mi
+ if (array->IsDictionary()) return array;
- maybe_dictionary = CopyFastElementsToDictionary(
- GetIsolate(), array, length, dictionary);
+ MaybeObject* maybe_dictionary = CreateNormalizedElements();
if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
// Switch to using the dictionary as the backing storage for elements.
- if (is_arguments) {
+ if (HasNonStrictArgumentsElementsMap()) {
FixedArray::cast(elements())->set(1, dictionary);
} else {
// Set the new map first to satify the elements type assert in
@@ -4748,8 +4768,7 @@ MaybeObject* JSObject::NormalizeElements() {
set_elements(dictionary);
}
- old_map->GetHeap()->isolate()->counters()->elements_to_dictionary()->
- Increment();
+ GetHeap()->isolate()->counters()->elements_to_dictionary()->Increment();
#ifdef DEBUG
if (FLAG_trace_normalization) {
@@ -4763,6 +4782,44 @@ MaybeObject* JSObject::NormalizeElements() {
}
+Handle<SeededNumberDictionary> JSObject::CreateNormalizedElements(
+ Handle<JSObject> object) {
+ CALL_HEAP_FUNCTION(object->GetIsolate(),
+ object->CreateNormalizedElements(),
+ SeededNumberDictionary);
+}
+
+
+MaybeObject* JSObject::CreateNormalizedElements() {
+ ASSERT(!HasExternalArrayElements());
+
+ // Find the backing store.
+ FixedArrayBase* array = GetElements();
+ ASSERT(!array->IsDictionary());
+
+ ASSERT(HasFastSmiOrObjectElements() ||
+ HasFastDoubleElements() ||
+ HasFastArgumentsElements());
+ // Compute the effective length and allocate a new backing store.
+ int length = IsJSArray()
+ ? Smi::cast(JSArray::cast(this)->length())->value()
+ : array->length();
+ int old_capacity = 0;
+ int used_elements = 0;
+ GetElementsCapacityAndUsage(&old_capacity, &used_elements);
+ SeededNumberDictionary* dictionary;
+ MaybeObject* maybe_dictionary =
+ SeededNumberDictionary::Allocate(GetHeap(), used_elements);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+
+ maybe_dictionary = CopyFastElementsToDictionary(
+ GetIsolate(), array, length, dictionary);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+
+ return dictionary;
+}
+
+
Smi* JSReceiver::GenerateIdentityHash() {
Isolate* isolate = GetIsolate();
@@ -5565,7 +5622,7 @@ Handle<Object> JSObject::Freeze(Handle<JSObject> object) {
Map* transition_map = result.GetTransitionTarget();
ASSERT(transition_map->has_dictionary_elements());
ASSERT(transition_map->is_frozen());
- ASSERT(!transition_map->is_extensible());
+ ASSERT(!transition_map->is_extensible());
danno 2013/10/30 12:17:45 nit: remove stray whitespace change.
mvstanton 2013/10/30 18:22:28 Done.
object->set_map(transition_map);
} else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
// Create a new descriptor array with fully-frozen properties
@@ -6226,11 +6283,56 @@ void JSObject::SetElementCallback(Handle<JSObject> object,
uint32_t index,
Handle<Object> structure,
PropertyAttributes attributes) {
+ Isolate* isolate = object->GetIsolate();
Heap* heap = object->GetHeap();
PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0);
+ bool is_arguments = object->HasNonStrictArgumentsElements();
+
+ // Do we need to create a new dictionary?
+ Handle<SeededNumberDictionary> new_element_dictionary;
danno 2013/10/30 12:17:45 I think NormalizeElements will do the right thing
mvstanton 2013/10/30 18:22:28 Yep, I thought combining the move to dictionary an
+ FixedArrayBase* array = object->GetElements();
+ if (!array->IsDictionary()) {
+ new_element_dictionary = CreateNormalizedElements(object);
+ }
+
+ if (!object->map()->has_element_callbacks()) {
+ LookupResult result(isolate);
+ Handle<Map> old_map(object->map());
+ old_map->LookupTransition(*object,
danno 2013/10/30 12:17:45 It looks like JSObject::SetObserve does almost exa
mvstanton 2013/10/30 18:22:28 addressed.
+ heap->element_callbacks_symbol(),
+ &result);
+ if (result.IsTransition()) {
+ object->set_map(result.GetTransitionTarget());
+ } else {
+ Handle<Map> new_map = Map::CopyAsElementCallbacksTransition(old_map,
+ !is_arguments);
+ object->set_map(*new_map);
+ }
+
+ ASSERT(object->map()->has_element_callbacks());
+
+ // Install the dictionary if required without doing an extra transition.
+ if (!new_element_dictionary.is_null()) {
+ if (is_arguments) {
+ FixedArray::cast(object->elements())->set(1, *new_element_dictionary);
+ } else {
+ object->set_elements(*new_element_dictionary);
+ }
+ }
+
+ // KeyedStoreICs (at least the non-generic ones) need a reset.
+ // I *could* look at the maps and only reset the poly/mono ones that
danno 2013/10/30 12:17:45 nit: we try to avoid using "I" or "we" in comments
mvstanton 2013/10/30 18:22:28 :) done.
+ // depend on a map in this prototype chain?
+ heap->ClearAllICsByKind(Code::KEYED_STORE_IC);
+ } else {
+ // If our map is already flagged for element callbacks, then we should
+ // not have had to create a new element dictionary.
+ ASSERT(new_element_dictionary.is_null());
+ }
+
+ Handle<SeededNumberDictionary> dictionary = Handle<SeededNumberDictionary>(
+ SeededNumberDictionary::cast(object->GetElements()), isolate);
- // Normalize elements to make this operation simple.
- Handle<SeededNumberDictionary> dictionary = NormalizeElements(object);
ASSERT(object->HasDictionaryElements() ||
object->HasDictionaryArgumentsElements());
@@ -6240,7 +6342,7 @@ void JSObject::SetElementCallback(Handle<JSObject> object,
dictionary->set_requires_slow_elements();
// Update the dictionary backing store on the object.
- if (object->elements()->map() == heap->non_strict_arguments_elements_map()) {
+ if (is_arguments) {
// Also delete any parameter alias.
//
// TODO(kmillikin): when deleting the last parameter alias we could
@@ -6672,6 +6774,7 @@ MaybeObject* Map::RawCopy(int instance_size) {
new_bit_field3 = Deprecated::update(new_bit_field3, false);
new_bit_field3 = IsUnstable::update(new_bit_field3, false);
result->set_bit_field3(new_bit_field3);
+ result->set_bit_field4(bit_field4());
return result;
}
@@ -6923,6 +7026,60 @@ MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) {
}
+MaybeObject* Map::CopyAsElementCallbacksTransition(
danno 2013/10/30 12:17:45 I think with a little work, you can 100% share thi
mvstanton 2013/10/30 18:22:28 I this this in a basic way, with a protected funct
+ bool with_dictionary_elements) {
+ ASSERT(!HasElementCallbacksTransition());
+
+ if (owns_descriptors()) {
+ // In case the map owned its own descriptors, share the descriptors and
+ // transfer ownership to the new map.
+ Map* new_map;
+ MaybeObject* maybe_new_map = CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ MaybeObject* added_elements = set_element_callbacks_map(new_map);
+ if (added_elements->IsFailure()) return added_elements;
+
+ new_map->InitializeDescriptors(instance_descriptors());
+ new_map->set_has_element_callbacks(true);
+ if (with_dictionary_elements) {
+ new_map->set_elements_kind(DICTIONARY_ELEMENTS);
+ }
+ new_map->SetBackPointer(this);
+ set_owns_descriptors(false);
+ return new_map;
+ }
+
+ // In case the map did not own its own descriptors, a split is forced by
+ // copying the map; creating a new descriptor array cell.
+ // Create a new free-floating map only if we are not allowed to store it.
+ Map* new_map;
+ MaybeObject* maybe_new_map = Copy();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ new_map->set_has_element_callbacks(true);
+ if (with_dictionary_elements) {
+ new_map->set_elements_kind(DICTIONARY_ELEMENTS);
+ }
+
+ MaybeObject* added_elements = set_element_callbacks_map(new_map);
+ if (added_elements->IsFailure()) return added_elements;
+ new_map->SetBackPointer(this);
+
+ return new_map;
+}
+
+
+Handle<Map> Map::CopyAsElementCallbacksTransition(
+ Handle<Map> map,
+ bool with_dictionary_elements) {
+ Isolate* isolate = map->GetIsolate();
+ CALL_HEAP_FUNCTION(isolate,
+ map->CopyAsElementCallbacksTransition(
+ with_dictionary_elements),
+ Map);
+}
+
+
Handle<Map> Map::CopyForObserved(Handle<Map> map) {
ASSERT(!map->is_observed());
@@ -9431,6 +9588,7 @@ static bool CheckEquivalent(Map* first, Map* second) {
first->instance_type() == second->instance_type() &&
first->bit_field() == second->bit_field() &&
first->bit_field2() == second->bit_field2() &&
+ first->bit_field4() == second->bit_field4() &&
first->is_observed() == second->is_observed() &&
first->function_with_prototype() == second->function_with_prototype();
}
@@ -10565,6 +10723,16 @@ void Code::ReplaceNthCell(int n, Cell* replace_with) {
void Code::ClearInlineCaches() {
+ ClearInlineCaches(NULL);
+}
+
+
+void Code::ClearInlineCaches(Code::Kind kind) {
+ ClearInlineCaches(&kind);
+}
+
+
+void Code::ClearInlineCaches(Code::Kind* kind) {
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) |
RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID) |
@@ -10573,7 +10741,9 @@ void Code::ClearInlineCaches() {
RelocInfo* info = it.rinfo();
Code* target(Code::GetCodeFromTargetAddress(info->target_address()));
if (target->is_inline_cache_stub()) {
- IC::Clear(this->GetIsolate(), info->pc());
+ if (kind == NULL || *kind == target->kind()) {
+ IC::Clear(this->GetIsolate(), info->pc());
+ }
}
}
}
@@ -11746,6 +11916,8 @@ Handle<Object> JSObject::SetPrototype(Handle<JSObject> object,
}
}
+ bool has_element_callbacks_in_chain =
+ object->map()->MayHaveIndexedCallbacksInPrototypeChain();
Handle<JSObject> real_receiver = object;
if (skip_hidden_prototypes) {
@@ -11778,6 +11950,13 @@ Handle<Object> JSObject::SetPrototype(Handle<JSObject> object,
ASSERT(new_map->prototype() == *value);
real_receiver->set_map(*new_map);
+ if (!has_element_callbacks_in_chain &&
+ new_map->MayHaveIndexedCallbacksInPrototypeChain()) {
+ // If our prototype chain didn't have element callbacks, and now we do
+ // have them, then we need to clear KeyedStoreICs.
+ object->GetHeap()->ClearAllICsByKind(Code::KEYED_STORE_IC);
+ }
+
heap->ClearInstanceofCache();
ASSERT(size == object->Size());
return value;
@@ -12767,8 +12946,11 @@ MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) {
if (from_kind == to_kind) return this;
- MaybeObject* maybe_failure = UpdateAllocationSite(to_kind);
- if (maybe_failure->IsFailure()) return maybe_failure;
+ // Don't update the site if to_kind isn't fast
+ if (IsFastElementsKind(to_kind)) {
+ MaybeObject* maybe_failure = UpdateAllocationSite(to_kind);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ }
Isolate* isolate = GetIsolate();
if (elements() == isolate->heap()->empty_fixed_array() ||
« src/objects.h ('K') | « src/objects.h ('k') | src/objects-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698