From e61f727705cabac4c2c519cf0aeaf36bb4cdfe61 Mon Sep 17 00:00:00 2001 From: "verwaest@chromium.org" Date: Mon, 18 Aug 2014 14:26:30 +0000 Subject: [PATCH] Rewriting SetOwnPropertyIgnoreAttributes using the LookupIterator BUG= R=jkummerow@chromium.org Review URL: https://codereview.chromium.org/468493002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23163 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/lookup.cc | 41 +++++- src/lookup.h | 33 ++--- src/objects.cc | 458 +++++++++++++++------------------------------------------ src/objects.h | 57 ++----- src/runtime.cc | 10 +- 5 files changed, 185 insertions(+), 414 deletions(-) diff --git a/src/lookup.cc b/src/lookup.cc index 967ce49..4f77364 100644 --- a/src/lookup.cc +++ b/src/lookup.cc @@ -126,6 +126,31 @@ void LookupIterator::PrepareForDataProperty(Handle value) { } +void LookupIterator::ReconfigureDataProperty(Handle value, + PropertyAttributes attributes) { + DCHECK(has_property_); + DCHECK(HolderIsReceiverOrHiddenPrototype()); + Handle holder = GetHolder(); + if (property_encoding_ != DICTIONARY) { + holder_map_ = Map::ReconfigureDataProperty(holder_map_, descriptor_number(), + attributes); + JSObject::MigrateToMap(holder, holder_map_); + } + + // Reload property information and update the descriptor if in dictionary + // mode. + if (holder_map_->is_dictionary_map()) { + property_encoding_ = DICTIONARY; + PropertyDetails details(attributes, NORMAL, 0); + JSObject::SetNormalizedProperty(holder, name(), value, details); + } else { + property_encoding_ = DESCRIPTOR; + } + + CHECK(HasProperty()); +} + + void LookupIterator::TransitionToDataProperty( Handle value, PropertyAttributes attributes, Object::StoreFromKeyed store_mode) { @@ -136,10 +161,6 @@ void LookupIterator::TransitionToDataProperty( // observable. Handle receiver = Handle::cast(GetReceiver()); - // Properties have to be added to context extension objects through - // SetOwnPropertyIgnoreAttributes. - DCHECK(!receiver->IsJSContextExtensionObject()); - if (receiver->IsJSGlobalProxy()) { PrototypeIterator iter(isolate(), receiver); receiver = @@ -162,6 +183,8 @@ void LookupIterator::TransitionToDataProperty( bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); + // Optimization that only works if configuration_ is not mutable. + if (!check_derived()) return true; DisallowHeapAllocation no_gc; Handle receiver = GetReceiver(); if (!receiver->IsJSReceiver()) return false; @@ -182,6 +205,16 @@ bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { } +bool LookupIterator::HolderIsNonGlobalHiddenPrototype() const { + if (!HolderIsReceiverOrHiddenPrototype()) return false; + Handle receiver = GetReceiver(); + Handle holder = GetHolder(); + if (receiver.is_identical_to(holder)) return false; + if (receiver->IsJSGlobalProxy()) return !holder->IsJSGlobalObject(); + return true; +} + + Handle LookupIterator::FetchValue() const { Object* result = NULL; Handle holder = GetHolder(); diff --git a/src/lookup.h b/src/lookup.h index 2d609c5..8705eea 100644 --- a/src/lookup.h +++ b/src/lookup.h @@ -15,15 +15,15 @@ namespace internal { class LookupIterator V8_FINAL BASE_EMBEDDED { public: enum Configuration { - CHECK_OWN_REAL = 0, - CHECK_HIDDEN = 1 << 0, - CHECK_DERIVED = 1 << 1, - CHECK_INTERCEPTOR = 1 << 2, + CHECK_OWN_REAL = 0, + CHECK_HIDDEN = 1 << 0, + CHECK_DERIVED = 1 << 1, + CHECK_INTERCEPTOR = 1 << 2, CHECK_ACCESS_CHECK = 1 << 3, - CHECK_ALL = CHECK_HIDDEN | CHECK_DERIVED | - CHECK_INTERCEPTOR | CHECK_ACCESS_CHECK, - SKIP_INTERCEPTOR = CHECK_ALL ^ CHECK_INTERCEPTOR, - CHECK_OWN = CHECK_ALL ^ CHECK_DERIVED + CHECK_HIDDEN_ACCESS = CHECK_HIDDEN | CHECK_ACCESS_CHECK, + SKIP_INTERCEPTOR = CHECK_HIDDEN_ACCESS | CHECK_DERIVED, + CHECK_ALL = SKIP_INTERCEPTOR | CHECK_INTERCEPTOR, + CHECK_OWN = CHECK_HIDDEN_ACCESS | CHECK_INTERCEPTOR }; enum State { @@ -90,7 +90,7 @@ class LookupIterator V8_FINAL BASE_EMBEDDED { Heap* heap() const { return isolate_->heap(); } Factory* factory() const { return isolate_->factory(); } Handle GetReceiver() const { - return Handle::cast(maybe_receiver_.ToHandleChecked()); + return maybe_receiver_.ToHandleChecked(); } Handle holder_map() const { return holder_map_; } template @@ -100,16 +100,7 @@ class LookupIterator V8_FINAL BASE_EMBEDDED { } Handle GetRoot() const; bool HolderIsReceiverOrHiddenPrototype() const; - - /* Dynamically reduce the trapped types. */ - void skip_interceptor() { - configuration_ = static_cast( - configuration_ & ~CHECK_INTERCEPTOR); - } - void skip_access_check() { - configuration_ = static_cast( - configuration_ & ~CHECK_ACCESS_CHECK); - } + bool HolderIsNonGlobalHiddenPrototype() const; /* ACCESS_CHECK */ bool HasAccess(v8::AccessType access_type) const; @@ -123,6 +114,8 @@ class LookupIterator V8_FINAL BASE_EMBEDDED { void TransitionToDataProperty(Handle value, PropertyAttributes attributes, Object::StoreFromKeyed store_mode); + void ReconfigureDataProperty(Handle value, + PropertyAttributes attributes); PropertyKind property_kind() const { DCHECK(has_property_); return property_kind_; @@ -196,6 +189,8 @@ class LookupIterator V8_FINAL BASE_EMBEDDED { } } + // If configuration_ becomes mutable, update + // HolderIsReceiverOrHiddenPrototype. Configuration configuration_; State state_; bool has_property_; diff --git a/src/objects.cc b/src/objects.cc index be86dad..d55dc66 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -575,8 +575,6 @@ MaybeHandle Object::SetPropertyWithDefinedSetter( static bool FindAllCanReadHolder(LookupIterator* it) { - it->skip_interceptor(); - it->skip_access_check(); for (; it->IsFound(); it->Next()) { if (it->state() == LookupIterator::PROPERTY && it->HasProperty() && @@ -618,8 +616,6 @@ Maybe JSObject::GetPropertyAttributesWithFailedAccessCheck( static bool FindAllCanWriteHolder(LookupIterator* it) { - it->skip_interceptor(); - it->skip_access_check(); for (; it->IsFound(); it->Next()) { if (it->state() == LookupIterator::PROPERTY && it->HasProperty() && it->property_kind() == LookupIterator::ACCESSOR) { @@ -675,21 +671,6 @@ Handle JSObject::GetNormalizedProperty(Handle object, void JSObject::SetNormalizedProperty(Handle object, - const LookupResult* result, - Handle value) { - DCHECK(!object->HasFastProperties()); - NameDictionary* property_dictionary = object->property_dictionary(); - if (object->IsGlobalObject()) { - Handle cell(PropertyCell::cast( - property_dictionary->ValueAt(result->GetDictionaryEntry()))); - PropertyCell::SetValueInferType(cell, value); - } else { - property_dictionary->ValueAtPut(result->GetDictionaryEntry(), *value); - } -} - - -void JSObject::SetNormalizedProperty(Handle object, Handle name, Handle value, PropertyDetails details) { @@ -1823,37 +1804,6 @@ MaybeHandle Map::CopyWithConstant(Handle map, } -void JSObject::AddFastProperty(Handle object, - Handle name, - Handle value, - PropertyAttributes attributes, - StoreFromKeyed store_mode, - TransitionFlag flag) { - DCHECK(!object->IsJSGlobalProxy()); - - MaybeHandle maybe_map; - if (value->IsJSFunction()) { - maybe_map = Map::CopyWithConstant( - handle(object->map()), name, value, attributes, flag); - } else if (!object->map()->TooManyFastProperties(store_mode)) { - Isolate* isolate = object->GetIsolate(); - Representation representation = value->OptimalRepresentation(); - maybe_map = Map::CopyWithField( - handle(object->map(), isolate), name, - value->OptimalType(isolate, representation), - attributes, representation, flag); - } - - Handle new_map; - if (!maybe_map.ToHandle(&new_map)) { - NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0); - return; - } - - JSObject::MigrateToNewProperty(object, new_map, value); -} - - void JSObject::AddSlowProperty(Handle object, Handle name, Handle value, @@ -1886,45 +1836,6 @@ void JSObject::AddSlowProperty(Handle object, } -MaybeHandle JSObject::AddPropertyInternal( - Handle object, Handle name, Handle value, - PropertyAttributes attributes, JSReceiver::StoreFromKeyed store_mode, - ExtensibilityCheck extensibility_check, TransitionFlag transition_flag) { - DCHECK(!object->IsJSGlobalProxy()); - Isolate* isolate = object->GetIsolate(); - - if (!name->IsUniqueName()) { - name = isolate->factory()->InternalizeString( - Handle::cast(name)); - } - - if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK && - !object->map()->is_extensible()) { - Handle args[1] = {name}; - Handle error = isolate->factory()->NewTypeError( - "object_not_extensible", HandleVector(args, ARRAY_SIZE(args))); - return isolate->Throw(error); - } - - if (object->HasFastProperties()) { - AddFastProperty(object, name, value, attributes, store_mode, - transition_flag); - } - - if (!object->HasFastProperties()) { - AddSlowProperty(object, name, value, attributes); - } - - if (object->map()->is_observed() && - *name != isolate->heap()->hidden_string()) { - Handle old_value = isolate->factory()->the_hole_value(); - EnqueueChangeRecord(object, "add", name, old_value); - } - - return value; -} - - Context* JSObject::GetCreationContext() { Object* constructor = this->map()->constructor(); JSFunction* function; @@ -1959,23 +1870,6 @@ void JSObject::EnqueueChangeRecord(Handle object, } -static void ReplaceSlowProperty(Handle object, - Handle name, - Handle value, - PropertyAttributes attributes) { - NameDictionary* dictionary = object->property_dictionary(); - int old_index = dictionary->FindEntry(name); - int new_enumeration_index = 0; // 0 means "Use the next available index." - if (old_index != -1) { - // All calls to ReplaceSlowProperty have had all transitions removed. - new_enumeration_index = dictionary->DetailsAt(old_index).dictionary_index(); - } - - PropertyDetails new_details(attributes, NORMAL, new_enumeration_index); - JSObject::SetNormalizedProperty(object, name, value, new_details); -} - - const char* Representation::Mnemonic() const { switch (kind_) { case kNone: return "v"; @@ -3029,7 +2923,8 @@ MaybeHandle Object::SetProperty(LookupIterator* it, if (done) break; } - return AddDataProperty(it, value, NONE, strict_mode, store_mode); + return AddDataProperty(it, value, NONE, strict_mode, store_mode, + PERFORM_EXTENSIBILITY_CHECK); } @@ -3084,7 +2979,8 @@ MaybeHandle Object::AddDataProperty(LookupIterator* it, Handle value, PropertyAttributes attributes, StrictMode strict_mode, - StoreFromKeyed store_mode) { + StoreFromKeyed store_mode, + ExtensibilityCheck check) { DCHECK(!it->GetReceiver()->IsJSProxy()); if (!it->GetReceiver()->IsJSObject()) { // TODO(verwaest): Throw a TypeError with a more specific message. @@ -3102,7 +2998,8 @@ MaybeHandle Object::AddDataProperty(LookupIterator* it, Handle::cast(PrototypeIterator::GetCurrent(iter)); } - if (!receiver->map()->is_extensible()) { + if (check == PERFORM_EXTENSIBILITY_CHECK && + !receiver->map()->is_extensible()) { if (strict_mode == SLOPPY) return value; Handle args[1] = {it->name()}; @@ -3943,45 +3840,6 @@ bool JSObject::TryMigrateInstance(Handle object) { } -MaybeHandle JSObject::SetPropertyUsingTransition( - Handle object, - LookupResult* lookup, - Handle name, - Handle value, - PropertyAttributes attributes) { - Handle transition_map(lookup->GetTransitionTarget()); - int descriptor = transition_map->LastAdded(); - - Handle descriptors(transition_map->instance_descriptors()); - PropertyDetails details = descriptors->GetDetails(descriptor); - - if (details.type() == CALLBACKS || attributes != details.attributes()) { - // AddPropertyInternal will either normalize the object, or create a new - // fast copy of the map. If we get a fast copy of the map, all field - // representations will be tagged since the transition is omitted. - return JSObject::AddPropertyInternal( - object, name, value, attributes, - JSReceiver::CERTAINLY_NOT_STORE_FROM_KEYED, - JSReceiver::OMIT_EXTENSIBILITY_CHECK, OMIT_TRANSITION); - } - - // Keep the target CONSTANT if the same value is stored. - // TODO(verwaest): Also support keeping the placeholder - // (value->IsUninitialized) as constant. - if (!lookup->CanHoldValue(value)) { - Representation field_representation = value->OptimalRepresentation(); - Handle field_type = value->OptimalType( - lookup->isolate(), field_representation); - transition_map = Map::GeneralizeRepresentation( - transition_map, descriptor, - field_representation, field_type, FORCE_FIELD); - } - - JSObject::MigrateToNewProperty(object, transition_map, value); - return value; -} - - void JSObject::MigrateToNewProperty(Handle object, Handle map, Handle value) { @@ -4012,65 +3870,6 @@ void JSObject::WriteToField(int descriptor, Object* value) { } -void JSObject::SetPropertyToField(LookupResult* lookup, Handle value) { - if (lookup->type() == CONSTANT || !lookup->CanHoldValue(value)) { - Representation field_representation = value->OptimalRepresentation(); - Handle field_type = value->OptimalType( - lookup->isolate(), field_representation); - JSObject::GeneralizeFieldRepresentation(handle(lookup->holder()), - lookup->GetDescriptorIndex(), - field_representation, field_type); - } - lookup->holder()->WriteToField(lookup->GetDescriptorIndex(), *value); -} - - -void JSObject::ConvertAndSetOwnProperty(LookupResult* lookup, - Handle name, - Handle value, - PropertyAttributes attributes) { - Handle object(lookup->holder()); - if (object->map()->TooManyFastProperties(Object::MAY_BE_STORE_FROM_KEYED)) { - JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0); - } else if (object->map()->is_prototype_map()) { - JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0); - } - - if (!object->HasFastProperties()) { - ReplaceSlowProperty(object, name, value, attributes); - ReoptimizeIfPrototype(object); - return; - } - - int descriptor_index = lookup->GetDescriptorIndex(); - if (lookup->GetAttributes() == attributes) { - JSObject::GeneralizeFieldRepresentation(object, descriptor_index, - Representation::Tagged(), - HeapType::Any(lookup->isolate())); - } else { - Handle old_map(object->map()); - Handle new_map = Map::CopyGeneralizeAllRepresentations(old_map, - descriptor_index, FORCE_FIELD, attributes, "attributes mismatch"); - JSObject::MigrateToMap(object, new_map); - } - - object->WriteToField(descriptor_index, *value); -} - - -void JSObject::SetPropertyToFieldWithAttributes(LookupResult* lookup, - Handle name, - Handle value, - PropertyAttributes attributes) { - if (lookup->GetAttributes() == attributes) { - if (value->IsUninitialized()) return; - SetPropertyToField(lookup, value); - } else { - ConvertAndSetOwnProperty(lookup, name, value, attributes); - } -} - - void JSObject::AddProperty(Handle object, Handle name, Handle value, PropertyAttributes attributes) { @@ -4100,156 +3899,123 @@ MaybeHandle JSObject::SetOwnPropertyIgnoreAttributes( StoreFromKeyed store_from_keyed, ExecutableAccessorInfoHandling handling) { DCHECK(!value->IsTheHole()); - Isolate* isolate = object->GetIsolate(); - - // Make sure that the top context does not change when doing callbacks or - // interceptor calls. - AssertNoContextChange ncc(isolate); + LookupIterator it(object, name, LookupIterator::CHECK_HIDDEN_ACCESS); + bool is_observed = object->map()->is_observed() && + *name != it.isolate()->heap()->hidden_string(); + for (; it.IsFound(); it.Next()) { + switch (it.state()) { + case LookupIterator::NOT_FOUND: + case LookupIterator::JSPROXY: + case LookupIterator::INTERCEPTOR: + UNREACHABLE(); - LookupResult lookup(isolate); - object->LookupOwn(name, &lookup, true); - if (!lookup.IsFound()) { - object->map()->LookupTransition(*object, *name, &lookup); - } + case LookupIterator::ACCESS_CHECK: + if (!it.isolate()->MayNamedAccess(object, name, v8::ACCESS_SET)) { + return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY); + } + break; - // Check access rights if needed. - if (object->IsAccessCheckNeeded()) { - if (!isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) { - LookupIterator it(object, name, LookupIterator::CHECK_OWN); - return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY); - } - } + case LookupIterator::PROPERTY: { + if (!it.HasProperty()) break; + if (it.HolderIsNonGlobalHiddenPrototype()) break; + PropertyDetails details = it.property_details(); + Handle old_value = it.isolate()->factory()->the_hole_value(); + switch (it.property_kind()) { + case LookupIterator::ACCESSOR: { + // Ensure the context isn't changed after calling into accessors. + AssertNoContextChange ncc(it.isolate()); - if (object->IsJSGlobalProxy()) { - PrototypeIterator iter(isolate, object); - if (iter.IsAtEnd()) return value; - DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); - return SetOwnPropertyIgnoreAttributes( - Handle::cast(PrototypeIterator::GetCurrent(iter)), name, - value, attributes, extensibility_check); - } + Handle accessors = it.GetAccessors(); - if (lookup.IsInterceptor() || - (lookup.IsDescriptorOrDictionary() && lookup.type() == CALLBACKS)) { - object->LookupOwnRealNamedProperty(name, &lookup); - } + if (is_observed && accessors->IsAccessorInfo()) { + ASSIGN_RETURN_ON_EXCEPTION( + it.isolate(), old_value, + GetPropertyWithAccessor(it.GetReceiver(), it.name(), + it.GetHolder(), accessors), + Object); + } - // Check for accessor in prototype chain removed here in clone. - if (!lookup.IsFound()) { - object->map()->LookupTransition(*object, *name, &lookup); - TransitionFlag flag = lookup.IsFound() - ? OMIT_TRANSITION : INSERT_TRANSITION; - // Neither properties nor transitions found. - return AddPropertyInternal(object, name, value, attributes, - store_from_keyed, extensibility_check, flag); - } + // Special handling for ExecutableAccessorInfo, which behaves like a + // data property. + if (handling == DONT_FORCE_FIELD && + accessors->IsExecutableAccessorInfo()) { + Handle result; + ASSIGN_RETURN_ON_EXCEPTION( + it.isolate(), result, + JSObject::SetPropertyWithAccessor( + it.GetReceiver(), it.name(), value, + it.GetHolder(), accessors, STRICT), + Object); + DCHECK(result->SameValue(*value)); + + if (details.attributes() == attributes) { + // Regular property update if the attributes match. + if (is_observed && !old_value->SameValue(*value)) { + // If we are setting the prototype of a function and are + // observed, don't send change records because the prototype + // handles that itself. + if (!object->IsJSFunction() || + !Name::Equals(it.isolate()->factory()->prototype_string(), + name) || + !Handle::cast(object) + ->should_have_prototype()) { + EnqueueChangeRecord(object, "update", name, old_value); + } + } + return value; + } - Handle old_value = isolate->factory()->the_hole_value(); - PropertyAttributes old_attributes = ABSENT; - bool is_observed = object->map()->is_observed() && - *name != isolate->heap()->hidden_string(); - if (is_observed && lookup.IsProperty()) { - if (lookup.IsDataProperty()) { - old_value = Object::GetPropertyOrElement(object, name).ToHandleChecked(); - } - old_attributes = lookup.GetAttributes(); - } + // Reconfigure the accessor if attributes mismatch. + Handle new_data = + Accessors::CloneAccessor( + it.isolate(), + Handle::cast(accessors)); + new_data->set_property_attributes(attributes); + // By clearing the setter we don't have to introduce a lookup to + // the setter, simply make it unavailable to reflect the + // attributes. + if (attributes & READ_ONLY) new_data->clear_setter(); + SetPropertyCallback(object, name, new_data, attributes); + if (is_observed) { + if (old_value->SameValue(*value)) { + old_value = it.isolate()->factory()->the_hole_value(); + } + EnqueueChangeRecord(object, "reconfigure", name, old_value); + } + return value; + } - bool executed_set_prototype = false; + // Regular accessor. Reconfigure to data property. + break; + } - // Check of IsReadOnly removed from here in clone. - if (lookup.IsTransition()) { - Handle result; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, result, - SetPropertyUsingTransition( - handle(lookup.holder()), &lookup, name, value, attributes), - Object); - } else { - switch (lookup.type()) { - case NORMAL: - ReplaceSlowProperty(object, name, value, attributes); - break; - case FIELD: - SetPropertyToFieldWithAttributes(&lookup, name, value, attributes); - break; - case CONSTANT: - // Only replace the constant if necessary. - if (lookup.GetAttributes() != attributes || - *value != lookup.GetConstant()) { - SetPropertyToFieldWithAttributes(&lookup, name, value, attributes); - } - break; - case CALLBACKS: - { - Handle callback(lookup.GetCallbackObject(), isolate); - if (callback->IsExecutableAccessorInfo() && - handling == DONT_FORCE_FIELD) { - Handle result; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, result, JSObject::SetPropertyWithAccessor( - object, name, value, handle(lookup.holder()), - callback, STRICT), - Object); - - if (attributes != lookup.GetAttributes()) { - Handle new_data = - Accessors::CloneAccessor( - isolate, Handle::cast(callback)); - new_data->set_property_attributes(attributes); - if (attributes & READ_ONLY) { - // This way we don't have to introduce a lookup to the setter, - // simply make it unavailable to reflect the attributes. - new_data->clear_setter(); + case LookupIterator::DATA: + // Regular property update if the attributes match. + if (details.attributes() == attributes) { + return SetDataProperty(&it, value); } + // Reconfigure the data property if the attributes mismatch. + if (is_observed) old_value = it.GetDataValue(); + } - SetPropertyCallback(object, name, new_data, attributes); - } - if (is_observed) { - // If we are setting the prototype of a function and are observed, - // don't send change records because the prototype handles that - // itself. - executed_set_prototype = object->IsJSFunction() && - String::Equals(isolate->factory()->prototype_string(), - Handle::cast(name)) && - Handle::cast(object)->should_have_prototype(); + it.ReconfigureDataProperty(value, attributes); + it.PrepareForDataProperty(value); + it.WriteDataValue(value); + + if (is_observed) { + if (old_value->SameValue(*value)) { + old_value = it.isolate()->factory()->the_hole_value(); } - } else { - ConvertAndSetOwnProperty(&lookup, name, value, attributes); + EnqueueChangeRecord(object, "reconfigure", name, old_value); } - break; - } - case NONEXISTENT: - case HANDLER: - case INTERCEPTOR: - UNREACHABLE(); - } - } - if (is_observed && !executed_set_prototype) { - if (lookup.IsTransition()) { - EnqueueChangeRecord(object, "add", name, old_value); - } else if (old_value->IsTheHole()) { - EnqueueChangeRecord(object, "reconfigure", name, old_value); - } else { - LookupResult new_lookup(isolate); - object->LookupOwn(name, &new_lookup, true); - bool value_changed = false; - if (new_lookup.IsDataProperty()) { - Handle new_value = - Object::GetPropertyOrElement(object, name).ToHandleChecked(); - value_changed = !old_value->SameValue(*new_value); - } - if (new_lookup.GetAttributes() != old_attributes) { - if (!value_changed) old_value = isolate->factory()->the_hole_value(); - EnqueueChangeRecord(object, "reconfigure", name, old_value); - } else if (value_changed) { - EnqueueChangeRecord(object, "update", name, old_value); + return value; } } } - return value; + return AddDataProperty(&it, value, attributes, STRICT, store_from_keyed, + extensibility_check); } @@ -7411,6 +7177,18 @@ Handle Map::TransitionToDataProperty(Handle map, Handle name, } +Handle Map::ReconfigureDataProperty(Handle map, int descriptor, + PropertyAttributes attributes) { + // Dictionaries have to be reconfigured in-place. + DCHECK(!map->is_dictionary_map()); + + // For now, give up on transitioning and just create a unique map. + // TODO(verwaest/ishell): Cache transitions with different attributes. + return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_FIELD, + attributes, "attributes mismatch"); +} + + Handle Map::CopyAddDescriptor(Handle map, Descriptor* descriptor, TransitionFlag flag) { diff --git a/src/objects.h b/src/objects.h index b214a37..b4d19e1 100644 --- a/src/objects.h +++ b/src/objects.h @@ -247,6 +247,14 @@ enum PropertyNormalizationMode { }; +// Internal properties (e.g. the hidden properties dictionary) might +// be added even though the receiver is non-extensible. +enum ExtensibilityCheck { + PERFORM_EXTENSIBILITY_CHECK, + OMIT_EXTENSIBILITY_CHECK +}; + + // Indicates how aggressively the prototype should be optimized. FAST_PROTOTYPE // will give the fastest result by tailoring the map to the prototype, but that // will cause polymorphism with other objects. REGULAR_PROTOTYPE is to be used @@ -1492,7 +1500,8 @@ class Object { LookupIterator* it, Handle value); MUST_USE_RESULT static MaybeHandle AddDataProperty( LookupIterator* it, Handle value, PropertyAttributes attributes, - StrictMode strict_mode, StoreFromKeyed store_mode); + StrictMode strict_mode, StoreFromKeyed store_mode, + ExtensibilityCheck check); MUST_USE_RESULT static inline MaybeHandle GetPropertyOrElement( Handle object, Handle key); @@ -1936,13 +1945,6 @@ class JSReceiver: public HeapObject { FORCE_DELETION }; - // Internal properties (e.g. the hidden properties dictionary) might - // be added even though the receiver is non-extensible. - enum ExtensibilityCheck { - PERFORM_EXTENSIBILITY_CHECK, - OMIT_EXTENSIBILITY_CHECK - }; - DECLARE_CAST(JSReceiver) MUST_USE_RESULT static MaybeHandle SetElement( @@ -2172,12 +2174,6 @@ class JSObject: public JSReceiver { static Handle GetNormalizedProperty(Handle object, const LookupResult* result); - // Sets the property value in a normalized object given a lookup result. - // Handles the special representation of JS global objects. - static void SetNormalizedProperty(Handle object, - const LookupResult* result, - Handle value); - // Sets the property value in a normalized object given (key, value, details). // Handles the special representation of JS global objects. static void SetNormalizedProperty(Handle object, @@ -2621,17 +2617,6 @@ class JSObject: public JSReceiver { Handle new_map, int expected_additional_properties); - static void SetPropertyToField(LookupResult* lookup, Handle value); - - static void ConvertAndSetOwnProperty(LookupResult* lookup, - Handle name, - Handle value, - PropertyAttributes attributes); - - static void SetPropertyToFieldWithAttributes(LookupResult* lookup, - Handle name, - Handle value, - PropertyAttributes attributes); static void GeneralizeFieldRepresentation(Handle object, int modify_index, Representation new_representation, @@ -2705,29 +2690,9 @@ class JSObject: public JSReceiver { StrictMode strict_mode, bool check_prototype = true); - MUST_USE_RESULT static MaybeHandle SetPropertyUsingTransition( - Handle object, - LookupResult* lookup, - Handle name, - Handle value, - PropertyAttributes attributes); MUST_USE_RESULT static MaybeHandle SetPropertyWithFailedAccessCheck( LookupIterator* it, Handle value, StrictMode strict_mode); - // Add a property to an object. - MUST_USE_RESULT static MaybeHandle AddPropertyInternal( - Handle object, Handle name, Handle value, - PropertyAttributes attributes, StoreFromKeyed store_mode, - ExtensibilityCheck extensibility_check, TransitionFlag flag); - - // Add a property to a fast-case object. - static void AddFastProperty(Handle object, - Handle name, - Handle value, - PropertyAttributes attributes, - StoreFromKeyed store_mode, - TransitionFlag flag); - // Add a property to a slow-case object. static void AddSlowProperty(Handle object, Handle name, @@ -6517,6 +6482,8 @@ class Map: public HeapObject { Handle value, PropertyAttributes attributes, StoreFromKeyed store_mode); + static Handle ReconfigureDataProperty(Handle map, int descriptor, + PropertyAttributes attributes); inline void AppendDescriptor(Descriptor* desc); diff --git a/src/runtime.cc b/src/runtime.cc index 7be53a7..2b9c20c 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -5041,10 +5041,8 @@ RUNTIME_FUNCTION(Runtime_DefineDataPropertyUnchecked) { ASSIGN_RETURN_FAILURE_ON_EXCEPTION( isolate, result, JSObject::SetOwnPropertyIgnoreAttributes( - js_object, name, obj_value, attr, - JSReceiver::PERFORM_EXTENSIBILITY_CHECK, - JSReceiver::MAY_BE_STORE_FROM_KEYED, - JSObject::DONT_FORCE_FIELD)); + js_object, name, obj_value, attr, PERFORM_EXTENSIBILITY_CHECK, + JSReceiver::MAY_BE_STORE_FROM_KEYED, JSObject::DONT_FORCE_FIELD)); return *result; } @@ -5198,7 +5196,7 @@ MaybeHandle Runtime::DefineObjectProperty( } else { if (name->IsString()) name = String::Flatten(Handle::cast(name)); return JSObject::SetOwnPropertyIgnoreAttributes( - js_object, name, value, attr, JSReceiver::PERFORM_EXTENSIBILITY_CHECK, + js_object, name, value, attr, PERFORM_EXTENSIBILITY_CHECK, store_from_keyed); } } @@ -5214,7 +5212,7 @@ MaybeHandle Runtime::DefineObjectProperty( SLOPPY, false, DEFINE_PROPERTY); } else { return JSObject::SetOwnPropertyIgnoreAttributes( - js_object, name, value, attr, JSReceiver::PERFORM_EXTENSIBILITY_CHECK, + js_object, name, value, attr, PERFORM_EXTENSIBILITY_CHECK, store_from_keyed); } } -- 2.7.4