| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index e95ff10a4287445b8088d8fa865fa5c37ef49656..2a33d264d8dff8f8d55da031544bfaf3ca7b2a67 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -5369,10 +5369,14 @@ bool JSObject::ReferencesObject(Object* obj) {
|
|
|
|
|
| MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
|
| - Isolate* isolate = object->GetIsolate();
|
| -
|
| if (!object->map()->is_extensible()) return object;
|
|
|
| + if (!object->HasSloppyArgumentsElements() && !object->map()->is_observed()) {
|
| + return PreventExtensionsWithTransition<NONE>(object);
|
| + }
|
| +
|
| + Isolate* isolate = object->GetIsolate();
|
| +
|
| if (object->IsAccessCheckNeeded() &&
|
| !isolate->MayNamedAccess(
|
| object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
|
| @@ -5426,22 +5430,45 @@ MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
|
| }
|
|
|
|
|
| -template<typename Dictionary>
|
| -static void FreezeDictionary(Dictionary* dictionary) {
|
| +Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
|
| + Handle<JSObject> object) {
|
| + DCHECK(!object->elements()->IsDictionary());
|
| + Isolate* isolate = object->GetIsolate();
|
| + int length = object->IsJSArray()
|
| + ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
|
| + : object->elements()->length();
|
| + if (length > 0) {
|
| + int capacity = 0;
|
| + int used = 0;
|
| + object->GetElementsCapacityAndUsage(&capacity, &used);
|
| + Handle<SeededNumberDictionary> new_element_dictionary =
|
| + SeededNumberDictionary::New(isolate, used);
|
| +
|
| + // Move elements to a dictionary; avoid calling NormalizeElements to avoid
|
| + // unnecessary transitions.
|
| + return CopyFastElementsToDictionary(handle(object->elements()), length,
|
| + new_element_dictionary);
|
| + }
|
| + // No existing elements, use a pre-allocated empty backing store
|
| + return isolate->factory()->empty_slow_element_dictionary();
|
| +}
|
| +
|
| +
|
| +template <typename Dictionary>
|
| +static void ApplyAttributesToDictionary(Dictionary* dictionary,
|
| + const PropertyAttributes attributes) {
|
| int capacity = dictionary->Capacity();
|
| for (int i = 0; i < capacity; i++) {
|
| Object* k = dictionary->KeyAt(i);
|
| if (dictionary->IsKey(k) &&
|
| !(k->IsSymbol() && Symbol::cast(k)->is_private())) {
|
| PropertyDetails details = dictionary->DetailsAt(i);
|
| - int attrs = DONT_DELETE;
|
| + int attrs = attributes;
|
| // READ_ONLY is an invalid attribute for JS setters/getters.
|
| - if (details.type() == CALLBACKS) {
|
| + if ((attributes & READ_ONLY) && details.type() == CALLBACKS) {
|
| Object* v = dictionary->ValueAt(i);
|
| if (v->IsPropertyCell()) v = PropertyCell::cast(v)->value();
|
| - if (!v->IsAccessorPair()) attrs |= READ_ONLY;
|
| - } else {
|
| - attrs |= READ_ONLY;
|
| + if (v->IsAccessorPair()) attrs &= ~READ_ONLY;
|
| }
|
| details = details.CopyAddAttributes(
|
| static_cast<PropertyAttributes>(attrs));
|
| @@ -5451,13 +5478,15 @@ static void FreezeDictionary(Dictionary* dictionary) {
|
| }
|
|
|
|
|
| -MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
|
| - // Freezing sloppy arguments should be handled elsewhere.
|
| +template <PropertyAttributes attrs>
|
| +MaybeHandle<Object> JSObject::PreventExtensionsWithTransition(
|
| + Handle<JSObject> object) {
|
| + STATIC_ASSERT(attrs == NONE || attrs == SEALED || attrs == FROZEN);
|
| +
|
| + // Sealing/freezing sloppy arguments should be handled elsewhere.
|
| DCHECK(!object->HasSloppyArgumentsElements());
|
| DCHECK(!object->map()->is_observed());
|
|
|
| - if (object->map()->is_frozen()) return object;
|
| -
|
| Isolate* isolate = object->GetIsolate();
|
| if (object->IsAccessCheckNeeded() &&
|
| !isolate->MayNamedAccess(
|
| @@ -5471,10 +5500,11 @@ MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
|
| PrototypeIterator iter(isolate, object);
|
| if (iter.IsAtEnd()) return object;
|
| DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
|
| - return Freeze(Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)));
|
| + return PreventExtensionsWithTransition<attrs>(
|
| + Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)));
|
| }
|
|
|
| - // It's not possible to freeze objects with external array elements
|
| + // It's not possible to seal or freeze objects with external array elements
|
| if (object->HasExternalArrayElements() ||
|
| object->HasFixedTypedArrayElements()) {
|
| THROW_NEW_ERROR(isolate,
|
| @@ -5485,54 +5515,48 @@ MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
|
|
|
| Handle<SeededNumberDictionary> new_element_dictionary;
|
| if (!object->elements()->IsDictionary()) {
|
| - int length = object->IsJSArray()
|
| - ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
|
| - : object->elements()->length();
|
| - if (length > 0) {
|
| - int capacity = 0;
|
| - int used = 0;
|
| - object->GetElementsCapacityAndUsage(&capacity, &used);
|
| - new_element_dictionary = SeededNumberDictionary::New(isolate, used);
|
| -
|
| - // Move elements to a dictionary; avoid calling NormalizeElements to avoid
|
| - // unnecessary transitions.
|
| - new_element_dictionary = CopyFastElementsToDictionary(
|
| - handle(object->elements()), length, new_element_dictionary);
|
| - } else {
|
| - // No existing elements, use a pre-allocated empty backing store
|
| - new_element_dictionary =
|
| - isolate->factory()->empty_slow_element_dictionary();
|
| - }
|
| + new_element_dictionary = GetNormalizedElementDictionary(object);
|
| + }
|
| +
|
| + Handle<Symbol> transition_marker;
|
| + if (attrs == NONE) {
|
| + transition_marker = isolate->factory()->nonextensible_symbol();
|
| + } else if (attrs == SEALED) {
|
| + transition_marker = isolate->factory()->sealed_symbol();
|
| + } else {
|
| + DCHECK(attrs == FROZEN);
|
| + transition_marker = isolate->factory()->frozen_symbol();
|
| }
|
|
|
| Handle<Map> old_map(object->map(), isolate);
|
| - int transition_index =
|
| - old_map->SearchSpecialTransition(isolate->heap()->frozen_symbol());
|
| + int transition_index = old_map->SearchSpecialTransition(*transition_marker);
|
| if (transition_index != TransitionArray::kNotFound) {
|
| Handle<Map> transition_map(old_map->GetTransition(transition_index));
|
| DCHECK(transition_map->has_dictionary_elements());
|
| - DCHECK(transition_map->is_frozen());
|
| DCHECK(!transition_map->is_extensible());
|
| JSObject::MigrateToMap(object, transition_map);
|
| } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
|
| - // Create a new descriptor array with fully-frozen properties
|
| - Handle<Map> new_map = Map::CopyForFreeze(old_map);
|
| + // Create a new descriptor array with the appropriate property attributes
|
| + Handle<Map> new_map = Map::CopyForPreventExtensions(
|
| + old_map, attrs, transition_marker, "CopyForPreventExtensions");
|
| JSObject::MigrateToMap(object, new_map);
|
| } else {
|
| DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map());
|
| // Slow path: need to normalize properties for safety
|
| - NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "SlowFreeze");
|
| + NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0,
|
| + "SlowPreventExtensions");
|
|
|
| // Create a new map, since other objects with this map may be extensible.
|
| // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
|
| - Handle<Map> new_map = Map::Copy(handle(object->map()), "SlowCopyForFreeze");
|
| - new_map->freeze();
|
| + Handle<Map> new_map =
|
| + Map::Copy(handle(object->map()), "SlowCopyForPreventExtensions");
|
| new_map->set_is_extensible(false);
|
| new_map->set_elements_kind(DICTIONARY_ELEMENTS);
|
| JSObject::MigrateToMap(object, new_map);
|
|
|
| - // Freeze dictionary-mode properties
|
| - FreezeDictionary(object->property_dictionary());
|
| + if (attrs != NONE) {
|
| + ApplyAttributesToDictionary(object->property_dictionary(), attrs);
|
| + }
|
| }
|
|
|
| DCHECK(object->map()->has_dictionary_elements());
|
| @@ -5544,14 +5568,25 @@ MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
|
| SeededNumberDictionary* dictionary = object->element_dictionary();
|
| // Make sure we never go back to the fast case
|
| dictionary->set_requires_slow_elements();
|
| - // Freeze all elements in the dictionary
|
| - FreezeDictionary(dictionary);
|
| + if (attrs != NONE) {
|
| + ApplyAttributesToDictionary(dictionary, attrs);
|
| + }
|
| }
|
|
|
| return object;
|
| }
|
|
|
|
|
| +MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
|
| + return PreventExtensionsWithTransition<FROZEN>(object);
|
| +}
|
| +
|
| +
|
| +MaybeHandle<Object> JSObject::Seal(Handle<JSObject> object) {
|
| + return PreventExtensionsWithTransition<SEALED>(object);
|
| +}
|
| +
|
| +
|
| void JSObject::SetObserved(Handle<JSObject> object) {
|
| DCHECK(!object->IsJSGlobalProxy());
|
| DCHECK(!object->IsJSGlobalObject());
|
| @@ -7026,17 +7061,20 @@ Handle<Map> Map::Create(Isolate* isolate, int inobject_properties) {
|
| }
|
|
|
|
|
| -Handle<Map> Map::CopyForFreeze(Handle<Map> map) {
|
| +Handle<Map> Map::CopyForPreventExtensions(Handle<Map> map,
|
| + PropertyAttributes attrs_to_add,
|
| + Handle<Symbol> transition_marker,
|
| + const char* reason) {
|
| int num_descriptors = map->NumberOfOwnDescriptors();
|
| Isolate* isolate = map->GetIsolate();
|
| Handle<DescriptorArray> new_desc = DescriptorArray::CopyUpToAddAttributes(
|
| - handle(map->instance_descriptors(), isolate), num_descriptors, FROZEN);
|
| + handle(map->instance_descriptors(), isolate), num_descriptors,
|
| + attrs_to_add);
|
| Handle<LayoutDescriptor> new_layout_descriptor(map->GetLayoutDescriptor(),
|
| isolate);
|
| Handle<Map> new_map = CopyReplaceDescriptors(
|
| map, new_desc, new_layout_descriptor, INSERT_TRANSITION,
|
| - isolate->factory()->frozen_symbol(), "CopyForFreeze", SPECIAL_TRANSITION);
|
| - new_map->freeze();
|
| + transition_marker, reason, SPECIAL_TRANSITION);
|
| new_map->set_is_extensible(false);
|
| new_map->set_elements_kind(DICTIONARY_ELEMENTS);
|
| return new_map;
|
| @@ -9391,7 +9429,6 @@ 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->is_frozen() == second->is_frozen() &&
|
| first->has_instance_call_handler() == second->has_instance_call_handler();
|
| }
|
|
|
|
|