1 // Copyright 2014 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_LOOKUP_H_
6 #define V8_LOOKUP_H_
7 
8 #include "src/globals.h"
9 #include "src/heap/factory.h"
10 #include "src/isolate.h"
11 #include "src/objects.h"
12 #include "src/objects/descriptor-array.h"
13 #include "src/objects/map.h"
14 
15 namespace v8 {
16 namespace internal {
17 
18 class V8_EXPORT_PRIVATE LookupIterator final BASE_EMBEDDED {
19  public:
20   enum Configuration {
21     // Configuration bits.
22     kInterceptor = 1 << 0,
23     kPrototypeChain = 1 << 1,
24 
25     // Convenience combinations of bits.
26     OWN_SKIP_INTERCEPTOR = 0,
27     OWN = kInterceptor,
28     PROTOTYPE_CHAIN_SKIP_INTERCEPTOR = kPrototypeChain,
29     PROTOTYPE_CHAIN = kPrototypeChain | kInterceptor,
30     DEFAULT = PROTOTYPE_CHAIN
31   };
32 
33   enum State {
34     ACCESS_CHECK,
35     INTEGER_INDEXED_EXOTIC,
36     INTERCEPTOR,
37     JSPROXY,
38     NOT_FOUND,
39     ACCESSOR,
40     DATA,
41     TRANSITION,
42     // Set state_ to BEFORE_PROPERTY to ensure that the next lookup will be a
43     // PROPERTY lookup.
44     BEFORE_PROPERTY = INTERCEPTOR
45   };
46 
47   LookupIterator(Handle<Object> receiver, Handle<Name> name,
48                  Configuration configuration = DEFAULT)
49       : LookupIterator(name->GetIsolate(), receiver, name, configuration) {}
50 
51   LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
52                  Configuration configuration = DEFAULT)
LookupIterator(isolate,receiver,name,GetRoot (isolate,receiver),configuration)53       : LookupIterator(isolate, receiver, name, GetRoot(isolate, receiver),
54                        configuration) {}
55 
56   LookupIterator(Handle<Object> receiver, Handle<Name> name,
57                  Handle<JSReceiver> holder,
58                  Configuration configuration = DEFAULT)
59       : LookupIterator(name->GetIsolate(), receiver, name, holder,
60                        configuration) {}
61 
62   LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
63                  Handle<JSReceiver> holder,
64                  Configuration configuration = DEFAULT)
configuration_(ComputeConfiguration (configuration,name))65       : configuration_(ComputeConfiguration(configuration, name)),
66         interceptor_state_(InterceptorState::kUninitialized),
67         property_details_(PropertyDetails::Empty()),
68         isolate_(isolate),
69         name_(isolate_->factory()->InternalizeName(name)),
70         receiver_(receiver),
71         initial_holder_(holder),
72         // kMaxUInt32 isn't a valid index.
73         index_(kMaxUInt32),
74         number_(static_cast<uint32_t>(DescriptorArray::kNotFound)) {
75 #ifdef DEBUG
76     uint32_t index;  // Assert that the name is not an array index.
77     DCHECK(!name->AsArrayIndex(&index));
78 #endif  // DEBUG
79     Start<false>();
80   }
81 
82   LookupIterator(Isolate* isolate, Handle<Object> receiver, uint32_t index,
83                  Configuration configuration = DEFAULT)
LookupIterator(isolate,receiver,index,GetRoot (isolate,receiver,index),configuration)84       : LookupIterator(isolate, receiver, index,
85                        GetRoot(isolate, receiver, index), configuration) {}
86 
87   LookupIterator(Isolate* isolate, Handle<Object> receiver, uint32_t index,
88                  Handle<JSReceiver> holder,
89                  Configuration configuration = DEFAULT)
configuration_(configuration)90       : configuration_(configuration),
91         interceptor_state_(InterceptorState::kUninitialized),
92         property_details_(PropertyDetails::Empty()),
93         isolate_(isolate),
94         receiver_(receiver),
95         initial_holder_(holder),
96         index_(index),
97         number_(static_cast<uint32_t>(DescriptorArray::kNotFound)) {
98     // kMaxUInt32 isn't a valid index.
99     DCHECK_NE(kMaxUInt32, index_);
100     Start<true>();
101   }
102 
103   static LookupIterator PropertyOrElement(
104       Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
105       Configuration configuration = DEFAULT) {
106     uint32_t index;
107     if (name->AsArrayIndex(&index)) {
108       LookupIterator it =
109           LookupIterator(isolate, receiver, index, configuration);
110       it.name_ = name;
111       return it;
112     }
113     return LookupIterator(receiver, name, configuration);
114   }
115 
116   static LookupIterator PropertyOrElement(
117       Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
118       Handle<JSReceiver> holder, Configuration configuration = DEFAULT) {
119     uint32_t index;
120     if (name->AsArrayIndex(&index)) {
121       LookupIterator it =
122           LookupIterator(isolate, receiver, index, holder, configuration);
123       it.name_ = name;
124       return it;
125     }
126     return LookupIterator(receiver, name, holder, configuration);
127   }
128 
129   static LookupIterator PropertyOrElement(
130       Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
131       bool* success, Handle<JSReceiver> holder,
132       Configuration configuration = DEFAULT);
133 
134   static LookupIterator PropertyOrElement(
135       Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
136       bool* success, Configuration configuration = DEFAULT);
137 
138   static LookupIterator ForTransitionHandler(
139       Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
140       Handle<Object> value, MaybeHandle<Map> maybe_transition_map);
141 
Restart()142   void Restart() {
143     InterceptorState state = InterceptorState::kUninitialized;
144     IsElement() ? RestartInternal<true>(state) : RestartInternal<false>(state);
145   }
146 
isolate()147   Isolate* isolate() const { return isolate_; }
state()148   State state() const { return state_; }
149 
name()150   Handle<Name> name() const {
151     DCHECK(!IsElement());
152     return name_;
153   }
GetName()154   Handle<Name> GetName() {
155     if (name_.is_null()) {
156       DCHECK(IsElement());
157       name_ = factory()->Uint32ToString(index_);
158     }
159     return name_;
160   }
index()161   uint32_t index() const { return index_; }
162 
IsElement()163   bool IsElement() const { return index_ != kMaxUInt32; }
164 
IsFound()165   bool IsFound() const { return state_ != NOT_FOUND; }
166   void Next();
NotFound()167   void NotFound() {
168     has_property_ = false;
169     state_ = NOT_FOUND;
170   }
171 
heap()172   Heap* heap() const { return isolate_->heap(); }
factory()173   Factory* factory() const { return isolate_->factory(); }
GetReceiver()174   Handle<Object> GetReceiver() const { return receiver_; }
175 
176   template <class T>
GetStoreTarget()177   Handle<T> GetStoreTarget() const {
178     DCHECK(receiver_->IsJSReceiver());
179     if (receiver_->IsJSGlobalProxy()) {
180       Map* map = JSGlobalProxy::cast(*receiver_)->map();
181       if (map->has_hidden_prototype()) {
182         return handle(JSGlobalObject::cast(map->prototype()), isolate_);
183       }
184     }
185     return Handle<T>::cast(receiver_);
186   }
is_dictionary_holder()187   bool is_dictionary_holder() const { return !holder_->HasFastProperties(); }
transition_map()188   Handle<Map> transition_map() const {
189     DCHECK_EQ(TRANSITION, state_);
190     return Handle<Map>::cast(transition_);
191   }
transition_cell()192   Handle<PropertyCell> transition_cell() const {
193     DCHECK_EQ(TRANSITION, state_);
194     return Handle<PropertyCell>::cast(transition_);
195   }
196   template <class T>
GetHolder()197   Handle<T> GetHolder() const {
198     DCHECK(IsFound());
199     return Handle<T>::cast(holder_);
200   }
201 
202   bool HolderIsReceiver() const;
203   bool HolderIsReceiverOrHiddenPrototype() const;
204 
check_prototype_chain()205   bool check_prototype_chain() const {
206     return (configuration_ & kPrototypeChain) != 0;
207   }
208 
209   /* ACCESS_CHECK */
210   bool HasAccess() const;
211 
212   /* PROPERTY */
ExtendingNonExtensible(Handle<JSReceiver> receiver)213   bool ExtendingNonExtensible(Handle<JSReceiver> receiver) {
214     DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
215     return !receiver->map()->is_extensible() &&
216            (IsElement() || !name_->IsPrivate());
217   }
218   void PrepareForDataProperty(Handle<Object> value);
219   void PrepareTransitionToDataProperty(Handle<JSReceiver> receiver,
220                                        Handle<Object> value,
221                                        PropertyAttributes attributes,
222                                        Object::StoreFromKeyed store_mode);
IsCacheableTransition()223   bool IsCacheableTransition() {
224     DCHECK_EQ(TRANSITION, state_);
225     return transition_->IsPropertyCell() ||
226            (transition_map()->is_dictionary_map() &&
227             !GetStoreTarget<JSReceiver>()->HasFastProperties()) ||
228            transition_map()->GetBackPointer()->IsMap();
229   }
230   void ApplyTransitionToDataProperty(Handle<JSReceiver> receiver);
231   void ReconfigureDataProperty(Handle<Object> value,
232                                PropertyAttributes attributes);
233   void Delete();
234   void TransitionToAccessorProperty(Handle<Object> getter,
235                                     Handle<Object> setter,
236                                     PropertyAttributes attributes);
237   void TransitionToAccessorPair(Handle<Object> pair,
238                                 PropertyAttributes attributes);
property_details()239   PropertyDetails property_details() const {
240     DCHECK(has_property_);
241     return property_details_;
242   }
property_attributes()243   PropertyAttributes property_attributes() const {
244     return property_details().attributes();
245   }
IsConfigurable()246   bool IsConfigurable() const { return property_details().IsConfigurable(); }
IsReadOnly()247   bool IsReadOnly() const { return property_details().IsReadOnly(); }
IsEnumerable()248   bool IsEnumerable() const { return property_details().IsEnumerable(); }
representation()249   Representation representation() const {
250     return property_details().representation();
251   }
location()252   PropertyLocation location() const { return property_details().location(); }
constness()253   PropertyConstness constness() const { return property_details().constness(); }
254   Handle<Map> GetFieldOwnerMap() const;
255   FieldIndex GetFieldIndex() const;
256   Handle<FieldType> GetFieldType() const;
257   int GetFieldDescriptorIndex() const;
258   int GetAccessorIndex() const;
259   int GetConstantIndex() const;
260   Handle<PropertyCell> GetPropertyCell() const;
261   Handle<Object> GetAccessors() const;
GetInterceptor()262   inline Handle<InterceptorInfo> GetInterceptor() const {
263     DCHECK_EQ(INTERCEPTOR, state_);
264     InterceptorInfo* result =
265         IsElement() ? GetInterceptor<true>(JSObject::cast(*holder_))
266                     : GetInterceptor<false>(JSObject::cast(*holder_));
267     return handle(result, isolate_);
268   }
269   Handle<InterceptorInfo> GetInterceptorForFailedAccessCheck() const;
270   Handle<Object> GetDataValue() const;
271   void WriteDataValue(Handle<Object> value, bool initializing_store);
UpdateProtector()272   inline void UpdateProtector() {
273     if (IsElement()) return;
274     // This list must be kept in sync with
275     // CodeStubAssembler::CheckForAssociatedProtector!
276     if (*name_ == heap()->is_concat_spreadable_symbol() ||
277         *name_ == heap()->constructor_string() ||
278         *name_ == heap()->next_string() || *name_ == heap()->species_symbol() ||
279         *name_ == heap()->iterator_symbol() ||
280         *name_ == heap()->resolve_string() || *name_ == heap()->then_string()) {
281       InternalUpdateProtector();
282     }
283   }
284 
285   // Lookup a 'cached' private property for an accessor.
286   // If not found returns false and leaves the LookupIterator unmodified.
287   bool TryLookupCachedProperty();
288   bool LookupCachedProperty();
289 
290  private:
291   // For |ForTransitionHandler|.
292   LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
293                  Handle<Map> transition_map, PropertyDetails details,
294                  bool has_property);
295 
296   void InternalUpdateProtector();
297 
298   enum class InterceptorState {
299     kUninitialized,
300     kSkipNonMasking,
301     kProcessNonMasking
302   };
303 
304   Handle<Map> GetReceiverMap() const;
305 
306   V8_WARN_UNUSED_RESULT inline JSReceiver* NextHolder(Map* map);
307 
308   template <bool is_element>
309   V8_EXPORT_PRIVATE void Start();
310   template <bool is_element>
311   void NextInternal(Map* map, JSReceiver* holder);
312   template <bool is_element>
LookupInHolder(Map * map,JSReceiver * holder)313   inline State LookupInHolder(Map* map, JSReceiver* holder) {
314     return map->IsSpecialReceiverMap()
315                ? LookupInSpecialHolder<is_element>(map, holder)
316                : LookupInRegularHolder<is_element>(map, holder);
317   }
318   template <bool is_element>
319   State LookupInRegularHolder(Map* map, JSReceiver* holder);
320   template <bool is_element>
321   State LookupInSpecialHolder(Map* map, JSReceiver* holder);
322   template <bool is_element>
RestartLookupForNonMaskingInterceptors()323   void RestartLookupForNonMaskingInterceptors() {
324     RestartInternal<is_element>(InterceptorState::kProcessNonMasking);
325   }
326   template <bool is_element>
327   void RestartInternal(InterceptorState interceptor_state);
328   Handle<Object> FetchValue() const;
329   bool IsConstFieldValueEqualTo(Object* value) const;
330   template <bool is_element>
331   void ReloadPropertyInformation();
332 
333   template <bool is_element>
334   bool SkipInterceptor(JSObject* holder);
335   template <bool is_element>
GetInterceptor(JSObject * holder)336   inline InterceptorInfo* GetInterceptor(JSObject* holder) const {
337     return is_element ? holder->GetIndexedInterceptor()
338                       : holder->GetNamedInterceptor();
339   }
340 
check_interceptor()341   bool check_interceptor() const {
342     return (configuration_ & kInterceptor) != 0;
343   }
descriptor_number()344   int descriptor_number() const {
345     DCHECK(!IsElement());
346     DCHECK(has_property_);
347     DCHECK(holder_->HasFastProperties());
348     return number_;
349   }
dictionary_entry()350   int dictionary_entry() const {
351     DCHECK(!IsElement());
352     DCHECK(has_property_);
353     DCHECK(!holder_->HasFastProperties());
354     return number_;
355   }
356 
ComputeConfiguration(Configuration configuration,Handle<Name> name)357   static Configuration ComputeConfiguration(
358       Configuration configuration, Handle<Name> name) {
359     return name->IsPrivate() ? OWN_SKIP_INTERCEPTOR : configuration;
360   }
361 
362   static Handle<JSReceiver> GetRootForNonJSReceiver(
363       Isolate* isolate, Handle<Object> receiver, uint32_t index = kMaxUInt32);
364   inline static Handle<JSReceiver> GetRoot(Isolate* isolate,
365                                            Handle<Object> receiver,
366                                            uint32_t index = kMaxUInt32) {
367     if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
368     return GetRootForNonJSReceiver(isolate, receiver, index);
369   }
370 
371   State NotFound(JSReceiver* const holder) const;
372 
373   // If configuration_ becomes mutable, update
374   // HolderIsReceiverOrHiddenPrototype.
375   const Configuration configuration_;
376   State state_;
377   bool has_property_;
378   InterceptorState interceptor_state_;
379   PropertyDetails property_details_;
380   Isolate* const isolate_;
381   Handle<Name> name_;
382   Handle<Object> transition_;
383   const Handle<Object> receiver_;
384   Handle<JSReceiver> holder_;
385   const Handle<JSReceiver> initial_holder_;
386   const uint32_t index_;
387   uint32_t number_;
388 };
389 
390 
391 }  // namespace internal
392 }  // namespace v8
393 
394 #endif  // V8_LOOKUP_H_
395