• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_OBJECTS_KEYS_H_
6 #define V8_OBJECTS_KEYS_H_
7 
8 #include "src/objects/hash-table.h"
9 #include "src/objects/js-objects.h"
10 #include "src/objects/objects.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 class JSProxy;
16 class FastKeyAccumulator;
17 
18 enum AddKeyConversion { DO_NOT_CONVERT, CONVERT_TO_ARRAY_INDEX };
19 
20 // This is a helper class for JSReceiver::GetKeys which collects and sorts keys.
21 // GetKeys needs to sort keys per prototype level, first showing the integer
22 // indices from elements then the strings from the properties. However, this
23 // does not apply to proxies which are in full control of how the keys are
24 // sorted.
25 //
26 // For performance reasons the KeyAccumulator internally separates integer keys
27 // in |elements_| into sorted lists per prototype level. String keys are
28 // collected in |string_properties_|, a single OrderedHashSet (similar for
29 // Symbols in |symbol_properties_|. To separate the keys per level later when
30 // assembling the final list, |levelLengths_| keeps track of the number of
31 // String and Symbol keys per level.
32 //
33 // Only unique keys are kept by the KeyAccumulator, strings are stored in a
34 // HashSet for inexpensive lookups. Integer keys are kept in sorted lists which
35 // are more compact and allow for reasonably fast includes check.
36 class KeyAccumulator final {
37  public:
KeyAccumulator(Isolate * isolate,KeyCollectionMode mode,PropertyFilter filter)38   KeyAccumulator(Isolate* isolate, KeyCollectionMode mode,
39                  PropertyFilter filter)
40       : isolate_(isolate), mode_(mode), filter_(filter) {}
41   ~KeyAccumulator() = default;
42   KeyAccumulator(const KeyAccumulator&) = delete;
43   KeyAccumulator& operator=(const KeyAccumulator&) = delete;
44 
45   static MaybeHandle<FixedArray> GetKeys(
46       Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
47       GetKeysConversion keys_conversion = GetKeysConversion::kKeepNumbers,
48       bool is_for_in = false, bool skip_indices = false);
49 
50   Handle<FixedArray> GetKeys(
51       GetKeysConversion convert = GetKeysConversion::kKeepNumbers);
52   Maybe<bool> CollectKeys(Handle<JSReceiver> receiver,
53                           Handle<JSReceiver> object);
54 
55   // Might return directly the object's enum_cache, copy the result before using
56   // as an elements backing store for a JSObject.
57   // Does not throw for uninitialized exports in module namespace objects, so
58   // this has to be checked separately.
59   static Handle<FixedArray> GetOwnEnumPropertyKeys(Isolate* isolate,
60                                                    Handle<JSObject> object);
61 
62   V8_WARN_UNUSED_RESULT ExceptionStatus
63   AddKey(Object key, AddKeyConversion convert = DO_NOT_CONVERT);
64   V8_WARN_UNUSED_RESULT ExceptionStatus
65   AddKey(Handle<Object> key, AddKeyConversion convert = DO_NOT_CONVERT);
66 
67   // Jump to the next level, pushing the current |levelLength_| to
68   // |levelLengths_| and adding a new list to |elements_|.
isolate()69   Isolate* isolate() { return isolate_; }
70   // Filter keys based on their property descriptors.
filter()71   PropertyFilter filter() { return filter_; }
72   // The collection mode defines whether we collect the keys from the prototype
73   // chain or only look at the receiver.
mode()74   KeyCollectionMode mode() { return mode_; }
set_skip_indices(bool value)75   void set_skip_indices(bool value) { skip_indices_ = value; }
76   // Shadowing keys are used to filter keys. This happens when non-enumerable
77   // keys appear again on the prototype chain.
78   void AddShadowingKey(Object key, AllowHeapAllocation* allow_gc);
79   void AddShadowingKey(Handle<Object> key);
80 
81  private:
82   enum IndexedOrNamed { kIndexed, kNamed };
83 
84   V8_WARN_UNUSED_RESULT ExceptionStatus
85   CollectPrivateNames(Handle<JSReceiver> receiver, Handle<JSObject> object);
86   Maybe<bool> CollectAccessCheckInterceptorKeys(
87       Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
88       Handle<JSObject> object);
89 
90   Maybe<bool> CollectInterceptorKeysInternal(
91       Handle<JSReceiver> receiver, Handle<JSObject> object,
92       Handle<InterceptorInfo> interceptor, IndexedOrNamed type);
93   Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver,
94                                      Handle<JSObject> object,
95                                      IndexedOrNamed type);
96 
97   Maybe<bool> CollectOwnElementIndices(Handle<JSReceiver> receiver,
98                                        Handle<JSObject> object);
99   Maybe<bool> CollectOwnPropertyNames(Handle<JSReceiver> receiver,
100                                       Handle<JSObject> object);
101   Maybe<bool> CollectOwnKeys(Handle<JSReceiver> receiver,
102                              Handle<JSObject> object);
103   Maybe<bool> CollectOwnJSProxyKeys(Handle<JSReceiver> receiver,
104                                     Handle<JSProxy> proxy);
105   Maybe<bool> CollectOwnJSProxyTargetKeys(Handle<JSProxy> proxy,
106                                           Handle<JSReceiver> target);
107 
108   V8_WARN_UNUSED_RESULT ExceptionStatus FilterForEnumerableProperties(
109       Handle<JSReceiver> receiver, Handle<JSObject> object,
110       Handle<InterceptorInfo> interceptor, Handle<JSObject> result,
111       IndexedOrNamed type);
112 
113   Maybe<bool> AddKeysFromJSProxy(Handle<JSProxy> proxy,
114                                  Handle<FixedArray> keys);
115   V8_WARN_UNUSED_RESULT ExceptionStatus AddKeys(Handle<FixedArray> array,
116                                                 AddKeyConversion convert);
117   V8_WARN_UNUSED_RESULT ExceptionStatus AddKeys(Handle<JSObject> array_like,
118                                                 AddKeyConversion convert);
119 
120   bool IsShadowed(Handle<Object> key);
121   bool HasShadowingKeys();
122   Handle<OrderedHashSet> keys();
123 
124   // In case of for-in loops we have to treat JSProxy keys differently and
125   // deduplicate them. Additionally we convert JSProxy keys back to array
126   // indices.
set_is_for_in(bool value)127   void set_is_for_in(bool value) { is_for_in_ = value; }
set_first_prototype_map(Handle<Map> value)128   void set_first_prototype_map(Handle<Map> value) {
129     first_prototype_map_ = value;
130   }
set_try_prototype_info_cache(bool value)131   void set_try_prototype_info_cache(bool value) {
132     try_prototype_info_cache_ = value;
133   }
set_receiver(Handle<JSReceiver> object)134   void set_receiver(Handle<JSReceiver> object) { receiver_ = object; }
135   // The last_non_empty_prototype is used to limit the prototypes for which
136   // we have to keep track of non-enumerable keys that can shadow keys
137   // repeated on the prototype chain.
set_last_non_empty_prototype(Handle<JSReceiver> object)138   void set_last_non_empty_prototype(Handle<JSReceiver> object) {
139     last_non_empty_prototype_ = object;
140   }
set_may_have_elements(bool value)141   void set_may_have_elements(bool value) { may_have_elements_ = value; }
142 
143   Isolate* isolate_;
144   // keys_ is either an Handle<OrderedHashSet> or in the case of own JSProxy
145   // keys a Handle<FixedArray>. The OrderedHashSet is in-place converted to the
146   // result list, a FixedArray containing all collected keys.
147   Handle<FixedArray> keys_;
148   Handle<Map> first_prototype_map_;
149   Handle<JSReceiver> receiver_;
150   Handle<JSReceiver> last_non_empty_prototype_;
151   Handle<ObjectHashSet> shadowing_keys_;
152   KeyCollectionMode mode_;
153   PropertyFilter filter_;
154   bool is_for_in_ = false;
155   bool skip_indices_ = false;
156   // For all the keys on the first receiver adding a shadowing key we can skip
157   // the shadow check.
158   bool skip_shadow_check_ = true;
159   bool may_have_elements_ = true;
160   bool try_prototype_info_cache_ = false;
161 
162   friend FastKeyAccumulator;
163 };
164 
165 // The FastKeyAccumulator handles the cases where there are no elements on the
166 // prototype chain and forwords the complex/slow cases to the normal
167 // KeyAccumulator. This significantly speeds up the cases where the OWN_ONLY
168 // case where we do not have to walk the prototype chain.
169 class FastKeyAccumulator {
170  public:
171   FastKeyAccumulator(Isolate* isolate, Handle<JSReceiver> receiver,
172                      KeyCollectionMode mode, PropertyFilter filter,
173                      bool is_for_in = false, bool skip_indices = false)
isolate_(isolate)174       : isolate_(isolate),
175         receiver_(receiver),
176         mode_(mode),
177         filter_(filter),
178         is_for_in_(is_for_in),
179         skip_indices_(skip_indices) {
180     Prepare();
181   }
182   FastKeyAccumulator(const FastKeyAccumulator&) = delete;
183   FastKeyAccumulator& operator=(const FastKeyAccumulator&) = delete;
184 
is_receiver_simple_enum()185   bool is_receiver_simple_enum() { return is_receiver_simple_enum_; }
has_empty_prototype()186   bool has_empty_prototype() { return has_empty_prototype_; }
may_have_elements()187   bool may_have_elements() { return may_have_elements_; }
188 
189   MaybeHandle<FixedArray> GetKeys(
190       GetKeysConversion convert = GetKeysConversion::kKeepNumbers);
191 
192  private:
193   void Prepare();
194   MaybeHandle<FixedArray> GetKeysFast(GetKeysConversion convert);
195   MaybeHandle<FixedArray> GetKeysSlow(GetKeysConversion convert);
196   MaybeHandle<FixedArray> GetKeysWithPrototypeInfoCache(
197       GetKeysConversion convert);
198 
199   MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache();
200 
201   bool MayHaveElements(JSReceiver receiver);
202   bool TryPrototypeInfoCache(Handle<JSReceiver> receiver);
203 
204   Isolate* isolate_;
205   Handle<JSReceiver> receiver_;
206   Handle<Map> first_prototype_map_;
207   Handle<JSReceiver> first_prototype_;
208   Handle<JSReceiver> last_non_empty_prototype_;
209   KeyCollectionMode mode_;
210   PropertyFilter filter_;
211   bool is_for_in_ = false;
212   bool skip_indices_ = false;
213   bool is_receiver_simple_enum_ = false;
214   bool has_empty_prototype_ = false;
215   bool may_have_elements_ = true;
216   bool has_prototype_info_cache_ = false;
217   bool try_prototype_info_cache_ = false;
218   bool only_own_has_simple_elements_ = false;
219 };
220 
221 }  // namespace internal
222 }  // namespace v8
223 
224 #endif  // V8_OBJECTS_KEYS_H_
225