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

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 support for preventExtensions 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
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 5337 matching lines...) Expand 10 before | Expand all | Expand 10 after
5348 return JSObject::cast(context->extension())->ReferencesObject(obj); 5348 return JSObject::cast(context->extension())->ReferencesObject(obj);
5349 } 5349 }
5350 } 5350 }
5351 5351
5352 // No references to object. 5352 // No references to object.
5353 return false; 5353 return false;
5354 } 5354 }
5355 5355
5356 5356
5357 MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { 5357 MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
5358 if (!object->map()->is_extensible()) return object;
5359
5360 if (!object->HasSloppyArgumentsElements() && !object->map()->is_observed()) {
5361 return PreventExtensionsWithTransition<NONE>(object);
5362 }
5363
5358 Isolate* isolate = object->GetIsolate(); 5364 Isolate* isolate = object->GetIsolate();
5359 5365
5360 if (!object->map()->is_extensible()) return object;
5361
5362 if (object->IsAccessCheckNeeded() && 5366 if (object->IsAccessCheckNeeded() &&
5363 !isolate->MayNamedAccess( 5367 !isolate->MayNamedAccess(
5364 object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) { 5368 object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
5365 isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS); 5369 isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS);
5366 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); 5370 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
5367 return isolate->factory()->false_value(); 5371 return isolate->factory()->false_value();
5368 } 5372 }
5369 5373
5370 if (object->IsJSGlobalProxy()) { 5374 if (object->IsJSGlobalProxy()) {
5371 PrototypeIterator iter(isolate, object); 5375 PrototypeIterator iter(isolate, object);
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
5405 RETURN_ON_EXCEPTION( 5409 RETURN_ON_EXCEPTION(
5406 isolate, 5410 isolate,
5407 EnqueueChangeRecord(object, "preventExtensions", Handle<Name>(), 5411 EnqueueChangeRecord(object, "preventExtensions", Handle<Name>(),
5408 isolate->factory()->the_hole_value()), 5412 isolate->factory()->the_hole_value()),
5409 Object); 5413 Object);
5410 } 5414 }
5411 return object; 5415 return object;
5412 } 5416 }
5413 5417
5414 5418
5415 template<typename Dictionary> 5419 Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
5416 static void FreezeDictionary(Dictionary* dictionary) { 5420 Handle<JSObject> object) {
5421 DCHECK(!object->elements()->IsDictionary());
5422 Isolate* isolate = object->GetIsolate();
5423 int length = object->IsJSArray()
5424 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
5425 : object->elements()->length();
5426 if (length > 0) {
5427 int capacity = 0;
5428 int used = 0;
5429 object->GetElementsCapacityAndUsage(&capacity, &used);
5430 Handle<SeededNumberDictionary> new_element_dictionary =
5431 SeededNumberDictionary::New(isolate, used);
5432
5433 // Move elements to a dictionary; avoid calling NormalizeElements to avoid
5434 // unnecessary transitions.
5435 return CopyFastElementsToDictionary(handle(object->elements()), length,
5436 new_element_dictionary);
5437 }
5438 // No existing elements, use a pre-allocated empty backing store
5439 return isolate->factory()->empty_slow_element_dictionary();
5440 }
5441
5442
5443 template <typename Dictionary>
5444 static void ApplyAttributesToDictionary(Dictionary* dictionary,
5445 const PropertyAttributes attributes) {
5417 int capacity = dictionary->Capacity(); 5446 int capacity = dictionary->Capacity();
5418 for (int i = 0; i < capacity; i++) { 5447 for (int i = 0; i < capacity; i++) {
5419 Object* k = dictionary->KeyAt(i); 5448 Object* k = dictionary->KeyAt(i);
5420 if (dictionary->IsKey(k) && 5449 if (dictionary->IsKey(k) &&
5421 !(k->IsSymbol() && Symbol::cast(k)->is_private())) { 5450 !(k->IsSymbol() && Symbol::cast(k)->is_private())) {
5422 PropertyDetails details = dictionary->DetailsAt(i); 5451 PropertyDetails details = dictionary->DetailsAt(i);
5423 int attrs = DONT_DELETE; 5452 int attrs = attributes;
5424 // READ_ONLY is an invalid attribute for JS setters/getters. 5453 // READ_ONLY is an invalid attribute for JS setters/getters.
5425 if (details.type() == CALLBACKS) { 5454 if ((attributes & READ_ONLY) && details.type() == CALLBACKS) {
5426 Object* v = dictionary->ValueAt(i); 5455 Object* v = dictionary->ValueAt(i);
5427 if (v->IsPropertyCell()) v = PropertyCell::cast(v)->value(); 5456 if (v->IsPropertyCell()) v = PropertyCell::cast(v)->value();
5428 if (!v->IsAccessorPair()) attrs |= READ_ONLY; 5457 if (v->IsAccessorPair()) attrs &= ~READ_ONLY;
5429 } else {
5430 attrs |= READ_ONLY;
5431 } 5458 }
5432 details = details.CopyAddAttributes( 5459 details = details.CopyAddAttributes(
5433 static_cast<PropertyAttributes>(attrs)); 5460 static_cast<PropertyAttributes>(attrs));
5434 dictionary->DetailsAtPut(i, details); 5461 dictionary->DetailsAtPut(i, details);
5435 } 5462 }
5436 } 5463 }
5437 } 5464 }
5438 5465
5439 5466
5440 MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) { 5467 template <PropertyAttributes attrs>
5441 // Freezing sloppy arguments should be handled elsewhere. 5468 MaybeHandle<Object> JSObject::PreventExtensionsWithTransition(
5469 Handle<JSObject> object) {
5470 STATIC_ASSERT(attrs == NONE || attrs == SEALED || attrs == FROZEN);
5471
5472 // Sealing/freezing sloppy arguments should be handled elsewhere.
5442 DCHECK(!object->HasSloppyArgumentsElements()); 5473 DCHECK(!object->HasSloppyArgumentsElements());
5443 DCHECK(!object->map()->is_observed()); 5474 DCHECK(!object->map()->is_observed());
5444 5475
5445 if (object->map()->is_frozen()) return object;
5446
5447 Isolate* isolate = object->GetIsolate(); 5476 Isolate* isolate = object->GetIsolate();
5448 if (object->IsAccessCheckNeeded() && 5477 if (object->IsAccessCheckNeeded() &&
5449 !isolate->MayNamedAccess( 5478 !isolate->MayNamedAccess(
5450 object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) { 5479 object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
5451 isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS); 5480 isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS);
5452 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); 5481 RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
5453 return isolate->factory()->false_value(); 5482 return isolate->factory()->false_value();
5454 } 5483 }
5455 5484
5456 if (object->IsJSGlobalProxy()) { 5485 if (object->IsJSGlobalProxy()) {
5457 PrototypeIterator iter(isolate, object); 5486 PrototypeIterator iter(isolate, object);
5458 if (iter.IsAtEnd()) return object; 5487 if (iter.IsAtEnd()) return object;
5459 DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); 5488 DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
5460 return Freeze(Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter))); 5489 return PreventExtensionsWithTransition<attrs>(
5490 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)));
5461 } 5491 }
5462 5492
5463 // It's not possible to freeze objects with external array elements 5493 // It's not possible to seal or freeze objects with external array elements
5464 if (object->HasExternalArrayElements() || 5494 if (object->HasExternalArrayElements() ||
5465 object->HasFixedTypedArrayElements()) { 5495 object->HasFixedTypedArrayElements()) {
5466 THROW_NEW_ERROR(isolate, 5496 THROW_NEW_ERROR(isolate,
5467 NewTypeError("cant_prevent_ext_external_array_elements", 5497 NewTypeError("cant_prevent_ext_external_array_elements",
5468 HandleVector(&object, 1)), 5498 HandleVector(&object, 1)),
5469 Object); 5499 Object);
5470 } 5500 }
5471 5501
5472 Handle<SeededNumberDictionary> new_element_dictionary; 5502 Handle<SeededNumberDictionary> new_element_dictionary;
5473 if (!object->elements()->IsDictionary()) { 5503 if (!object->elements()->IsDictionary()) {
5474 int length = object->IsJSArray() 5504 new_element_dictionary = GetNormalizedElementDictionary(object);
5475 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value() 5505 }
5476 : object->elements()->length();
5477 if (length > 0) {
5478 int capacity = 0;
5479 int used = 0;
5480 object->GetElementsCapacityAndUsage(&capacity, &used);
5481 new_element_dictionary = SeededNumberDictionary::New(isolate, used);
5482 5506
5483 // Move elements to a dictionary; avoid calling NormalizeElements to avoid 5507 Handle<Symbol> transition_marker;
5484 // unnecessary transitions. 5508 if (attrs == NONE) {
5485 new_element_dictionary = CopyFastElementsToDictionary( 5509 transition_marker = isolate->factory()->nonextensible_symbol();
5486 handle(object->elements()), length, new_element_dictionary); 5510 } else if (attrs == SEALED) {
5487 } else { 5511 transition_marker = isolate->factory()->sealed_symbol();
5488 // No existing elements, use a pre-allocated empty backing store 5512 } else {
5489 new_element_dictionary = 5513 DCHECK(attrs == FROZEN);
5490 isolate->factory()->empty_slow_element_dictionary(); 5514 transition_marker = isolate->factory()->frozen_symbol();
5491 }
5492 } 5515 }
5493 5516
5494 Handle<Map> old_map(object->map(), isolate); 5517 Handle<Map> old_map(object->map(), isolate);
5495 int transition_index = 5518 int transition_index = old_map->SearchTransition(*transition_marker);
5496 old_map->SearchTransition(isolate->heap()->frozen_symbol());
5497 if (transition_index != TransitionArray::kNotFound) { 5519 if (transition_index != TransitionArray::kNotFound) {
5498 Handle<Map> transition_map(old_map->GetTransition(transition_index)); 5520 Handle<Map> transition_map(old_map->GetTransition(transition_index));
5499 DCHECK(transition_map->has_dictionary_elements()); 5521 DCHECK(transition_map->has_dictionary_elements());
5500 DCHECK(transition_map->is_frozen());
5501 DCHECK(!transition_map->is_extensible()); 5522 DCHECK(!transition_map->is_extensible());
5502 JSObject::MigrateToMap(object, transition_map); 5523 JSObject::MigrateToMap(object, transition_map);
5503 } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) { 5524 } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) {
5504 // Create a new descriptor array with fully-frozen properties 5525 // Create a new descriptor array with the appropriate property attributes
5505 Handle<Map> new_map = Map::CopyForFreeze(old_map); 5526 Handle<Map> new_map = Map::CopyForPreventExtensions(
5527 old_map, attrs, transition_marker, "CopyForPreventExtensions");
5506 JSObject::MigrateToMap(object, new_map); 5528 JSObject::MigrateToMap(object, new_map);
5507 } else { 5529 } else {
5508 DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map()); 5530 DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map());
5509 // Slow path: need to normalize properties for safety 5531 // Slow path: need to normalize properties for safety
5510 NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "SlowFreeze"); 5532 NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0,
5533 "SlowPreventExtensions");
5511 5534
5512 // Create a new map, since other objects with this map may be extensible. 5535 // Create a new map, since other objects with this map may be extensible.
5513 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. 5536 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
5514 Handle<Map> new_map = Map::Copy(handle(object->map()), "SlowCopyForFreeze"); 5537 Handle<Map> new_map =
5515 new_map->freeze(); 5538 Map::Copy(handle(object->map()), "SlowCopyForPreventExtensions");
5516 new_map->set_is_extensible(false); 5539 new_map->set_is_extensible(false);
5517 new_map->set_elements_kind(DICTIONARY_ELEMENTS); 5540 new_map->set_elements_kind(DICTIONARY_ELEMENTS);
5518 JSObject::MigrateToMap(object, new_map); 5541 JSObject::MigrateToMap(object, new_map);
5519 5542
5520 // Freeze dictionary-mode properties 5543 if (attrs != NONE) {
5521 FreezeDictionary(object->property_dictionary()); 5544 ApplyAttributesToDictionary(object->property_dictionary(), attrs);
5545 }
5522 } 5546 }
5523 5547
5524 DCHECK(object->map()->has_dictionary_elements()); 5548 DCHECK(object->map()->has_dictionary_elements());
5525 if (!new_element_dictionary.is_null()) { 5549 if (!new_element_dictionary.is_null()) {
5526 object->set_elements(*new_element_dictionary); 5550 object->set_elements(*new_element_dictionary);
5527 } 5551 }
5528 5552
5529 if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) { 5553 if (object->elements() != isolate->heap()->empty_slow_element_dictionary()) {
5530 SeededNumberDictionary* dictionary = object->element_dictionary(); 5554 SeededNumberDictionary* dictionary = object->element_dictionary();
5531 // Make sure we never go back to the fast case 5555 // Make sure we never go back to the fast case
5532 dictionary->set_requires_slow_elements(); 5556 dictionary->set_requires_slow_elements();
5533 // Freeze all elements in the dictionary 5557 if (attrs != NONE) {
5534 FreezeDictionary(dictionary); 5558 ApplyAttributesToDictionary(dictionary, attrs);
5559 }
5535 } 5560 }
5536 5561
5537 return object; 5562 return object;
5538 } 5563 }
5539 5564
5540 5565
5566 MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
5567 return PreventExtensionsWithTransition<FROZEN>(object);
5568 }
5569
5570
5571 MaybeHandle<Object> JSObject::Seal(Handle<JSObject> object) {
5572 return PreventExtensionsWithTransition<SEALED>(object);
5573 }
5574
5575
5541 void JSObject::SetObserved(Handle<JSObject> object) { 5576 void JSObject::SetObserved(Handle<JSObject> object) {
5542 DCHECK(!object->IsJSGlobalProxy()); 5577 DCHECK(!object->IsJSGlobalProxy());
5543 DCHECK(!object->IsJSGlobalObject()); 5578 DCHECK(!object->IsJSGlobalObject());
5544 Isolate* isolate = object->GetIsolate(); 5579 Isolate* isolate = object->GetIsolate();
5545 Handle<Map> new_map; 5580 Handle<Map> new_map;
5546 Handle<Map> old_map(object->map(), isolate); 5581 Handle<Map> old_map(object->map(), isolate);
5547 DCHECK(!old_map->is_observed()); 5582 DCHECK(!old_map->is_observed());
5548 int transition_index = 5583 int transition_index =
5549 old_map->SearchTransition(isolate->heap()->observed_symbol()); 5584 old_map->SearchTransition(isolate->heap()->observed_symbol());
5550 if (transition_index != TransitionArray::kNotFound) { 5585 if (transition_index != TransitionArray::kNotFound) {
(...skipping 1454 matching lines...) Expand 10 before | Expand all | Expand 10 after
7005 7040
7006 // Adjust the map with the extra inobject properties. 7041 // Adjust the map with the extra inobject properties.
7007 copy->set_inobject_properties(inobject_properties); 7042 copy->set_inobject_properties(inobject_properties);
7008 copy->set_unused_property_fields(inobject_properties); 7043 copy->set_unused_property_fields(inobject_properties);
7009 copy->set_instance_size(new_instance_size); 7044 copy->set_instance_size(new_instance_size);
7010 copy->set_visitor_id(StaticVisitorBase::GetVisitorId(*copy)); 7045 copy->set_visitor_id(StaticVisitorBase::GetVisitorId(*copy));
7011 return copy; 7046 return copy;
7012 } 7047 }
7013 7048
7014 7049
7015 Handle<Map> Map::CopyForFreeze(Handle<Map> map) { 7050 Handle<Map> Map::CopyForPreventExtensions(Handle<Map> map,
7051 PropertyAttributes attrs_to_add,
7052 Handle<Symbol> transition_marker,
7053 const char* reason) {
7016 int num_descriptors = map->NumberOfOwnDescriptors(); 7054 int num_descriptors = map->NumberOfOwnDescriptors();
7017 Isolate* isolate = map->GetIsolate(); 7055 Isolate* isolate = map->GetIsolate();
7018 Handle<DescriptorArray> new_desc = DescriptorArray::CopyUpToAddAttributes( 7056 Handle<DescriptorArray> new_desc = DescriptorArray::CopyUpToAddAttributes(
7019 handle(map->instance_descriptors(), isolate), num_descriptors, FROZEN); 7057 handle(map->instance_descriptors(), isolate), num_descriptors,
7058 attrs_to_add);
7020 Handle<LayoutDescriptor> new_layout_descriptor(map->GetLayoutDescriptor(), 7059 Handle<LayoutDescriptor> new_layout_descriptor(map->GetLayoutDescriptor(),
7021 isolate); 7060 isolate);
7022 Handle<Map> new_map = CopyReplaceDescriptors( 7061 Handle<Map> new_map =
7023 map, new_desc, new_layout_descriptor, INSERT_TRANSITION, 7062 CopyReplaceDescriptors(map, new_desc, new_layout_descriptor,
7024 isolate->factory()->frozen_symbol(), "CopyForFreeze"); 7063 INSERT_TRANSITION, transition_marker, reason);
7025 new_map->freeze();
7026 new_map->set_is_extensible(false); 7064 new_map->set_is_extensible(false);
7027 new_map->set_elements_kind(DICTIONARY_ELEMENTS); 7065 new_map->set_elements_kind(DICTIONARY_ELEMENTS);
7028 return new_map; 7066 return new_map;
7029 } 7067 }
7030 7068
7031 7069
7032 bool DescriptorArray::CanHoldValue(int descriptor, Object* value) { 7070 bool DescriptorArray::CanHoldValue(int descriptor, Object* value) {
7033 PropertyDetails details = GetDetails(descriptor); 7071 PropertyDetails details = GetDetails(descriptor);
7034 switch (details.type()) { 7072 switch (details.type()) {
7035 case FIELD: 7073 case FIELD:
(...skipping 2345 matching lines...) Expand 10 before | Expand all | Expand 10 after
9381 } 9419 }
9382 9420
9383 9421
9384 static bool CheckEquivalent(Map* first, Map* second) { 9422 static bool CheckEquivalent(Map* first, Map* second) {
9385 return 9423 return
9386 first->constructor() == second->constructor() && 9424 first->constructor() == second->constructor() &&
9387 first->prototype() == second->prototype() && 9425 first->prototype() == second->prototype() &&
9388 first->instance_type() == second->instance_type() && 9426 first->instance_type() == second->instance_type() &&
9389 first->bit_field() == second->bit_field() && 9427 first->bit_field() == second->bit_field() &&
9390 first->bit_field2() == second->bit_field2() && 9428 first->bit_field2() == second->bit_field2() &&
9391 first->is_frozen() == second->is_frozen() &&
9392 first->has_instance_call_handler() == second->has_instance_call_handler(); 9429 first->has_instance_call_handler() == second->has_instance_call_handler();
9393 } 9430 }
9394 9431
9395 9432
9396 bool Map::EquivalentToForTransition(Map* other) { 9433 bool Map::EquivalentToForTransition(Map* other) {
9397 return CheckEquivalent(this, other); 9434 return CheckEquivalent(this, other);
9398 } 9435 }
9399 9436
9400 9437
9401 bool Map::EquivalentToForNormalization(Map* other, 9438 bool Map::EquivalentToForNormalization(Map* other,
(...skipping 7494 matching lines...) Expand 10 before | Expand all | Expand 10 after
16896 Handle<DependentCode> codes = 16933 Handle<DependentCode> codes =
16897 DependentCode::Insert(handle(cell->dependent_code(), info->isolate()), 16934 DependentCode::Insert(handle(cell->dependent_code(), info->isolate()),
16898 DependentCode::kPropertyCellChangedGroup, 16935 DependentCode::kPropertyCellChangedGroup,
16899 info->object_wrapper()); 16936 info->object_wrapper());
16900 if (*codes != cell->dependent_code()) cell->set_dependent_code(*codes); 16937 if (*codes != cell->dependent_code()) cell->set_dependent_code(*codes);
16901 info->dependencies(DependentCode::kPropertyCellChangedGroup)->Add( 16938 info->dependencies(DependentCode::kPropertyCellChangedGroup)->Add(
16902 cell, info->zone()); 16939 cell, info->zone());
16903 } 16940 }
16904 16941
16905 } } // namespace v8::internal 16942 } } // namespace v8::internal
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698