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

Side by Side Diff: src/objects.cc

Issue 776143005: Optimize Object.seal and Object.preventExtensions (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Add nonextensible and sealed as special transitions Created 6 years 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 unified diff | Download patch
« no previous file with comments | « src/objects.h ('k') | src/objects-inl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 the V8 project authors. All rights reserved. 1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <sstream> 5 #include <sstream>
6 6
7 #include "src/v8.h" 7 #include "src/v8.h"
8 8
9 #include "src/accessors.h" 9 #include "src/accessors.h"
10 #include "src/allocation-site-scopes.h" 10 #include "src/allocation-site-scopes.h"
(...skipping 5351 matching lines...) Expand 10 before | Expand all | Expand 10 after
5362 return JSObject::cast(context->extension())->ReferencesObject(obj); 5362 return JSObject::cast(context->extension())->ReferencesObject(obj);
5363 } 5363 }
5364 } 5364 }
5365 5365
5366 // No references to object. 5366 // No references to object.
5367 return false; 5367 return false;
5368 } 5368 }
5369 5369
5370 5370
5371 MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { 5371 MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
5372 if (!object->map()->is_extensible()) return object;
5373
5374 if (!object->HasSloppyArgumentsElements() && !object->map()->is_observed()) {
5375 return PreventExtensionsWithTransition<NONE>(object);
5376 }
5377
5372 Isolate* isolate = object->GetIsolate(); 5378 Isolate* isolate = object->GetIsolate();
5373 5379
5374 if (!object->map()->is_extensible()) return object;
5375
5376 if (object->IsAccessCheckNeeded() && 5380 if (object->IsAccessCheckNeeded() &&
5377 !isolate->MayNamedAccess( 5381 !isolate->MayNamedAccess(
5378 object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) { 5382 object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
5379 isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS); 5383 isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS);
5380 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); 5384 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
5381 return isolate->factory()->false_value(); 5385 return isolate->factory()->false_value();
5382 } 5386 }
5383 5387
5384 if (object->IsJSGlobalProxy()) { 5388 if (object->IsJSGlobalProxy()) {
5385 PrototypeIterator iter(isolate, object); 5389 PrototypeIterator iter(isolate, object);
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
5419 RETURN_ON_EXCEPTION( 5423 RETURN_ON_EXCEPTION(
5420 isolate, 5424 isolate,
5421 EnqueueChangeRecord(object, "preventExtensions", Handle<Name>(), 5425 EnqueueChangeRecord(object, "preventExtensions", Handle<Name>(),
5422 isolate->factory()->the_hole_value()), 5426 isolate->factory()->the_hole_value()),
5423 Object); 5427 Object);
5424 } 5428 }
5425 return object; 5429 return object;
5426 } 5430 }
5427 5431
5428 5432
5429 template<typename Dictionary> 5433 Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
5430 static void FreezeDictionary(Dictionary* dictionary) { 5434 Handle<JSObject> object) {
5435 DCHECK(!object->elements()->IsDictionary());
5436 Isolate* isolate = object->GetIsolate();
5437 int length = object->IsJSArray()
5438 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
5439 : object->elements()->length();
5440 if (length > 0) {
5441 int capacity = 0;
5442 int used = 0;
5443 object->GetElementsCapacityAndUsage(&capacity, &used);
5444 Handle<SeededNumberDictionary> new_element_dictionary =
5445 SeededNumberDictionary::New(isolate, used);
5446
5447 // Move elements to a dictionary; avoid calling NormalizeElements to avoid
5448 // unnecessary transitions.
5449 return CopyFastElementsToDictionary(handle(object->elements()), length,
5450 new_element_dictionary);
5451 }
5452 // No existing elements, use a pre-allocated empty backing store
5453 return isolate->factory()->empty_slow_element_dictionary();
5454 }
5455
5456
5457 template <typename Dictionary>
5458 static void ApplyAttributesToDictionary(Dictionary* dictionary,
5459 const PropertyAttributes attributes) {
5431 int capacity = dictionary->Capacity(); 5460 int capacity = dictionary->Capacity();
5432 for (int i = 0; i < capacity; i++) { 5461 for (int i = 0; i < capacity; i++) {
5433 Object* k = dictionary->KeyAt(i); 5462 Object* k = dictionary->KeyAt(i);
5434 if (dictionary->IsKey(k) && 5463 if (dictionary->IsKey(k) &&
5435 !(k->IsSymbol() && Symbol::cast(k)->is_private())) { 5464 !(k->IsSymbol() && Symbol::cast(k)->is_private())) {
5436 PropertyDetails details = dictionary->DetailsAt(i); 5465 PropertyDetails details = dictionary->DetailsAt(i);
5437 int attrs = DONT_DELETE; 5466 int attrs = attributes;
5438 // READ_ONLY is an invalid attribute for JS setters/getters. 5467 // READ_ONLY is an invalid attribute for JS setters/getters.
5439 if (details.type() == CALLBACKS) { 5468 if ((attributes & READ_ONLY) && details.type() == CALLBACKS) {
5440 Object* v = dictionary->ValueAt(i); 5469 Object* v = dictionary->ValueAt(i);
5441 if (v->IsPropertyCell()) v = PropertyCell::cast(v)->value(); 5470 if (v->IsPropertyCell()) v = PropertyCell::cast(v)->value();
5442 if (!v->IsAccessorPair()) attrs |= READ_ONLY; 5471 if (v->IsAccessorPair()) attrs &= ~READ_ONLY;
5443 } else {
5444 attrs |= READ_ONLY;
5445 } 5472 }
5446 details = details.CopyAddAttributes( 5473 details = details.CopyAddAttributes(
5447 static_cast<PropertyAttributes>(attrs)); 5474 static_cast<PropertyAttributes>(attrs));
5448 dictionary->DetailsAtPut(i, details); 5475 dictionary->DetailsAtPut(i, details);
5449 } 5476 }
5450 } 5477 }
5451 } 5478 }
5452 5479
5453 5480
5454 MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) { 5481 template <PropertyAttributes attrs>
5455 // Freezing sloppy arguments should be handled elsewhere. 5482 MaybeHandle<Object> JSObject::PreventExtensionsWithTransition(
5483 Handle<JSObject> object) {
5484 STATIC_ASSERT(attrs == NONE || attrs == SEALED || attrs == FROZEN);
5485
5486 // Sealing/freezing sloppy arguments should be handled elsewhere.
5456 DCHECK(!object->HasSloppyArgumentsElements()); 5487 DCHECK(!object->HasSloppyArgumentsElements());
5457 DCHECK(!object->map()->is_observed()); 5488 DCHECK(!object->map()->is_observed());
5458 5489
5459 if (object->map()->is_frozen()) return object;
5460
5461 Isolate* isolate = object->GetIsolate(); 5490 Isolate* isolate = object->GetIsolate();
5462 if (object->IsAccessCheckNeeded() && 5491 if (object->IsAccessCheckNeeded() &&
5463 !isolate->MayNamedAccess( 5492 !isolate->MayNamedAccess(
5464 object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) { 5493 object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
5465 isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS); 5494 isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS);
5466 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); 5495 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
5467 return isolate->factory()->false_value(); 5496 return isolate->factory()->false_value();
5468 } 5497 }
5469 5498
5470 if (object->IsJSGlobalProxy()) { 5499 if (object->IsJSGlobalProxy()) {
5471 PrototypeIterator iter(isolate, object); 5500 PrototypeIterator iter(isolate, object);
5472 if (iter.IsAtEnd()) return object; 5501 if (iter.IsAtEnd()) return object;
5473 DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); 5502 DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
5474 return Freeze(Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter))); 5503 return PreventExtensionsWithTransition<attrs>(
5504 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)));
5475 } 5505 }
5476 5506
5477 // It's not possible to freeze objects with external array elements 5507 // It's not possible to seal or freeze objects with external array elements
5478 if (object->HasExternalArrayElements() || 5508 if (object->HasExternalArrayElements() ||
5479 object->HasFixedTypedArrayElements()) { 5509 object->HasFixedTypedArrayElements()) {
5480 THROW_NEW_ERROR(isolate, 5510 THROW_NEW_ERROR(isolate,
5481 NewTypeError("cant_prevent_ext_external_array_elements", 5511 NewTypeError("cant_prevent_ext_external_array_elements",
5482 HandleVector(&object, 1)), 5512 HandleVector(&object, 1)),
5483 Object); 5513 Object);
5484 } 5514 }
5485 5515
5486 Handle<SeededNumberDictionary> new_element_dictionary; 5516 Handle<SeededNumberDictionary> new_element_dictionary;
5487 if (!object->elements()->IsDictionary()) { 5517 if (!object->elements()->IsDictionary()) {
5488 int length = object->IsJSArray() 5518 new_element_dictionary = GetNormalizedElementDictionary(object);
5489 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value() 5519 }
5490 : object->elements()->length();
5491 if (length > 0) {
5492 int capacity = 0;
5493 int used = 0;
5494 object->GetElementsCapacityAndUsage(&capacity, &used);
5495 new_element_dictionary = SeededNumberDictionary::New(isolate, used);
5496 5520
5497 // Move elements to a dictionary; avoid calling NormalizeElements to avoid 5521 Handle<Symbol> transition_marker;
5498 // unnecessary transitions. 5522 if (attrs == NONE) {
5499 new_element_dictionary = CopyFastElementsToDictionary( 5523 transition_marker = isolate->factory()->nonextensible_symbol();
5500 handle(object->elements()), length, new_element_dictionary); 5524 } else if (attrs == SEALED) {
5501 } else { 5525 transition_marker = isolate->factory()->sealed_symbol();
5502 // No existing elements, use a pre-allocated empty backing store 5526 } else {
5503 new_element_dictionary = 5527 DCHECK(attrs == FROZEN);
5504 isolate->factory()->empty_slow_element_dictionary(); 5528 transition_marker = isolate->factory()->frozen_symbol();
5505 }
5506 } 5529 }
5507 5530
5508 Handle<Map> old_map(object->map(), isolate); 5531 Handle<Map> old_map(object->map(), isolate);
5509 int transition_index = 5532 int transition_index = old_map->SearchSpecialTransition(*transition_marker);
5510 old_map->SearchSpecialTransition(isolate->heap()->frozen_symbol());
5511 if (transition_index != TransitionArray::kNotFound) { 5533 if (transition_index != TransitionArray::kNotFound) {
5512 Handle<Map> transition_map(old_map->GetTransition(transition_index)); 5534 Handle<Map> transition_map(old_map->GetTransition(transition_index));
5513 DCHECK(transition_map->has_dictionary_elements()); 5535 DCHECK(transition_map->has_dictionary_elements());
5514 DCHECK(transition_map->is_frozen());
5515 DCHECK(!transition_map->is_extensible()); 5536 DCHECK(!transition_map->is_extensible());
5516 JSObject::MigrateToMap(object, transition_map); 5537 JSObject::MigrateToMap(object, transition_map);
5517 } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) { 5538 } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
5518 // Create a new descriptor array with fully-frozen properties 5539 // Create a new descriptor array with the appropriate property attributes
5519 Handle<Map> new_map = Map::CopyForFreeze(old_map); 5540 Handle<Map> new_map = Map::CopyForPreventExtensions(
5541 old_map, attrs, transition_marker, "CopyForPreventExtensions");
5520 JSObject::MigrateToMap(object, new_map); 5542 JSObject::MigrateToMap(object, new_map);
5521 } else { 5543 } else {
5522 DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map()); 5544 DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map());
5523 // Slow path: need to normalize properties for safety 5545 // Slow path: need to normalize properties for safety
5524 NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "SlowFreeze"); 5546 NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0,
5547 "SlowPreventExtensions");
5525 5548
5526 // Create a new map, since other objects with this map may be extensible. 5549 // Create a new map, since other objects with this map may be extensible.
5527 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. 5550 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
5528 Handle<Map> new_map = Map::Copy(handle(object->map()), "SlowCopyForFreeze"); 5551 Handle<Map> new_map =
5529 new_map->freeze(); 5552 Map::Copy(handle(object->map()), "SlowCopyForPreventExtensions");
5530 new_map->set_is_extensible(false); 5553 new_map->set_is_extensible(false);
5531 new_map->set_elements_kind(DICTIONARY_ELEMENTS); 5554 new_map->set_elements_kind(DICTIONARY_ELEMENTS);
5532 JSObject::MigrateToMap(object, new_map); 5555 JSObject::MigrateToMap(object, new_map);
5533 5556
5534 // Freeze dictionary-mode properties 5557 if (attrs != NONE) {
5535 FreezeDictionary(object->property_dictionary()); 5558 ApplyAttributesToDictionary(object->property_dictionary(), attrs);
5559 }
5536 } 5560 }
5537 5561
5538 DCHECK(object->map()->has_dictionary_elements()); 5562 DCHECK(object->map()->has_dictionary_elements());
5539 if (!new_element_dictionary.is_null()) { 5563 if (!new_element_dictionary.is_null()) {
5540 object->set_elements(*new_element_dictionary); 5564 object->set_elements(*new_element_dictionary);
5541 } 5565 }
5542 5566
5543 if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) { 5567 if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) {
5544 SeededNumberDictionary* dictionary = object->element_dictionary(); 5568 SeededNumberDictionary* dictionary = object->element_dictionary();
5545 // Make sure we never go back to the fast case 5569 // Make sure we never go back to the fast case
5546 dictionary->set_requires_slow_elements(); 5570 dictionary->set_requires_slow_elements();
5547 // Freeze all elements in the dictionary 5571 if (attrs != NONE) {
5548 FreezeDictionary(dictionary); 5572 ApplyAttributesToDictionary(dictionary, attrs);
5573 }
5549 } 5574 }
5550 5575
5551 return object; 5576 return object;
5552 } 5577 }
5553 5578
5554 5579
5580 MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
5581 return PreventExtensionsWithTransition<FROZEN>(object);
5582 }
5583
5584
5585 MaybeHandle<Object> JSObject::Seal(Handle<JSObject> object) {
5586 return PreventExtensionsWithTransition<SEALED>(object);
5587 }
5588
5589
5555 void JSObject::SetObserved(Handle<JSObject> object) { 5590 void JSObject::SetObserved(Handle<JSObject> object) {
5556 DCHECK(!object->IsJSGlobalProxy()); 5591 DCHECK(!object->IsJSGlobalProxy());
5557 DCHECK(!object->IsJSGlobalObject()); 5592 DCHECK(!object->IsJSGlobalObject());
5558 Isolate* isolate = object->GetIsolate(); 5593 Isolate* isolate = object->GetIsolate();
5559 Handle<Map> new_map; 5594 Handle<Map> new_map;
5560 Handle<Map> old_map(object->map(), isolate); 5595 Handle<Map> old_map(object->map(), isolate);
5561 DCHECK(!old_map->is_observed()); 5596 DCHECK(!old_map->is_observed());
5562 int transition_index = 5597 int transition_index =
5563 old_map->SearchSpecialTransition(isolate->heap()->observed_symbol()); 5598 old_map->SearchSpecialTransition(isolate->heap()->observed_symbol());
5564 if (transition_index != TransitionArray::kNotFound) { 5599 if (transition_index != TransitionArray::kNotFound) {
(...skipping 1454 matching lines...) Expand 10 before | Expand all | Expand 10 after
7019 7054
7020 // Adjust the map with the extra inobject properties. 7055 // Adjust the map with the extra inobject properties.
7021 copy->set_inobject_properties(inobject_properties); 7056 copy->set_inobject_properties(inobject_properties);
7022 copy->set_unused_property_fields(inobject_properties); 7057 copy->set_unused_property_fields(inobject_properties);
7023 copy->set_instance_size(new_instance_size); 7058 copy->set_instance_size(new_instance_size);
7024 copy->set_visitor_id(StaticVisitorBase::GetVisitorId(*copy)); 7059 copy->set_visitor_id(StaticVisitorBase::GetVisitorId(*copy));
7025 return copy; 7060 return copy;
7026 } 7061 }
7027 7062
7028 7063
7029 Handle<Map> Map::CopyForFreeze(Handle<Map> map) { 7064 Handle<Map> Map::CopyForPreventExtensions(Handle<Map> map,
7065 PropertyAttributes attrs_to_add,
7066 Handle<Symbol> transition_marker,
7067 const char* reason) {
7030 int num_descriptors = map->NumberOfOwnDescriptors(); 7068 int num_descriptors = map->NumberOfOwnDescriptors();
7031 Isolate* isolate = map->GetIsolate(); 7069 Isolate* isolate = map->GetIsolate();
7032 Handle<DescriptorArray> new_desc = DescriptorArray::CopyUpToAddAttributes( 7070 Handle<DescriptorArray> new_desc = DescriptorArray::CopyUpToAddAttributes(
7033 handle(map->instance_descriptors(), isolate), num_descriptors, FROZEN); 7071 handle(map->instance_descriptors(), isolate), num_descriptors,
7072 attrs_to_add);
7034 Handle<LayoutDescriptor> new_layout_descriptor(map->GetLayoutDescriptor(), 7073 Handle<LayoutDescriptor> new_layout_descriptor(map->GetLayoutDescriptor(),
7035 isolate); 7074 isolate);
7036 Handle<Map> new_map = CopyReplaceDescriptors( 7075 Handle<Map> new_map = CopyReplaceDescriptors(
7037 map, new_desc, new_layout_descriptor, INSERT_TRANSITION, 7076 map, new_desc, new_layout_descriptor, INSERT_TRANSITION,
7038 isolate->factory()->frozen_symbol(), "CopyForFreeze", SPECIAL_TRANSITION); 7077 transition_marker, reason, SPECIAL_TRANSITION);
7039 new_map->freeze();
7040 new_map->set_is_extensible(false); 7078 new_map->set_is_extensible(false);
7041 new_map->set_elements_kind(DICTIONARY_ELEMENTS); 7079 new_map->set_elements_kind(DICTIONARY_ELEMENTS);
7042 return new_map; 7080 return new_map;
7043 } 7081 }
7044 7082
7045 7083
7046 bool DescriptorArray::CanHoldValue(int descriptor, Object* value) { 7084 bool DescriptorArray::CanHoldValue(int descriptor, Object* value) {
7047 PropertyDetails details = GetDetails(descriptor); 7085 PropertyDetails details = GetDetails(descriptor);
7048 switch (details.type()) { 7086 switch (details.type()) {
7049 case FIELD: 7087 case FIELD:
(...skipping 2334 matching lines...) Expand 10 before | Expand all | Expand 10 after
9384 } 9422 }
9385 9423
9386 9424
9387 static bool CheckEquivalent(Map* first, Map* second) { 9425 static bool CheckEquivalent(Map* first, Map* second) {
9388 return 9426 return
9389 first->constructor() == second->constructor() && 9427 first->constructor() == second->constructor() &&
9390 first->prototype() == second->prototype() && 9428 first->prototype() == second->prototype() &&
9391 first->instance_type() == second->instance_type() && 9429 first->instance_type() == second->instance_type() &&
9392 first->bit_field() == second->bit_field() && 9430 first->bit_field() == second->bit_field() &&
9393 first->bit_field2() == second->bit_field2() && 9431 first->bit_field2() == second->bit_field2() &&
9394 first->is_frozen() == second->is_frozen() &&
9395 first->has_instance_call_handler() == second->has_instance_call_handler(); 9432 first->has_instance_call_handler() == second->has_instance_call_handler();
9396 } 9433 }
9397 9434
9398 9435
9399 bool Map::EquivalentToForTransition(Map* other) { 9436 bool Map::EquivalentToForTransition(Map* other) {
9400 return CheckEquivalent(this, other); 9437 return CheckEquivalent(this, other);
9401 } 9438 }
9402 9439
9403 9440
9404 bool Map::EquivalentToForNormalization(Map* other, 9441 bool Map::EquivalentToForNormalization(Map* other,
(...skipping 7490 matching lines...) Expand 10 before | Expand all | Expand 10 after
16895 Handle<DependentCode> codes = 16932 Handle<DependentCode> codes =
16896 DependentCode::Insert(handle(cell->dependent_code(), info->isolate()), 16933 DependentCode::Insert(handle(cell->dependent_code(), info->isolate()),
16897 DependentCode::kPropertyCellChangedGroup, 16934 DependentCode::kPropertyCellChangedGroup,
16898 info->object_wrapper()); 16935 info->object_wrapper());
16899 if (*codes != cell->dependent_code()) cell->set_dependent_code(*codes); 16936 if (*codes != cell->dependent_code()) cell->set_dependent_code(*codes);
16900 info->dependencies(DependentCode::kPropertyCellChangedGroup)->Add( 16937 info->dependencies(DependentCode::kPropertyCellChangedGroup)->Add(
16901 cell, info->zone()); 16938 cell, info->zone());
16902 } 16939 }
16903 16940
16904 } } // namespace v8::internal 16941 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/objects.h ('k') | src/objects-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698