// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_OBJECTS_MAP_UPDATER_H_ #define V8_OBJECTS_MAP_UPDATER_H_ #include "src/common/globals.h" #include "src/handles/handles.h" #include "src/objects/elements-kind.h" #include "src/objects/field-type.h" #include "src/objects/map.h" #include "src/objects/property-details.h" namespace v8 { namespace internal { // The |MapUpdater| class implements all sorts of map reconfigurations // including changes of elements kind, property attributes, property kind, // property location and field representations/type changes. It ensures that // the reconfigured map and all the intermediate maps are properly integrated // into the existing transition tree. // // To avoid high degrees over polymorphism, and to stabilize quickly, on every // rewrite the new type is deduced by merging the current type with any // potential new (partial) version of the type in the transition tree. // To do this, on each rewrite: // - Search the root of the transition tree using FindRootMap, remember // the integrity level (preventExtensions/seal/freeze) of transitions. // - Find/create a |root_map| with the requested |new_elements_kind|. // - Find |target_map|, the newest matching version of this map using the // "updated" |old_map|'s descriptor array (i.e. whose entry at |modify_index| // is considered to be of |new_kind| and having |new_attributes|) to walk // the transition tree. If there was an integrity level transition on the path // to the old map, use the descriptor array of the map preceding the first // integrity level transition (|integrity_source_map|), and try to replay // the integrity level transition afterwards. // - Merge/generalize the "updated" descriptor array of the |old_map| and // descriptor array of the |target_map|. // - Generalize the |modify_index| descriptor using |new_representation| and // |new_field_type|. // - Walk the tree again starting from the root towards |target_map|. Stop at // |split_map|, the first map whose descriptor array does not match the merged // descriptor array. // - If |target_map| == |split_map|, and there are no integrity level // transitions, |target_map| is in the expected state. Return it. // - Otherwise, invalidate the outdated transition target from |target_map|, and // replace its transition tree with a new branch for the updated descriptors. // - If the |old_map| had integrity level transition, create the new map for it. class V8_EXPORT_PRIVATE MapUpdater { public: MapUpdater(Isolate* isolate, Handle<Map> old_map); // Prepares for reconfiguring of a property at |descriptor| to data field // with given |attributes| and |representation|/|field_type| and // performs the steps 1-6. Handle<Map> ReconfigureToDataField(InternalIndex descriptor, PropertyAttributes attributes, PropertyConstness constness, Representation representation, Handle<FieldType> field_type); // Prepares for reconfiguring elements kind and performs the steps 1-6. Handle<Map> ReconfigureElementsKind(ElementsKind elements_kind); // Prepares for updating deprecated map to most up-to-date non-deprecated // version and performs the steps 1-6. Handle<Map> Update(); // As above but does not mutate maps; instead, we attempt to replay existing // transitions to find an updated map. No lock is taken. static base::Optional<Map> TryUpdateNoLock(Isolate* isolate, Map old_map, ConcurrencyMode cmode) V8_WARN_UNUSED_RESULT; static Handle<Map> ReconfigureExistingProperty(Isolate* isolate, Handle<Map> map, InternalIndex descriptor, PropertyKind kind, PropertyAttributes attributes, PropertyConstness constness); static void GeneralizeField(Isolate* isolate, Handle<Map> map, InternalIndex modify_index, PropertyConstness new_constness, Representation new_representation, Handle<FieldType> new_field_type); // Completes inobject slack tracking for the transition tree starting at the // initial map. static void CompleteInobjectSlackTracking(Isolate* isolate, Map initial_map); private: enum State { kInitialized, kAtRootMap, kAtTargetMap, kAtIntegrityLevelSource, kEnd }; // Updates map to the most up-to-date non-deprecated version. static inline Handle<Map> UpdateMapNoLock(Isolate* isolate, Handle<Map> old_map); // Prepares for updating deprecated map to most up-to-date non-deprecated // version and performs the steps 1-6. // Unlike the Update() entry point it doesn't lock the map_updater_access // mutex. Handle<Map> UpdateImpl(); // Try to reconfigure property in-place without rebuilding transition tree // and creating new maps. See implementation for details. State TryReconfigureToDataFieldInplace(); // Step 1. // - Search the root of the transition tree using FindRootMap. // - Find/create a |root_map_| with requested |new_elements_kind_|. State FindRootMap(); // Step 2. // - Find |target_map|, the newest matching version of this map using the // "updated" |old_map|'s descriptor array (i.e. whose entry at // |modify_index| is considered to be of |new_kind| and having // |new_attributes|) to walk the transition tree. If there was an integrity // level transition on the path to the old map, use the descriptor array // of the map preceding the first integrity level transition // (|integrity_source_map|), and try to replay the integrity level // transition afterwards. State FindTargetMap(); // Step 3. // - Merge/generalize the "updated" descriptor array of the |old_map_| and // descriptor array of the |target_map_|. // - Generalize the |modified_descriptor_| using |new_representation| and // |new_field_type_|. Handle<DescriptorArray> BuildDescriptorArray(); // Step 4. // - Walk the tree again starting from the root towards |target_map|. Stop at // |split_map|, the first map whose descriptor array does not match the // merged descriptor array. Handle<Map> FindSplitMap(Handle<DescriptorArray> descriptors); // Step 5. // - If |target_map| == |split_map|, |target_map| is in the expected state. // Return it. // - Otherwise, invalidate the outdated transition target from |target_map|, // and replace its transition tree with a new branch for the updated // descriptors. State ConstructNewMap(); // Step 6. // - If the |old_map| had integrity level transition, create the new map // for it. State ConstructNewMapWithIntegrityLevelTransition(); // When a requested reconfiguration can not be done the result is a copy // of |old_map_| in dictionary mode. State Normalize(const char* reason); // Returns name of a |descriptor| property. inline Name GetKey(InternalIndex descriptor) const; // Returns property details of a |descriptor| in "updated" |old_descriptors_| // array. inline PropertyDetails GetDetails(InternalIndex descriptor) const; // Returns value of a |descriptor| with kDescriptor location in "updated" // |old_descriptors_| array. inline Object GetValue(InternalIndex descriptor) const; // Returns field type for a |descriptor| with kField location in "updated" // |old_descriptors_| array. inline FieldType GetFieldType(InternalIndex descriptor) const; // If a |descriptor| property in "updated" |old_descriptors_| has kField // location then returns its field type, otherwise computes the optimal field // type for the descriptor's value and |representation|. The |location| // value must be a pre-fetched location for |descriptor|. inline Handle<FieldType> GetOrComputeFieldType( InternalIndex descriptor, PropertyLocation location, Representation representation) const; // If a |descriptor| property in given |descriptors| array has kField // location then returns its field type, otherwise computes the optimal field // type for the descriptor's value and |representation|. // The |location| value must be a pre-fetched location for |descriptor|. inline Handle<FieldType> GetOrComputeFieldType( Handle<DescriptorArray> descriptors, InternalIndex descriptor, PropertyLocation location, Representation representation); // Update field type of the given descriptor to new representation and new // type. The type must be prepared for storing in descriptor array: // it must be either a simple type or a map wrapped in a weak cell. static void UpdateFieldType(Isolate* isolate, Handle<Map> map, InternalIndex descriptor_number, Handle<Name> name, PropertyConstness new_constness, Representation new_representation, const MaybeObjectHandle& new_wrapped_type); void GeneralizeField(Handle<Map> map, InternalIndex modify_index, PropertyConstness new_constness, Representation new_representation, Handle<FieldType> new_field_type); bool TrySaveIntegrityLevelTransitions(); Isolate* isolate_; Handle<Map> old_map_; Handle<DescriptorArray> old_descriptors_; Handle<Map> root_map_; Handle<Map> target_map_; Handle<Map> result_map_; int old_nof_; // Information about integrity level transitions. bool has_integrity_level_transition_ = false; PropertyAttributes integrity_level_ = NONE; Handle<Symbol> integrity_level_symbol_; Handle<Map> integrity_source_map_; State state_ = kInitialized; ElementsKind new_elements_kind_; bool is_transitionable_fast_elements_kind_; // If |modified_descriptor_.is_found()|, then the fields below form // an "update" of the |old_map_|'s descriptors. InternalIndex modified_descriptor_ = InternalIndex::NotFound(); PropertyKind new_kind_ = PropertyKind::kData; PropertyAttributes new_attributes_ = NONE; PropertyConstness new_constness_ = PropertyConstness::kMutable; PropertyLocation new_location_ = PropertyLocation::kField; Representation new_representation_ = Representation::None(); // Data specific to kField location. Handle<FieldType> new_field_type_; // Data specific to kDescriptor location. Handle<Object> new_value_; }; } // namespace internal } // namespace v8 #endif // V8_OBJECTS_MAP_UPDATER_H_