lookup.h 14.4 KB
Newer Older
1 2 3 4
// Copyright 2014 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.

5 6
#ifndef V8_OBJECTS_LOOKUP_H_
#define V8_OBJECTS_LOOKUP_H_
7

8
#include "src/common/globals.h"
9
#include "src/execution/isolate.h"
10
#include "src/heap/factory.h"
11
#include "src/objects/descriptor-array.h"
12
#include "src/objects/js-objects.h"
13
#include "src/objects/map.h"
14
#include "src/objects/objects.h"
15

16 17 18 19
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/value-type.h"
#endif  // V8_ENABLE_WEBASSEMBLY

20 21 22
namespace v8 {
namespace internal {

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
class PropertyKey {
 public:
  inline PropertyKey(Isolate* isolate, double index);
  // {name} might be a string representation of an element index.
  inline PropertyKey(Isolate* isolate, Handle<Name> name);
  // {valid_key} is a Name or Number.
  inline PropertyKey(Isolate* isolate, Handle<Object> valid_key);
  // {key} could be anything.
  PropertyKey(Isolate* isolate, Handle<Object> key, bool* success);

  inline bool is_element() const;
  Handle<Name> name() const { return name_; }
  size_t index() const { return index_; }
  inline Handle<Name> GetName(Isolate* isolate);

 private:
  Handle<Name> name_;
  size_t index_;
};

43
class V8_EXPORT_PRIVATE LookupIterator final {
44
 public:
45
  enum Configuration {
46
    // Configuration bits.
47 48
    kInterceptor = 1 << 0,
    kPrototypeChain = 1 << 1,
49

50
    // Convenience combinations of bits.
51 52
    OWN_SKIP_INTERCEPTOR = 0,
    OWN = kInterceptor,
53 54
    PROTOTYPE_CHAIN_SKIP_INTERCEPTOR = kPrototypeChain,
    PROTOTYPE_CHAIN = kPrototypeChain | kInterceptor,
55
    DEFAULT = PROTOTYPE_CHAIN
56 57 58
  };

  enum State {
59
    ACCESS_CHECK,
60
    INTEGER_INDEXED_EXOTIC,
61 62
    INTERCEPTOR,
    JSPROXY,
63
    NOT_FOUND,
64 65
    ACCESSOR,
    DATA,
66
    TRANSITION,
67 68 69
    // Set state_ to BEFORE_PROPERTY to ensure that the next lookup will be a
    // PROPERTY lookup.
    BEFORE_PROPERTY = INTERCEPTOR
70 71
  };

72
  // {name} is guaranteed to be a property name (and not e.g. "123").
73 74 75
  inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
                        Handle<Name> name,
                        Configuration configuration = DEFAULT);
76
  inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
77
                        Handle<Name> name, Handle<Object> lookup_start_object,
78
                        Configuration configuration = DEFAULT);
79

80
  inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index,
81
                        Configuration configuration = DEFAULT);
82
  inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index,
83
                        Handle<Object> lookup_start_object,
84
                        Configuration configuration = DEFAULT);
85

86
  inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
87 88
                        const PropertyKey& key,
                        Configuration configuration = DEFAULT);
89
  inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
90 91
                        const PropertyKey& key,
                        Handle<Object> lookup_start_object,
92
                        Configuration configuration = DEFAULT);
93

94 95 96 97
  void Restart() {
    InterceptorState state = InterceptorState::kUninitialized;
    IsElement() ? RestartInternal<true>(state) : RestartInternal<false>(state);
  }
98

99 100
  Isolate* isolate() const { return isolate_; }
  State state() const { return state_; }
101

102
  inline Handle<Name> name() const;
103
  inline Handle<Name> GetName();
104 105 106 107 108
  size_t index() const { return index_; }
  uint32_t array_index() const {
    DCHECK_LE(index_, JSArray::kMaxArrayIndex);
    return static_cast<uint32_t>(index_);
  }
109

110 111 112 113 114 115 116
  // Returns true if this LookupIterator has an index in the range
  // [0, size_t::max).
  bool IsElement() const { return index_ != kInvalidIndex; }
  // Returns true if this LookupIterator has an index that counts as an
  // element for the given object (up to kMaxArrayIndex for JSArrays,
  // any integer for JSTypedArrays).
  inline bool IsElement(JSReceiver object) const;
117

118 119
  inline bool IsPrivateName() const;

120 121
  bool IsFound() const { return state_ != NOT_FOUND; }
  void Next();
122 123 124 125
  void NotFound() {
    has_property_ = false;
    state_ = NOT_FOUND;
  }
126

127
  Heap* heap() const { return isolate_->heap(); }
128
  Factory* factory() const { return isolate_->factory(); }
129
  Handle<Object> GetReceiver() const { return receiver_; }
130

131
  template <class T>
132
  inline Handle<T> GetStoreTarget() const;
133
  inline bool is_dictionary_holder() const;
134 135
  inline Handle<Map> transition_map() const;
  inline Handle<PropertyCell> transition_cell() const;
136
  template <class T>
137
  inline Handle<T> GetHolder() const;
138

139 140
  Handle<Object> lookup_start_object() const { return lookup_start_object_; }

141
  bool HolderIsReceiver() const;
142
  bool HolderIsReceiverOrHiddenPrototype() const;
143

neis's avatar
neis committed
144 145 146 147
  bool check_prototype_chain() const {
    return (configuration_ & kPrototypeChain) != 0;
  }

148
  /* ACCESS_CHECK */
149
  bool HasAccess() const;
150 151

  /* PROPERTY */
152
  inline bool ExtendingNonExtensible(Handle<JSReceiver> receiver);
153
  void PrepareForDataProperty(Handle<Object> value);
154
  void PrepareTransitionToDataProperty(Handle<JSReceiver> receiver,
155
                                       Handle<Object> value,
156
                                       PropertyAttributes attributes,
157
                                       StoreOrigin store_origin);
158
  inline bool IsCacheableTransition();
159
  void ApplyTransitionToDataProperty(Handle<JSReceiver> receiver);
160 161
  void ReconfigureDataProperty(Handle<Object> value,
                               PropertyAttributes attributes);
162
  void Delete();
163 164
  void TransitionToAccessorProperty(Handle<Object> getter,
                                    Handle<Object> setter,
165
                                    PropertyAttributes attributes);
166 167
  void TransitionToAccessorPair(Handle<Object> pair,
                                PropertyAttributes attributes);
168
  PropertyDetails property_details() const {
169
    DCHECK(has_property_);
170 171
    return property_details_;
  }
172 173 174
  PropertyAttributes property_attributes() const {
    return property_details().attributes();
  }
175
  bool IsConfigurable() const { return property_details().IsConfigurable(); }
176
  bool IsReadOnly() const { return property_details().IsReadOnly(); }
177
  bool IsEnumerable() const { return property_details().IsEnumerable(); }
178 179
  Representation representation() const {
    return property_details().representation();
180
  }
181
  PropertyLocation location() const { return property_details().location(); }
182
  PropertyConstness constness() const { return property_details().constness(); }
183
  FieldIndex GetFieldIndex() const;
184
  int GetFieldDescriptorIndex() const;
185
  int GetAccessorIndex() const;
186
  Handle<PropertyCell> GetPropertyCell() const;
187
  Handle<Object> GetAccessors() const;
188
  inline Handle<InterceptorInfo> GetInterceptor() const;
189
  Handle<InterceptorInfo> GetInterceptorForFailedAccessCheck() const;
190 191
  Handle<Object> GetDataValue(AllocationPolicy allocation_policy =
                                  AllocationPolicy::kAllocationAllowed) const;
192
  void WriteDataValue(Handle<Object> value, bool initializing_store);
193 194
  Handle<Object> GetDataValue(SeqCstAccessTag tag) const;
  void WriteDataValue(Handle<Object> value, SeqCstAccessTag tag);
195
  Handle<Object> SwapDataValue(Handle<Object> value, SeqCstAccessTag tag);
196
  inline void UpdateProtector();
197 198
  static inline void UpdateProtector(Isolate* isolate, Handle<Object> receiver,
                                     Handle<Name> name);
199

200 201 202 203 204 205 206
#if V8_ENABLE_WEBASSEMBLY
  // Fetches type of WasmStruct's field or WasmArray's elements, it
  // is used for preparing the value for storing into WasmObjects.
  wasm::ValueType wasm_value_type() const;
  void WriteDataValueToWasmObject(Handle<Object> value);
#endif  // V8_ENABLE_WEBASSEMBLY

207 208
  // Lookup a 'cached' private property for an accessor.
  // If not found returns false and leaves the LookupIterator unmodified.
209
  bool TryLookupCachedProperty(Handle<AccessorPair> accessor);
210 211
  bool TryLookupCachedProperty();

212
 private:
213 214
  friend PropertyKey;

215 216
  static const size_t kInvalidIndex = std::numeric_limits<size_t>::max();

217
  bool LookupCachedProperty(Handle<AccessorPair> accessor);
218 219
  inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
                        Handle<Name> name, size_t index,
220 221
                        Handle<Object> lookup_start_object,
                        Configuration configuration);
222

223 224 225 226 227
  // For |ForTransitionHandler|.
  LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
                 Handle<Map> transition_map, PropertyDetails details,
                 bool has_property);

228 229
  static void InternalUpdateProtector(Isolate* isolate, Handle<Object> receiver,
                                      Handle<Name> name);
230

231 232 233 234 235 236
  enum class InterceptorState {
    kUninitialized,
    kSkipNonMasking,
    kProcessNonMasking
  };

237 238
  Handle<Map> GetReceiverMap() const;

239
  V8_WARN_UNUSED_RESULT inline JSReceiver NextHolder(Map map);
240

241 242 243
  bool is_js_array_element(bool is_element) const {
    return is_element && index_ <= JSArray::kMaxArrayIndex;
  }
244
  template <bool is_element>
245
  V8_EXPORT_PRIVATE void Start();
246
  template <bool is_element>
247
  void NextInternal(Map map, JSReceiver holder);
248
  template <bool is_element>
249
  inline State LookupInHolder(Map map, JSReceiver holder) {
250
    return map.IsSpecialReceiverMap()
251 252 253 254
               ? LookupInSpecialHolder<is_element>(map, holder)
               : LookupInRegularHolder<is_element>(map, holder);
  }
  template <bool is_element>
255
  State LookupInRegularHolder(Map map, JSReceiver holder);
256
  template <bool is_element>
257
  State LookupInSpecialHolder(Map map, JSReceiver holder);
258
  template <bool is_element>
259
  void RestartLookupForNonMaskingInterceptors() {
260
    RestartInternal<is_element>(InterceptorState::kProcessNonMasking);
261
  }
262
  template <bool is_element>
263
  void RestartInternal(InterceptorState interceptor_state);
264 265
  Handle<Object> FetchValue(AllocationPolicy allocation_policy =
                                AllocationPolicy::kAllocationAllowed) const;
266
  bool IsConstFieldValueEqualTo(Object value) const;
267 268
  bool IsConstDictValueEqualTo(Object value) const;

269
  template <bool is_element>
270
  void ReloadPropertyInformation();
271

272
  template <bool is_element>
273
  bool SkipInterceptor(JSObject holder);
274
  template <bool is_element>
275
  inline InterceptorInfo GetInterceptor(JSObject holder) const;
276

277
  bool check_interceptor() const {
278
    return (configuration_ & kInterceptor) != 0;
279
  }
280
  inline InternalIndex descriptor_number() const;
281
  inline InternalIndex dictionary_entry() const;
282

283 284
  static inline Configuration ComputeConfiguration(Isolate* isolate,
                                                   Configuration configuration,
285
                                                   Handle<Name> name);
286

287
  static Handle<JSReceiver> GetRootForNonJSReceiver(
288 289
      Isolate* isolate, Handle<Object> lookup_start_object,
      size_t index = kInvalidIndex);
290
  static inline Handle<JSReceiver> GetRoot(Isolate* isolate,
291
                                           Handle<Object> lookup_start_object,
292
                                           size_t index = kInvalidIndex);
293

294
  State NotFound(JSReceiver const holder) const;
295

296 297
  // If configuration_ becomes mutable, update
  // HolderIsReceiverOrHiddenPrototype.
298
  const Configuration configuration_;
299 300 301 302
  State state_ = NOT_FOUND;
  bool has_property_ = false;
  InterceptorState interceptor_state_ = InterceptorState::kUninitialized;
  PropertyDetails property_details_ = PropertyDetails::Empty();
303
  Isolate* const isolate_;
304
  Handle<Name> name_;
305
  Handle<Object> transition_;
306
  const Handle<Object> receiver_;
307
  Handle<JSReceiver> holder_;
308
  const Handle<Object> lookup_start_object_;
309
  const size_t index_;
310
  InternalIndex number_ = InternalIndex::NotFound();
311 312
};

313 314 315 316 317 318 319 320 321 322 323 324 325
// Similar to the LookupIterator, but for concurrent accesses from a background
// thread.
//
// Note: This is a work in progress, intended to bundle code related to
// concurrent lookups here. In its current state, the class is obviously not an
// 'iterator'. Still, keeping the name for now, with the intent to clarify
// names and implementation once we've gotten some experience with more
// involved logic.
// TODO(jgruber, v8:7790): Consider using a LookupIterator-style interface.
// TODO(jgruber, v8:7790): Consider merging back into the LookupIterator once
// functionality and constraints are better known.
class ConcurrentLookupIterator final : public AllStatic {
 public:
326 327 328 329 330 331 332
  // Tri-state to distinguish between 'not-present' and 'who-knows' failures.
  enum Result {
    kPresent,     // The value was found.
    kNotPresent,  // No value exists.
    kGaveUp,      // The operation can't be completed.
  };

333 334 335 336 337 338 339 340 341 342 343
  // Implements the own data property lookup for the specialized case of
  // fixed_cow_array backing stores (these are only in use for array literal
  // boilerplates). The contract is that the elements, elements kind, and array
  // length passed to this function should all be read from the same JSArray
  // instance; but due to concurrency it's possible that they may not be
  // consistent among themselves (e.g. the elements kind may not match the
  // given elements backing store). We are thus extra-careful to handle
  // exceptional situations.
  V8_EXPORT_PRIVATE static base::Optional<Object> TryGetOwnCowElement(
      Isolate* isolate, FixedArray array_elements, ElementsKind elements_kind,
      int array_length, size_t index);
344

345 346 347
  // As above, the contract is that the elements and elements kind should be
  // read from the same holder, but this function is implemented defensively to
  // tolerate concurrency issues.
348 349 350 351
  V8_EXPORT_PRIVATE static Result TryGetOwnConstantElement(
      Object* result_out, Isolate* isolate, LocalIsolate* local_isolate,
      JSObject holder, FixedArrayBase elements, ElementsKind elements_kind,
      size_t index);
352

353 354 355 356 357 358 359
  // Implements the own data property lookup for the specialized case of
  // strings.
  V8_EXPORT_PRIVATE static Result TryGetOwnChar(String* result_out,
                                                Isolate* isolate,
                                                LocalIsolate* local_isolate,
                                                String string, size_t index);

360 361 362 363 364 365 366 367
  // This method reimplements the following sequence in a concurrent setting:
  //
  // LookupIterator it(holder, isolate, name, LookupIterator::OWN);
  // it.TryLookupCachedProperty();
  // if (it.state() == LookupIterator::DATA) it.GetPropertyCell();
  V8_EXPORT_PRIVATE static base::Optional<PropertyCell> TryGetPropertyCell(
      Isolate* isolate, LocalIsolate* local_isolate,
      Handle<JSGlobalObject> holder, Handle<Name> name);
368 369
};

370 371
}  // namespace internal
}  // namespace v8
372

373
#endif  // V8_OBJECTS_LOOKUP_H_