| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index 5b1d18f8faea1eeec193f324074c985e968921d5..6b5d80c992d22517346caaf5e330426796275707 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -3085,9 +3085,8 @@ MaybeHandle<Object> Object::SetDataProperty(LookupIterator* it,
|
| // Old value for the observation change record.
|
| // Fetch before transforming the object since the encoding may become
|
| // incompatible with what's cached in |it|.
|
| - bool is_observed =
|
| - receiver->map()->is_observed() &&
|
| - !it->name().is_identical_to(it->factory()->hidden_string());
|
| + bool is_observed = receiver->map()->is_observed() &&
|
| + !it->isolate()->IsInternallyUsedPropertyName(it->name());
|
| MaybeHandle<Object> maybe_old;
|
| if (is_observed) maybe_old = it->GetDataValue();
|
|
|
| @@ -3157,7 +3156,7 @@ MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it,
|
|
|
| // Send the change record if there are observers.
|
| if (receiver->map()->is_observed() &&
|
| - !it->name().is_identical_to(it->factory()->hidden_string())) {
|
| + !it->isolate()->IsInternallyUsedPropertyName(it->name())) {
|
| RETURN_ON_EXCEPTION(it->isolate(), JSObject::EnqueueChangeRecord(
|
| receiver, "add", it->name(),
|
| it->factory()->the_hole_value()),
|
| @@ -3961,7 +3960,7 @@ void JSObject::AddProperty(Handle<JSObject> object, Handle<Name> name,
|
| DCHECK(maybe.has_value);
|
| DCHECK(!it.IsFound());
|
| DCHECK(object->map()->is_extensible() ||
|
| - name.is_identical_to(it.isolate()->factory()->hidden_string()));
|
| + it.isolate()->IsInternallyUsedPropertyName(name));
|
| #endif
|
| AddDataProperty(&it, value, attributes, STRICT,
|
| CERTAINLY_NOT_STORE_FROM_KEYED).Check();
|
| @@ -3979,7 +3978,7 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
|
| DCHECK(!value->IsTheHole());
|
| LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
|
| bool is_observed = object->map()->is_observed() &&
|
| - *name != it.isolate()->heap()->hidden_string();
|
| + !it.isolate()->IsInternallyUsedPropertyName(name);
|
| for (; it.IsFound(); it.Next()) {
|
| switch (it.state()) {
|
| case LookupIterator::INTERCEPTOR:
|
| @@ -5109,7 +5108,7 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
|
| LookupIterator it(object, name, config);
|
|
|
| bool is_observed = object->map()->is_observed() &&
|
| - *name != it.isolate()->heap()->hidden_string();
|
| + !it.isolate()->IsInternallyUsedPropertyName(name);
|
| Handle<Object> old_value = it.isolate()->factory()->the_hole_value();
|
|
|
| for (; it.IsFound(); it.Next()) {
|
| @@ -6370,7 +6369,7 @@ MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object,
|
|
|
| Handle<Object> old_value = isolate->factory()->the_hole_value();
|
| bool is_observed = object->map()->is_observed() &&
|
| - *name != isolate->heap()->hidden_string();
|
| + !isolate->IsInternallyUsedPropertyName(name);
|
| bool preexists = false;
|
| if (is_observed) {
|
| if (is_element) {
|
| @@ -6629,7 +6628,7 @@ Object* JSObject::SlowReverseLookup(Object* value) {
|
| Handle<Map> Map::RawCopy(Handle<Map> map, int instance_size) {
|
| Handle<Map> result = map->GetIsolate()->factory()->NewMap(
|
| map->instance_type(), instance_size);
|
| - result->set_prototype(map->prototype());
|
| + result->SetPrototype(handle(map->prototype(), map->GetIsolate()));
|
| result->set_constructor(map->constructor());
|
| result->set_bit_field(map->bit_field());
|
| result->set_bit_field2(map->bit_field2());
|
| @@ -6825,6 +6824,12 @@ void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child,
|
| parent->set_transitions(*transitions);
|
| }
|
| child->SetBackPointer(*parent);
|
| + if (child->prototype()->IsJSObject()) {
|
| + Handle<JSObject> proto(JSObject::cast(child->prototype()));
|
| + if (!child->ShouldRegisterAsPrototypeUser(proto)) {
|
| + JSObject::UnregisterPrototypeUser(proto, child);
|
| + }
|
| + }
|
| #if TRACE_MAPS
|
| Map::TraceTransition("Transition", *parent, *child, *name);
|
| #endif
|
| @@ -8135,6 +8140,116 @@ bool FixedArray::IsEqualTo(FixedArray* other) {
|
| #endif
|
|
|
|
|
| +// static
|
| +void WeakFixedArray::Set(Handle<WeakFixedArray> array, int index,
|
| + Handle<HeapObject> value) {
|
| + DCHECK(array->IsEmptySlot(index)); // Don't overwrite anything.
|
| + Handle<WeakCell> cell =
|
| + value->IsMap() ? Map::WeakCellForMap(Handle<Map>::cast(value))
|
| + : array->GetIsolate()->factory()->NewWeakCell(value);
|
| + Handle<FixedArray>::cast(array)->set(index + kFirstIndex, *cell);
|
| + if (FLAG_trace_weak_arrays) {
|
| + PrintF("[WeakFixedArray: storing at index %d ]\n", index);
|
| + }
|
| + array->set_last_used_index(index);
|
| +}
|
| +
|
| +
|
| +// static
|
| +Handle<WeakFixedArray> WeakFixedArray::Add(
|
| + Handle<Object> maybe_array, Handle<HeapObject> value,
|
| + SearchForDuplicates search_for_duplicates) {
|
| + Handle<WeakFixedArray> array =
|
| + (maybe_array.is_null() || !maybe_array->IsWeakFixedArray())
|
| + ? Allocate(value->GetIsolate(), 1, Handle<WeakFixedArray>::null())
|
| + : Handle<WeakFixedArray>::cast(maybe_array);
|
| +
|
| + if (search_for_duplicates == kAddIfNotFound) {
|
| + for (int i = 0; i < array->Length(); ++i) {
|
| + if (array->Get(i) == *value) return array;
|
| + }
|
| + } else {
|
| +#ifdef DEBUG
|
| + for (int i = 0; i < array->Length(); ++i) {
|
| + DCHECK_NE(*value, array->Get(i));
|
| + }
|
| +#endif
|
| + }
|
| +
|
| + // Try to store the new entry if there's room. Optimize for consecutive
|
| + // accesses.
|
| + int first_index = array->last_used_index();
|
| + for (int i = first_index;;) {
|
| + if (array->IsEmptySlot((i))) {
|
| + WeakFixedArray::Set(array, i, value);
|
| + return array;
|
| + }
|
| + if (FLAG_trace_weak_arrays) {
|
| + PrintF("[WeakFixedArray: searching for free slot]\n");
|
| + }
|
| + i = (i + 1) % array->Length();
|
| + if (i == first_index) break;
|
| + }
|
| +
|
| + // No usable slot found, grow the array.
|
| + int new_length = array->Length() + (array->Length() >> 1) + 4;
|
| + Handle<WeakFixedArray> new_array =
|
| + Allocate(array->GetIsolate(), new_length, array);
|
| + if (FLAG_trace_weak_arrays) {
|
| + PrintF("[WeakFixedArray: growing to size %d ]\n", new_length);
|
| + }
|
| + WeakFixedArray::Set(new_array, array->Length(), value);
|
| + return new_array;
|
| +}
|
| +
|
| +
|
| +void WeakFixedArray::Remove(Handle<HeapObject> value) {
|
| + // Optimize for the most recently added element to be removed again.
|
| + int first_index = last_used_index();
|
| + for (int i = first_index;;) {
|
| + if (Get(i) == *value) {
|
| + clear(i);
|
| + // Users of WeakFixedArray should make sure that there are no duplicates,
|
| + // they can use Add(..., kAddIfNotFound) if necessary.
|
| + return;
|
| + }
|
| + i = (i + 1) % Length();
|
| + if (i == first_index) break;
|
| + }
|
| +}
|
| +
|
| +
|
| +// static
|
| +Handle<WeakFixedArray> WeakFixedArray::Allocate(
|
| + Isolate* isolate, int size, Handle<WeakFixedArray> initialize_from) {
|
| + DCHECK(0 <= size);
|
| + Handle<FixedArray> result =
|
| + isolate->factory()->NewUninitializedFixedArray(size + kFirstIndex);
|
| + Handle<WeakFixedArray> casted_result = Handle<WeakFixedArray>::cast(result);
|
| + if (initialize_from.is_null()) {
|
| + for (int i = 0; i < result->length(); ++i) {
|
| + result->set(i, Smi::FromInt(0));
|
| + }
|
| + } else {
|
| + DCHECK(initialize_from->Length() <= size);
|
| + Handle<FixedArray> raw_source = Handle<FixedArray>::cast(initialize_from);
|
| + int target_index = kFirstIndex;
|
| + for (int source_index = kFirstIndex; source_index < raw_source->length();
|
| + ++source_index) {
|
| + // The act of allocating might have caused entries in the source array
|
| + // to be cleared. Copy only what's needed.
|
| + if (initialize_from->IsEmptySlot(source_index - kFirstIndex)) continue;
|
| + result->set(target_index++, raw_source->get(source_index));
|
| + }
|
| + casted_result->set_last_used_index(target_index - 1 - kFirstIndex);
|
| + for (; target_index < result->length(); ++target_index) {
|
| + result->set(target_index, Smi::FromInt(0));
|
| + }
|
| + }
|
| + return casted_result;
|
| +}
|
| +
|
| +
|
| Handle<DescriptorArray> DescriptorArray::Allocate(Isolate* isolate,
|
| int number_of_descriptors,
|
| int slack) {
|
| @@ -9716,6 +9831,74 @@ void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
|
| }
|
|
|
|
|
| +void JSObject::RegisterPrototypeUser(Handle<JSObject> prototype,
|
| + Handle<HeapObject> user) {
|
| + DCHECK(FLAG_track_prototype_users);
|
| + Isolate* isolate = prototype->GetIsolate();
|
| + Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
|
| +
|
| + // Get prototype users array, create it if it doesn't exist yet.
|
| + Handle<Object> maybe_array =
|
| + JSObject::GetProperty(prototype, symbol).ToHandleChecked();
|
| +
|
| + Handle<WeakFixedArray> new_array = WeakFixedArray::Add(maybe_array, user);
|
| + if (!maybe_array.is_identical_to(new_array)) {
|
| + JSObject::SetOwnPropertyIgnoreAttributes(prototype, symbol, new_array,
|
| + DONT_ENUM).Assert();
|
| + }
|
| +}
|
| +
|
| +
|
| +void JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype,
|
| + Handle<HeapObject> user) {
|
| + Isolate* isolate = prototype->GetIsolate();
|
| + Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
|
| +
|
| + Handle<Object> maybe_array =
|
| + JSObject::GetProperty(prototype, symbol).ToHandleChecked();
|
| + if (!maybe_array->IsWeakFixedArray()) return;
|
| + Handle<WeakFixedArray>::cast(maybe_array)->Remove(user);
|
| +}
|
| +
|
| +
|
| +void Map::SetPrototype(Handle<Object> prototype,
|
| + PrototypeOptimizationMode proto_mode) {
|
| + if (this->prototype()->IsJSObject() && FLAG_track_prototype_users) {
|
| + Handle<JSObject> old_prototype(JSObject::cast(this->prototype()));
|
| + JSObject::UnregisterPrototypeUser(old_prototype, handle(this));
|
| + }
|
| + if (prototype->IsJSObject()) {
|
| + Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype);
|
| + if (ShouldRegisterAsPrototypeUser(prototype_jsobj)) {
|
| + JSObject::RegisterPrototypeUser(prototype_jsobj, handle(this));
|
| + }
|
| + JSObject::OptimizeAsPrototype(prototype_jsobj, proto_mode);
|
| + }
|
| + WriteBarrierMode wb_mode =
|
| + prototype->IsNull() ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER;
|
| + set_prototype(*prototype, wb_mode);
|
| +}
|
| +
|
| +
|
| +bool Map::ShouldRegisterAsPrototypeUser(Handle<JSObject> prototype) {
|
| + if (!FLAG_track_prototype_users) return false;
|
| + if (this->is_prototype_map()) return true;
|
| + if (this->is_dictionary_map()) return false;
|
| + Object* back = GetBackPointer();
|
| + if (!back->IsMap()) return true;
|
| + if (Map::cast(back)->prototype() != *prototype) return true;
|
| + return false;
|
| +}
|
| +
|
| +
|
| +bool Map::CanUseOptimizationsBasedOnPrototypeRegistry() {
|
| + if (!FLAG_track_prototype_users) return false;
|
| + if (this->is_prototype_map()) return true;
|
| + if (GetBackPointer()->IsMap()) return true;
|
| + return false;
|
| +}
|
| +
|
| +
|
| Handle<Object> CacheInitialJSArrayMaps(
|
| Handle<Context> native_context, Handle<Map> initial_map) {
|
| // Replace all of the cached initial array maps in the native context with
|
| @@ -9854,11 +10037,9 @@ bool JSFunction::RemovePrototype() {
|
|
|
| void JSFunction::SetInitialMap(Handle<JSFunction> function, Handle<Map> map,
|
| Handle<Object> prototype) {
|
| - if (prototype->IsJSObject()) {
|
| - Handle<JSObject> js_proto = Handle<JSObject>::cast(prototype);
|
| - JSObject::OptimizeAsPrototype(js_proto, FAST_PROTOTYPE);
|
| + if (map->prototype() != *prototype) {
|
| + map->SetPrototype(prototype, FAST_PROTOTYPE);
|
| }
|
| - map->set_prototype(*prototype);
|
| function->set_prototype_or_initial_map(*map);
|
| map->set_constructor(*function);
|
| #if TRACE_MAPS
|
| @@ -12010,12 +12191,13 @@ const char* DependentCode::DependencyGroupName(DependencyGroup group) {
|
|
|
|
|
| Handle<Map> Map::TransitionToPrototype(Handle<Map> map,
|
| - Handle<Object> prototype) {
|
| + Handle<Object> prototype,
|
| + PrototypeOptimizationMode mode) {
|
| Handle<Map> new_map = GetPrototypeTransition(map, prototype);
|
| if (new_map.is_null()) {
|
| new_map = Copy(map, "TransitionToPrototype");
|
| PutPrototypeTransition(map, prototype, new_map);
|
| - new_map->set_prototype(*prototype);
|
| + new_map->SetPrototype(prototype, mode);
|
| }
|
| return new_map;
|
| }
|
| @@ -12085,13 +12267,9 @@ MaybeHandle<Object> JSObject::SetPrototype(Handle<JSObject> object,
|
| // Nothing to do if prototype is already set.
|
| if (map->prototype() == *value) return value;
|
|
|
| - if (value->IsJSObject()) {
|
| - PrototypeOptimizationMode mode =
|
| - from_javascript ? REGULAR_PROTOTYPE : FAST_PROTOTYPE;
|
| - JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value), mode);
|
| - }
|
| -
|
| - Handle<Map> new_map = Map::TransitionToPrototype(map, value);
|
| + PrototypeOptimizationMode mode =
|
| + from_javascript ? REGULAR_PROTOTYPE : FAST_PROTOTYPE;
|
| + Handle<Map> new_map = Map::TransitionToPrototype(map, value, mode);
|
| DCHECK(new_map->prototype() == *value);
|
| JSObject::MigrateToMap(real_receiver, new_map);
|
|
|
|
|