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