• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "src/objects/lookup.h"
6 
7 #include "src/common/globals.h"
8 #include "src/deoptimizer/deoptimizer.h"
9 #include "src/execution/isolate-inl.h"
10 #include "src/execution/protectors-inl.h"
11 #include "src/init/bootstrapper.h"
12 #include "src/logging/counters.h"
13 #include "src/objects/arguments-inl.h"
14 #include "src/objects/elements.h"
15 #include "src/objects/field-type.h"
16 #include "src/objects/hash-table-inl.h"
17 #include "src/objects/heap-number-inl.h"
18 #include "src/objects/ordered-hash-table.h"
19 #include "src/objects/struct-inl.h"
20 
21 namespace v8 {
22 namespace internal {
23 
Key(Isolate * isolate,Handle<Object> key,bool * success)24 LookupIterator::Key::Key(Isolate* isolate, Handle<Object> key, bool* success) {
25   if (key->ToIntegerIndex(&index_)) {
26     *success = true;
27     return;
28   }
29   *success = Object::ToName(isolate, key).ToHandle(&name_);
30   if (!*success) {
31     DCHECK(isolate->has_pending_exception());
32     index_ = kInvalidIndex;
33     return;
34   }
35   if (!name_->AsIntegerIndex(&index_)) {
36     // {AsIntegerIndex} may modify {index_} before deciding to fail.
37     index_ = LookupIterator::kInvalidIndex;
38   }
39 }
40 
LookupIterator(Isolate * isolate,Handle<Object> receiver,Handle<Name> name,Handle<Map> transition_map,PropertyDetails details,bool has_property)41 LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
42                                Handle<Name> name, Handle<Map> transition_map,
43                                PropertyDetails details, bool has_property)
44     : configuration_(DEFAULT),
45       state_(TRANSITION),
46       has_property_(has_property),
47       interceptor_state_(InterceptorState::kUninitialized),
48       property_details_(details),
49       isolate_(isolate),
50       name_(name),
51       transition_(transition_map),
52       receiver_(receiver),
53       lookup_start_object_(receiver),
54       index_(kInvalidIndex) {
55   holder_ = GetRoot(isolate, lookup_start_object_);
56 }
57 
58 template <bool is_element>
Start()59 void LookupIterator::Start() {
60   // GetRoot might allocate if lookup_start_object_ is a string.
61   holder_ = GetRoot(isolate_, lookup_start_object_, index_);
62 
63   {
64     DisallowHeapAllocation no_gc;
65 
66     has_property_ = false;
67     state_ = NOT_FOUND;
68 
69     JSReceiver holder = *holder_;
70     Map map = holder.map(isolate_);
71 
72     state_ = LookupInHolder<is_element>(map, holder);
73     if (IsFound()) return;
74 
75     NextInternal<is_element>(map, holder);
76   }
77 }
78 
79 template void LookupIterator::Start<true>();
80 template void LookupIterator::Start<false>();
81 
Next()82 void LookupIterator::Next() {
83   DCHECK_NE(JSPROXY, state_);
84   DCHECK_NE(TRANSITION, state_);
85   DisallowHeapAllocation no_gc;
86   has_property_ = false;
87 
88   JSReceiver holder = *holder_;
89   Map map = holder.map(isolate_);
90 
91   if (map.IsSpecialReceiverMap()) {
92     state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
93                          : LookupInSpecialHolder<false>(map, holder);
94     if (IsFound()) return;
95   }
96 
97   IsElement() ? NextInternal<true>(map, holder)
98               : NextInternal<false>(map, holder);
99 }
100 
101 template <bool is_element>
NextInternal(Map map,JSReceiver holder)102 void LookupIterator::NextInternal(Map map, JSReceiver holder) {
103   do {
104     JSReceiver maybe_holder = NextHolder(map);
105     if (maybe_holder.is_null()) {
106       if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
107         RestartLookupForNonMaskingInterceptors<is_element>();
108         return;
109       }
110       state_ = NOT_FOUND;
111       if (holder != *holder_) holder_ = handle(holder, isolate_);
112       return;
113     }
114     holder = maybe_holder;
115     map = holder.map(isolate_);
116     state_ = LookupInHolder<is_element>(map, holder);
117   } while (!IsFound());
118 
119   holder_ = handle(holder, isolate_);
120 }
121 
122 template <bool is_element>
RestartInternal(InterceptorState interceptor_state)123 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
124   interceptor_state_ = interceptor_state;
125   property_details_ = PropertyDetails::Empty();
126   number_ = InternalIndex::NotFound();
127   Start<is_element>();
128 }
129 
130 template void LookupIterator::RestartInternal<true>(InterceptorState);
131 template void LookupIterator::RestartInternal<false>(InterceptorState);
132 
133 // static
GetRootForNonJSReceiver(Isolate * isolate,Handle<Object> lookup_start_object,size_t index)134 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
135     Isolate* isolate, Handle<Object> lookup_start_object, size_t index) {
136   // Strings are the only objects with properties (only elements) directly on
137   // the wrapper. Hence we can skip generating the wrapper for all other cases.
138   if (lookup_start_object->IsString(isolate) &&
139       index <
140           static_cast<size_t>(String::cast(*lookup_start_object).length())) {
141     // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
142     // context, ensuring that we don't leak it into JS?
143     Handle<JSFunction> constructor = isolate->string_function();
144     Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
145     Handle<JSPrimitiveWrapper>::cast(result)->set_value(*lookup_start_object);
146     return result;
147   }
148   Handle<HeapObject> root(
149       lookup_start_object->GetPrototypeChainRootMap(isolate).prototype(isolate),
150       isolate);
151   if (root->IsNull(isolate)) {
152     isolate->PushStackTraceAndDie(
153         reinterpret_cast<void*>(lookup_start_object->ptr()));
154   }
155   return Handle<JSReceiver>::cast(root);
156 }
157 
GetReceiverMap() const158 Handle<Map> LookupIterator::GetReceiverMap() const {
159   if (receiver_->IsNumber(isolate_)) return factory()->heap_number_map();
160   return handle(Handle<HeapObject>::cast(receiver_)->map(isolate_), isolate_);
161 }
162 
HasAccess() const163 bool LookupIterator::HasAccess() const {
164   DCHECK_EQ(ACCESS_CHECK, state_);
165   return isolate_->MayAccess(handle(isolate_->context(), isolate_),
166                              GetHolder<JSObject>());
167 }
168 
169 template <bool is_element>
ReloadPropertyInformation()170 void LookupIterator::ReloadPropertyInformation() {
171   state_ = BEFORE_PROPERTY;
172   interceptor_state_ = InterceptorState::kUninitialized;
173   state_ = LookupInHolder<is_element>(holder_->map(isolate_), *holder_);
174   DCHECK(IsFound() || !holder_->HasFastProperties(isolate_));
175 }
176 
177 namespace {
178 
IsTypedArrayFunctionInAnyContext(Isolate * isolate,HeapObject object)179 bool IsTypedArrayFunctionInAnyContext(Isolate* isolate, HeapObject object) {
180   static uint32_t context_slots[] = {
181 #define TYPED_ARRAY_CONTEXT_SLOTS(Type, type, TYPE, ctype) \
182   Context::TYPE##_ARRAY_FUN_INDEX,
183 
184       TYPED_ARRAYS(TYPED_ARRAY_CONTEXT_SLOTS)
185 #undef TYPED_ARRAY_CONTEXT_SLOTS
186   };
187 
188   if (!object.IsJSFunction(isolate)) return false;
189 
190   return std::any_of(
191       std::begin(context_slots), std::end(context_slots),
192       [=](uint32_t slot) { return isolate->IsInAnyContext(object, slot); });
193 }
194 
195 }  // namespace
196 
197 // static
InternalUpdateProtector(Isolate * isolate,Handle<Object> receiver_generic,Handle<Name> name)198 void LookupIterator::InternalUpdateProtector(Isolate* isolate,
199                                              Handle<Object> receiver_generic,
200                                              Handle<Name> name) {
201   if (isolate->bootstrapper()->IsActive()) return;
202   if (!receiver_generic->IsHeapObject()) return;
203   Handle<HeapObject> receiver = Handle<HeapObject>::cast(receiver_generic);
204 
205   ReadOnlyRoots roots(isolate);
206   if (*name == roots.constructor_string()) {
207     if (!Protectors::IsArraySpeciesLookupChainIntact(isolate) &&
208         !Protectors::IsPromiseSpeciesLookupChainIntact(isolate) &&
209         !Protectors::IsRegExpSpeciesLookupChainIntact(isolate) &&
210         !Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) {
211       return;
212     }
213     // Setting the constructor property could change an instance's @@species
214     if (receiver->IsJSArray(isolate)) {
215       if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
216       isolate->CountUsage(
217           v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
218       Protectors::InvalidateArraySpeciesLookupChain(isolate);
219       return;
220     } else if (receiver->IsJSPromise(isolate)) {
221       if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
222       Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
223       return;
224     } else if (receiver->IsJSRegExp(isolate)) {
225       if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
226       Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
227       return;
228     } else if (receiver->IsJSTypedArray(isolate)) {
229       if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
230       Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
231       return;
232     }
233     if (receiver->map(isolate).is_prototype_map()) {
234       DisallowHeapAllocation no_gc;
235       // Setting the constructor of any prototype with the @@species protector
236       // (of any realm) also needs to invalidate the protector.
237       // For typed arrays, we check a prototype of this receiver since
238       // TypedArrays have different prototypes for each type, and their parent
239       // prototype is pointing the same TYPED_ARRAY_PROTOTYPE.
240       if (isolate->IsInAnyContext(*receiver,
241                                   Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
242         if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
243         isolate->CountUsage(
244             v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified);
245         Protectors::InvalidateArraySpeciesLookupChain(isolate);
246       } else if (isolate->IsInAnyContext(*receiver,
247                                          Context::PROMISE_PROTOTYPE_INDEX)) {
248         if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
249         Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
250       } else if (isolate->IsInAnyContext(*receiver,
251                                          Context::REGEXP_PROTOTYPE_INDEX)) {
252         if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
253         Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
254       } else if (isolate->IsInAnyContext(
255                      receiver->map(isolate).prototype(isolate),
256                      Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
257         if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
258         Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
259       }
260     }
261   } else if (*name == roots.next_string()) {
262     if (receiver->IsJSArrayIterator() ||
263         isolate->IsInAnyContext(
264             *receiver, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)) {
265       // Setting the next property of %ArrayIteratorPrototype% also needs to
266       // invalidate the array iterator protector.
267       if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return;
268       Protectors::InvalidateArrayIteratorLookupChain(isolate);
269     } else if (receiver->IsJSMapIterator() ||
270                isolate->IsInAnyContext(
271                    *receiver, Context::INITIAL_MAP_ITERATOR_PROTOTYPE_INDEX)) {
272       if (!Protectors::IsMapIteratorLookupChainIntact(isolate)) return;
273       Protectors::InvalidateMapIteratorLookupChain(isolate);
274     } else if (receiver->IsJSSetIterator() ||
275                isolate->IsInAnyContext(
276                    *receiver, Context::INITIAL_SET_ITERATOR_PROTOTYPE_INDEX)) {
277       if (!Protectors::IsSetIteratorLookupChainIntact(isolate)) return;
278       Protectors::InvalidateSetIteratorLookupChain(isolate);
279     } else if (receiver->IsJSStringIterator() ||
280                isolate->IsInAnyContext(
281                    *receiver,
282                    Context::INITIAL_STRING_ITERATOR_PROTOTYPE_INDEX)) {
283       // Setting the next property of %StringIteratorPrototype% invalidates the
284       // string iterator protector.
285       if (!Protectors::IsStringIteratorLookupChainIntact(isolate)) return;
286       Protectors::InvalidateStringIteratorLookupChain(isolate);
287     }
288   } else if (*name == roots.species_symbol()) {
289     if (!Protectors::IsArraySpeciesLookupChainIntact(isolate) &&
290         !Protectors::IsPromiseSpeciesLookupChainIntact(isolate) &&
291         !Protectors::IsRegExpSpeciesLookupChainIntact(isolate) &&
292         !Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) {
293       return;
294     }
295     // Setting the Symbol.species property of any Array, Promise or TypedArray
296     // constructor invalidates the @@species protector
297     if (isolate->IsInAnyContext(*receiver, Context::ARRAY_FUNCTION_INDEX)) {
298       if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
299       isolate->CountUsage(
300           v8::Isolate::UseCounterFeature::kArraySpeciesModified);
301       Protectors::InvalidateArraySpeciesLookupChain(isolate);
302     } else if (isolate->IsInAnyContext(*receiver,
303                                        Context::PROMISE_FUNCTION_INDEX)) {
304       if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
305       Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
306     } else if (isolate->IsInAnyContext(*receiver,
307                                        Context::REGEXP_FUNCTION_INDEX)) {
308       if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
309       Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
310     } else if (IsTypedArrayFunctionInAnyContext(isolate, *receiver)) {
311       if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
312       Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
313     }
314   } else if (*name == roots.is_concat_spreadable_symbol()) {
315     if (!Protectors::IsIsConcatSpreadableLookupChainIntact(isolate)) return;
316     Protectors::InvalidateIsConcatSpreadableLookupChain(isolate);
317   } else if (*name == roots.iterator_symbol()) {
318     if (receiver->IsJSArray(isolate)) {
319       if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return;
320       Protectors::InvalidateArrayIteratorLookupChain(isolate);
321     } else if (receiver->IsJSSet(isolate) || receiver->IsJSSetIterator() ||
322                isolate->IsInAnyContext(
323                    *receiver, Context::INITIAL_SET_ITERATOR_PROTOTYPE_INDEX) ||
324                isolate->IsInAnyContext(*receiver,
325                                        Context::INITIAL_SET_PROTOTYPE_INDEX)) {
326       if (Protectors::IsSetIteratorLookupChainIntact(isolate)) {
327         Protectors::InvalidateSetIteratorLookupChain(isolate);
328       }
329     } else if (receiver->IsJSMapIterator() ||
330                isolate->IsInAnyContext(
331                    *receiver, Context::INITIAL_MAP_ITERATOR_PROTOTYPE_INDEX)) {
332       if (Protectors::IsMapIteratorLookupChainIntact(isolate)) {
333         Protectors::InvalidateMapIteratorLookupChain(isolate);
334       }
335     } else if (isolate->IsInAnyContext(
336                    *receiver, Context::INITIAL_ITERATOR_PROTOTYPE_INDEX)) {
337       if (Protectors::IsMapIteratorLookupChainIntact(isolate)) {
338         Protectors::InvalidateMapIteratorLookupChain(isolate);
339       }
340       if (Protectors::IsSetIteratorLookupChainIntact(isolate)) {
341         Protectors::InvalidateSetIteratorLookupChain(isolate);
342       }
343     } else if (isolate->IsInAnyContext(
344                    *receiver, Context::INITIAL_STRING_PROTOTYPE_INDEX)) {
345       // Setting the Symbol.iterator property of String.prototype invalidates
346       // the string iterator protector. Symbol.iterator can also be set on a
347       // String wrapper, but not on a primitive string. We only support
348       // protector for primitive strings.
349       if (!Protectors::IsStringIteratorLookupChainIntact(isolate)) return;
350       Protectors::InvalidateStringIteratorLookupChain(isolate);
351     }
352   } else if (*name == roots.resolve_string()) {
353     if (!Protectors::IsPromiseResolveLookupChainIntact(isolate)) return;
354     // Setting the "resolve" property on any %Promise% intrinsic object
355     // invalidates the Promise.resolve protector.
356     if (isolate->IsInAnyContext(*receiver, Context::PROMISE_FUNCTION_INDEX)) {
357       Protectors::InvalidatePromiseResolveLookupChain(isolate);
358     }
359   } else if (*name == roots.then_string()) {
360     if (!Protectors::IsPromiseThenLookupChainIntact(isolate)) return;
361     // Setting the "then" property on any JSPromise instance or on the
362     // initial %PromisePrototype% invalidates the Promise#then protector.
363     // Also setting the "then" property on the initial %ObjectPrototype%
364     // invalidates the Promise#then protector, since we use this protector
365     // to guard the fast-path in AsyncGeneratorResolve, where we can skip
366     // the ResolvePromise step and go directly to FulfillPromise if we
367     // know that the Object.prototype doesn't contain a "then" method.
368     if (receiver->IsJSPromise(isolate) ||
369         isolate->IsInAnyContext(*receiver,
370                                 Context::INITIAL_OBJECT_PROTOTYPE_INDEX) ||
371         isolate->IsInAnyContext(*receiver, Context::PROMISE_PROTOTYPE_INDEX)) {
372       Protectors::InvalidatePromiseThenLookupChain(isolate);
373     }
374   }
375 }
376 
PrepareForDataProperty(Handle<Object> value)377 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
378   DCHECK(state_ == DATA || state_ == ACCESSOR);
379   DCHECK(HolderIsReceiverOrHiddenPrototype());
380 
381   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
382   // JSProxy does not have fast properties so we do an early return.
383   DCHECK_IMPLIES(holder->IsJSProxy(isolate_),
384                  !holder->HasFastProperties(isolate_));
385   DCHECK_IMPLIES(holder->IsJSProxy(isolate_), name()->IsPrivate(isolate_));
386   if (holder->IsJSProxy(isolate_)) return;
387 
388   Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
389 
390   if (IsElement(*holder)) {
391     ElementsKind kind = holder_obj->GetElementsKind(isolate_);
392     ElementsKind to = value->OptimalElementsKind(isolate_);
393     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
394     to = GetMoreGeneralElementsKind(kind, to);
395 
396     if (kind != to) {
397       JSObject::TransitionElementsKind(holder_obj, to);
398     }
399 
400     // Copy the backing store if it is copy-on-write.
401     if (IsSmiOrObjectElementsKind(to) || IsSealedElementsKind(to) ||
402         IsNonextensibleElementsKind(to)) {
403       JSObject::EnsureWritableFastElements(holder_obj);
404     }
405     return;
406   }
407 
408   if (holder_obj->IsJSGlobalObject(isolate_)) {
409     Handle<GlobalDictionary> dictionary(
410         JSGlobalObject::cast(*holder_obj).global_dictionary(isolate_),
411         isolate());
412     Handle<PropertyCell> cell(dictionary->CellAt(isolate_, dictionary_entry()),
413                               isolate());
414     property_details_ = cell->property_details();
415     PropertyCell::PrepareForValue(isolate(), dictionary, dictionary_entry(),
416                                   value, property_details_);
417     return;
418   }
419   if (!holder_obj->HasFastProperties(isolate_)) return;
420 
421   PropertyConstness new_constness = PropertyConstness::kConst;
422   if (constness() == PropertyConstness::kConst) {
423     DCHECK_EQ(kData, property_details_.kind());
424     // Check that current value matches new value otherwise we should make
425     // the property mutable.
426     if (!IsConstFieldValueEqualTo(*value))
427       new_constness = PropertyConstness::kMutable;
428   }
429 
430   Handle<Map> old_map(holder_obj->map(isolate_), isolate_);
431   DCHECK(!old_map->is_dictionary_map());
432 
433   Handle<Map> new_map = Map::Update(isolate_, old_map);
434   if (!new_map->is_dictionary_map()) {
435     new_map = Map::PrepareForDataProperty(
436         isolate(), new_map, descriptor_number(), new_constness, value);
437 
438     if (old_map.is_identical_to(new_map)) {
439       // Update the property details if the representation was None.
440       if (constness() != new_constness || representation().IsNone()) {
441         property_details_ =
442             new_map->instance_descriptors(isolate_, kRelaxedLoad)
443                 .GetDetails(descriptor_number());
444       }
445       return;
446     }
447   }
448   // We should only get here if the new_map is different from the old map,
449   // otherwise we would have falled through to the is_identical_to check above.
450   DCHECK_NE(*old_map, *new_map);
451 
452   JSObject::MigrateToMap(isolate_, holder_obj, new_map);
453   ReloadPropertyInformation<false>();
454 }
455 
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)456 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
457                                              PropertyAttributes attributes) {
458   DCHECK(state_ == DATA || state_ == ACCESSOR);
459   DCHECK(HolderIsReceiverOrHiddenPrototype());
460 
461   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
462 
463   // Property details can never change for private properties.
464   if (holder->IsJSProxy(isolate_)) {
465     DCHECK(name()->IsPrivate(isolate_));
466     return;
467   }
468 
469   Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
470   if (IsElement(*holder)) {
471     DCHECK(!holder_obj->HasTypedArrayElements(isolate_));
472     DCHECK(attributes != NONE || !holder_obj->HasFastElements(isolate_));
473     Handle<FixedArrayBase> elements(holder_obj->elements(isolate_), isolate());
474     holder_obj->GetElementsAccessor(isolate_)->Reconfigure(
475         holder_obj, elements, number_, value, attributes);
476     ReloadPropertyInformation<true>();
477   } else if (holder_obj->HasFastProperties(isolate_)) {
478     Handle<Map> old_map(holder_obj->map(isolate_), isolate_);
479     // Force mutable to avoid changing constant value by reconfiguring
480     // kData -> kAccessor -> kData.
481     Handle<Map> new_map = Map::ReconfigureExistingProperty(
482         isolate_, old_map, descriptor_number(), i::kData, attributes,
483         PropertyConstness::kMutable);
484     if (!new_map->is_dictionary_map()) {
485       // Make sure that the data property has a compatible representation.
486       // TODO(leszeks): Do this as part of ReconfigureExistingProperty.
487       new_map =
488           Map::PrepareForDataProperty(isolate(), new_map, descriptor_number(),
489                                       PropertyConstness::kMutable, value);
490     }
491     JSObject::MigrateToMap(isolate_, holder_obj, new_map);
492     ReloadPropertyInformation<false>();
493   }
494 
495   if (!IsElement(*holder) && !holder_obj->HasFastProperties(isolate_)) {
496     PropertyDetails details(kData, attributes, PropertyCellType::kMutable);
497     if (holder_obj->map(isolate_).is_prototype_map() &&
498         (property_details_.attributes() & READ_ONLY) == 0 &&
499         (attributes & READ_ONLY) != 0) {
500       // Invalidate prototype validity cell when a property is reconfigured
501       // from writable to read-only as this may invalidate transitioning store
502       // IC handlers.
503       JSObject::InvalidatePrototypeChains(holder->map(isolate_));
504     }
505     if (holder_obj->IsJSGlobalObject(isolate_)) {
506       Handle<GlobalDictionary> dictionary(
507           JSGlobalObject::cast(*holder_obj).global_dictionary(isolate_),
508           isolate());
509 
510       Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
511           isolate(), dictionary, dictionary_entry(), value, details);
512       cell->set_value(*value);
513       property_details_ = cell->property_details();
514     } else {
515       if (V8_DICT_MODE_PROTOTYPES_BOOL) {
516         Handle<OrderedNameDictionary> dictionary(
517             holder_obj->property_dictionary_ordered(isolate_), isolate());
518         dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
519         DCHECK_EQ(details.AsSmi(),
520                   dictionary->DetailsAt(dictionary_entry()).AsSmi());
521         property_details_ = details;
522       } else {
523         Handle<NameDictionary> dictionary(
524             holder_obj->property_dictionary(isolate_), isolate());
525         PropertyDetails original_details =
526             dictionary->DetailsAt(dictionary_entry());
527         int enumeration_index = original_details.dictionary_index();
528         DCHECK_GT(enumeration_index, 0);
529         details = details.set_index(enumeration_index);
530         dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
531         property_details_ = details;
532       }
533     }
534     state_ = DATA;
535   }
536 
537   WriteDataValue(value, true);
538 
539 #if VERIFY_HEAP
540   if (FLAG_verify_heap) {
541     holder->HeapObjectVerify(isolate());
542   }
543 #endif
544 }
545 
546 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
547 // via a trap. Adding properties to primitive values is not observable.
PrepareTransitionToDataProperty(Handle<JSReceiver> receiver,Handle<Object> value,PropertyAttributes attributes,StoreOrigin store_origin)548 void LookupIterator::PrepareTransitionToDataProperty(
549     Handle<JSReceiver> receiver, Handle<Object> value,
550     PropertyAttributes attributes, StoreOrigin store_origin) {
551   DCHECK_IMPLIES(receiver->IsJSProxy(isolate_), name()->IsPrivate(isolate_));
552   DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
553   if (state_ == TRANSITION) return;
554 
555   if (!IsElement() && name()->IsPrivate(isolate_)) {
556     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
557   }
558 
559   DCHECK(state_ != LookupIterator::ACCESSOR ||
560          (GetAccessors()->IsAccessorInfo(isolate_) &&
561           AccessorInfo::cast(*GetAccessors()).is_special_data_property()));
562   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
563   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
564 
565   Handle<Map> map(receiver->map(isolate_), isolate_);
566 
567   // Dictionary maps can always have additional data properties.
568   if (map->is_dictionary_map()) {
569     state_ = TRANSITION;
570     if (map->IsJSGlobalObjectMap()) {
571       Handle<PropertyCell> cell = isolate_->factory()->NewPropertyCell(name());
572       DCHECK(cell->value(isolate_).IsTheHole(isolate_));
573       DCHECK(!value->IsTheHole(isolate_));
574       // Don't set enumeration index (it will be set during value store).
575       property_details_ = PropertyDetails(
576           kData, attributes,
577           PropertyCell::TypeForUninitializedCell(isolate_, value));
578 
579       transition_ = cell;
580       has_property_ = true;
581     } else {
582       // Don't set enumeration index (it will be set during value store).
583       property_details_ =
584           PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
585       transition_ = map;
586     }
587     return;
588   }
589 
590   Handle<Map> transition =
591       Map::TransitionToDataProperty(isolate_, map, name_, value, attributes,
592                                     PropertyConstness::kConst, store_origin);
593   state_ = TRANSITION;
594   transition_ = transition;
595 
596   if (transition->is_dictionary_map()) {
597     // Don't set enumeration index (it will be set during value store).
598     property_details_ =
599         PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
600   } else {
601     property_details_ = transition->GetLastDescriptorDetails(isolate_);
602     has_property_ = true;
603   }
604 }
605 
ApplyTransitionToDataProperty(Handle<JSReceiver> receiver)606 void LookupIterator::ApplyTransitionToDataProperty(
607     Handle<JSReceiver> receiver) {
608   DCHECK_EQ(TRANSITION, state_);
609 
610   DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
611   holder_ = receiver;
612   if (receiver->IsJSGlobalObject(isolate_)) {
613     JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
614 
615     // Install a property cell.
616     Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
617     DCHECK(!global->HasFastProperties());
618     Handle<GlobalDictionary> dictionary(global->global_dictionary(isolate_),
619                                         isolate_);
620 
621     dictionary =
622         GlobalDictionary::Add(isolate_, dictionary, name(), transition_cell(),
623                               property_details_, &number_);
624     global->set_global_dictionary(*dictionary);
625 
626     // Reload details containing proper enumeration index value.
627     property_details_ = transition_cell()->property_details();
628     has_property_ = true;
629     state_ = DATA;
630     return;
631   }
632   Handle<Map> transition = transition_map();
633   bool simple_transition =
634       transition->GetBackPointer(isolate_) == receiver->map(isolate_);
635 
636   if (configuration_ == DEFAULT && !transition->is_dictionary_map() &&
637       !transition->IsPrototypeValidityCellValid()) {
638     // Only LookupIterator instances with DEFAULT (full prototype chain)
639     // configuration can produce valid transition handler maps.
640     Handle<Object> validity_cell =
641         Map::GetOrCreatePrototypeChainValidityCell(transition, isolate());
642     transition->set_prototype_validity_cell(*validity_cell);
643   }
644 
645   if (!receiver->IsJSProxy(isolate_)) {
646     JSObject::MigrateToMap(isolate_, Handle<JSObject>::cast(receiver),
647                            transition);
648   }
649 
650   if (simple_transition) {
651     number_ = transition->LastAdded();
652     property_details_ = transition->GetLastDescriptorDetails(isolate_);
653     state_ = DATA;
654   } else if (receiver->map(isolate_).is_dictionary_map()) {
655     if (receiver->map(isolate_).is_prototype_map() &&
656         receiver->IsJSObject(isolate_)) {
657       JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
658     }
659     if (V8_DICT_MODE_PROTOTYPES_BOOL) {
660       Handle<OrderedNameDictionary> dictionary(
661           receiver->property_dictionary_ordered(isolate_), isolate_);
662 
663       dictionary =
664           OrderedNameDictionary::Add(isolate(), dictionary, name(),
665                                      isolate_->factory()->uninitialized_value(),
666                                      property_details_)
667               .ToHandleChecked();
668 
669       // set to last used entry
670       number_ = InternalIndex(dictionary->UsedCapacity() - 1);
671       receiver->SetProperties(*dictionary);
672     } else {
673       Handle<NameDictionary> dictionary(receiver->property_dictionary(isolate_),
674                                         isolate_);
675 
676       dictionary =
677           NameDictionary::Add(isolate(), dictionary, name(),
678                               isolate_->factory()->uninitialized_value(),
679                               property_details_, &number_);
680       receiver->SetProperties(*dictionary);
681       // Reload details containing proper enumeration index value.
682       property_details_ = dictionary->DetailsAt(number_);
683     }
684     has_property_ = true;
685     state_ = DATA;
686 
687   } else {
688     ReloadPropertyInformation<false>();
689   }
690 }
691 
Delete()692 void LookupIterator::Delete() {
693   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
694   if (IsElement(*holder)) {
695     Handle<JSObject> object = Handle<JSObject>::cast(holder);
696     ElementsAccessor* accessor = object->GetElementsAccessor(isolate_);
697     accessor->Delete(object, number_);
698   } else {
699     DCHECK(!name()->IsPrivateName(isolate_));
700     bool is_prototype_map = holder->map(isolate_).is_prototype_map();
701     RuntimeCallTimerScope stats_scope(
702         isolate_, is_prototype_map
703                       ? RuntimeCallCounterId::kPrototypeObject_DeleteProperty
704                       : RuntimeCallCounterId::kObject_DeleteProperty);
705 
706     PropertyNormalizationMode mode =
707         is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
708 
709     if (holder->HasFastProperties(isolate_)) {
710       JSObject::NormalizeProperties(isolate_, Handle<JSObject>::cast(holder),
711                                     mode, 0, "DeletingProperty");
712       ReloadPropertyInformation<false>();
713     }
714     JSReceiver::DeleteNormalizedProperty(holder, dictionary_entry());
715     if (holder->IsJSObject(isolate_)) {
716       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
717     }
718   }
719   state_ = NOT_FOUND;
720 }
721 
TransitionToAccessorProperty(Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)722 void LookupIterator::TransitionToAccessorProperty(
723     Handle<Object> getter, Handle<Object> setter,
724     PropertyAttributes attributes) {
725   DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
726   // Can only be called when the receiver is a JSObject. JSProxy has to be
727   // handled via a trap. Adding properties to primitive values is not
728   // observable.
729   Handle<JSObject> receiver = GetStoreTarget<JSObject>();
730   if (!IsElement() && name()->IsPrivate(isolate_)) {
731     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
732   }
733 
734   if (!IsElement(*receiver) && !receiver->map(isolate_).is_dictionary_map()) {
735     Handle<Map> old_map(receiver->map(isolate_), isolate_);
736 
737     if (!holder_.is_identical_to(receiver)) {
738       holder_ = receiver;
739       state_ = NOT_FOUND;
740     } else if (state_ == INTERCEPTOR) {
741       LookupInRegularHolder<false>(*old_map, *holder_);
742     }
743     // The case of IsFound() && number_.is_not_found() can occur for
744     // interceptors.
745     DCHECK_IMPLIES(!IsFound(), number_.is_not_found());
746 
747     Handle<Map> new_map = Map::TransitionToAccessorProperty(
748         isolate_, old_map, name_, number_, getter, setter, attributes);
749     bool simple_transition =
750         new_map->GetBackPointer(isolate_) == receiver->map(isolate_);
751     JSObject::MigrateToMap(isolate_, receiver, new_map);
752 
753     if (simple_transition) {
754       number_ = new_map->LastAdded();
755       property_details_ = new_map->GetLastDescriptorDetails(isolate_);
756       state_ = ACCESSOR;
757       return;
758     }
759 
760     ReloadPropertyInformation<false>();
761     if (!new_map->is_dictionary_map()) return;
762   }
763 
764   Handle<AccessorPair> pair;
765   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair(isolate_)) {
766     pair = Handle<AccessorPair>::cast(GetAccessors());
767     // If the component and attributes are identical, nothing has to be done.
768     if (pair->Equals(*getter, *setter)) {
769       if (property_details().attributes() == attributes) {
770         if (!IsElement(*receiver)) JSObject::ReoptimizeIfPrototype(receiver);
771         return;
772       }
773     } else {
774       pair = AccessorPair::Copy(isolate(), pair);
775       pair->SetComponents(*getter, *setter);
776     }
777   } else {
778     pair = factory()->NewAccessorPair();
779     pair->SetComponents(*getter, *setter);
780   }
781 
782   TransitionToAccessorPair(pair, attributes);
783 
784 #if VERIFY_HEAP
785   if (FLAG_verify_heap) {
786     receiver->JSObjectVerify(isolate());
787   }
788 #endif
789 }
790 
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)791 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
792                                               PropertyAttributes attributes) {
793   Handle<JSObject> receiver = GetStoreTarget<JSObject>();
794   holder_ = receiver;
795 
796   PropertyDetails details(kAccessor, attributes, PropertyCellType::kMutable);
797 
798   if (IsElement(*receiver)) {
799     // TODO(verwaest): Move code into the element accessor.
800     isolate_->CountUsage(v8::Isolate::kIndexAccessor);
801     Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(receiver);
802 
803     dictionary = NumberDictionary::Set(isolate_, dictionary, array_index(),
804                                        pair, receiver, details);
805     receiver->RequireSlowElements(*dictionary);
806 
807     if (receiver->HasSlowArgumentsElements(isolate_)) {
808       SloppyArgumentsElements parameter_map =
809           SloppyArgumentsElements::cast(receiver->elements(isolate_));
810       uint32_t length = parameter_map.length();
811       if (number_.is_found() && number_.as_uint32() < length) {
812         parameter_map.set_mapped_entries(
813             number_.as_int(), ReadOnlyRoots(isolate_).the_hole_value());
814       }
815       parameter_map.set_arguments(*dictionary);
816     } else {
817       receiver->set_elements(*dictionary);
818     }
819 
820     ReloadPropertyInformation<true>();
821   } else {
822     PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES;
823     if (receiver->map(isolate_).is_prototype_map()) {
824       JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
825       mode = KEEP_INOBJECT_PROPERTIES;
826     }
827 
828     // Normalize object to make this operation simple.
829     JSObject::NormalizeProperties(isolate_, receiver, mode, 0,
830                                   "TransitionToAccessorPair");
831 
832     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
833     JSObject::ReoptimizeIfPrototype(receiver);
834 
835     ReloadPropertyInformation<false>();
836   }
837 }
838 
HolderIsReceiver() const839 bool LookupIterator::HolderIsReceiver() const {
840   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
841   // Optimization that only works if configuration_ is not mutable.
842   if (!check_prototype_chain()) return true;
843   return *receiver_ == *holder_;
844 }
845 
HolderIsReceiverOrHiddenPrototype() const846 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
847   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
848   // Optimization that only works if configuration_ is not mutable.
849   if (!check_prototype_chain()) return true;
850   if (*receiver_ == *holder_) return true;
851   if (!receiver_->IsJSGlobalProxy(isolate_)) return false;
852   return Handle<JSGlobalProxy>::cast(receiver_)->map(isolate_).prototype(
853              isolate_) == *holder_;
854 }
855 
FetchValue(AllocationPolicy allocation_policy) const856 Handle<Object> LookupIterator::FetchValue(
857     AllocationPolicy allocation_policy) const {
858   Object result;
859   if (IsElement(*holder_)) {
860     Handle<JSObject> holder = GetHolder<JSObject>();
861     ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
862     return accessor->Get(holder, number_);
863   } else if (holder_->IsJSGlobalObject(isolate_)) {
864     Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
865     result = holder->global_dictionary(isolate_).ValueAt(isolate_,
866                                                          dictionary_entry());
867   } else if (!holder_->HasFastProperties(isolate_)) {
868     if (V8_DICT_MODE_PROTOTYPES_BOOL) {
869       result = holder_->property_dictionary_ordered(isolate_).ValueAt(
870           dictionary_entry());
871     } else {
872       result = holder_->property_dictionary(isolate_).ValueAt(
873           isolate_, dictionary_entry());
874     }
875   } else if (property_details_.location() == kField) {
876     DCHECK_EQ(kData, property_details_.kind());
877     Handle<JSObject> holder = GetHolder<JSObject>();
878     FieldIndex field_index =
879         FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
880     if (allocation_policy == AllocationPolicy::kAllocationDisallowed &&
881         field_index.is_inobject() && field_index.is_double()) {
882       return isolate_->factory()->undefined_value();
883     }
884     return JSObject::FastPropertyAt(holder, property_details_.representation(),
885                                     field_index);
886   } else {
887     result = holder_->map(isolate_)
888                  .instance_descriptors(isolate_, kRelaxedLoad)
889                  .GetStrongValue(isolate_, descriptor_number());
890   }
891   return handle(result, isolate_);
892 }
893 
IsConstFieldValueEqualTo(Object value) const894 bool LookupIterator::IsConstFieldValueEqualTo(Object value) const {
895   DCHECK(!IsElement(*holder_));
896   DCHECK(holder_->HasFastProperties(isolate_));
897   DCHECK_EQ(kField, property_details_.location());
898   DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
899   if (value.IsUninitialized(isolate())) {
900     // Storing uninitialized value means that we are preparing for a computed
901     // property value in an object literal. The initializing store will follow
902     // and it will properly update constness based on the actual value.
903     return true;
904   }
905   Handle<JSObject> holder = GetHolder<JSObject>();
906   FieldIndex field_index =
907       FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
908   if (property_details_.representation().IsDouble()) {
909     if (!value.IsNumber(isolate_)) return false;
910     uint64_t bits;
911     if (holder->IsUnboxedDoubleField(isolate_, field_index)) {
912       bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
913     } else {
914       Object current_value = holder->RawFastPropertyAt(isolate_, field_index);
915       DCHECK(current_value.IsHeapNumber(isolate_));
916       bits = HeapNumber::cast(current_value).value_as_bits();
917     }
918     // Use bit representation of double to check for hole double, since
919     // manipulating the signaling NaN used for the hole in C++, e.g. with
920     // bit_cast or value(), will change its value on ia32 (the x87 stack is
921     // used to return values and stores to the stack silently clear the
922     // signalling bit).
923     if (bits == kHoleNanInt64) {
924       // Uninitialized double field.
925       return true;
926     }
927     return Object::SameNumberValue(bit_cast<double>(bits), value.Number());
928   } else {
929     Object current_value = holder->RawFastPropertyAt(isolate_, field_index);
930     if (current_value.IsUninitialized(isolate()) || current_value == value) {
931       return true;
932     }
933     return current_value.IsNumber(isolate_) && value.IsNumber(isolate_) &&
934            Object::SameNumberValue(current_value.Number(), value.Number());
935   }
936 }
937 
GetFieldDescriptorIndex() const938 int LookupIterator::GetFieldDescriptorIndex() const {
939   DCHECK(has_property_);
940   DCHECK(holder_->HasFastProperties());
941   DCHECK_EQ(kField, property_details_.location());
942   DCHECK_EQ(kData, property_details_.kind());
943   // TODO(jkummerow): Propagate InternalIndex further.
944   return descriptor_number().as_int();
945 }
946 
GetAccessorIndex() const947 int LookupIterator::GetAccessorIndex() const {
948   DCHECK(has_property_);
949   DCHECK(holder_->HasFastProperties(isolate_));
950   DCHECK_EQ(kDescriptor, property_details_.location());
951   DCHECK_EQ(kAccessor, property_details_.kind());
952   return descriptor_number().as_int();
953 }
954 
GetFieldOwnerMap() const955 Handle<Map> LookupIterator::GetFieldOwnerMap() const {
956   DCHECK(has_property_);
957   DCHECK(holder_->HasFastProperties(isolate_));
958   DCHECK_EQ(kField, property_details_.location());
959   DCHECK(!IsElement(*holder_));
960   Map holder_map = holder_->map(isolate_);
961   return handle(holder_map.FindFieldOwner(isolate(), descriptor_number()),
962                 isolate_);
963 }
964 
GetFieldIndex() const965 FieldIndex LookupIterator::GetFieldIndex() const {
966   DCHECK(has_property_);
967   DCHECK(holder_->HasFastProperties(isolate_));
968   DCHECK_EQ(kField, property_details_.location());
969   DCHECK(!IsElement(*holder_));
970   return FieldIndex::ForDescriptor(holder_->map(isolate_), descriptor_number());
971 }
972 
GetFieldType() const973 Handle<FieldType> LookupIterator::GetFieldType() const {
974   DCHECK(has_property_);
975   DCHECK(holder_->HasFastProperties(isolate_));
976   DCHECK_EQ(kField, property_details_.location());
977   return handle(holder_->map(isolate_)
978                     .instance_descriptors(isolate_, kRelaxedLoad)
979                     .GetFieldType(isolate_, descriptor_number()),
980                 isolate_);
981 }
982 
GetPropertyCell() const983 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
984   DCHECK(!IsElement(*holder_));
985   Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
986   return handle(
987       holder->global_dictionary(isolate_).CellAt(isolate_, dictionary_entry()),
988       isolate_);
989 }
990 
GetAccessors() const991 Handle<Object> LookupIterator::GetAccessors() const {
992   DCHECK_EQ(ACCESSOR, state_);
993   return FetchValue();
994 }
995 
GetDataValue(AllocationPolicy allocation_policy) const996 Handle<Object> LookupIterator::GetDataValue(
997     AllocationPolicy allocation_policy) const {
998   DCHECK_EQ(DATA, state_);
999   Handle<Object> value = FetchValue(allocation_policy);
1000   return value;
1001 }
1002 
WriteDataValue(Handle<Object> value,bool initializing_store)1003 void LookupIterator::WriteDataValue(Handle<Object> value,
1004                                     bool initializing_store) {
1005   DCHECK_EQ(DATA, state_);
1006   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
1007   if (IsElement(*holder)) {
1008     Handle<JSObject> object = Handle<JSObject>::cast(holder);
1009     ElementsAccessor* accessor = object->GetElementsAccessor(isolate_);
1010     accessor->Set(object, number_, *value);
1011   } else if (holder->HasFastProperties(isolate_)) {
1012     if (property_details_.location() == kField) {
1013       // Check that in case of VariableMode::kConst field the existing value is
1014       // equal to |value|.
1015       DCHECK_IMPLIES(!initializing_store && property_details_.constness() ==
1016                                                 PropertyConstness::kConst,
1017                      IsConstFieldValueEqualTo(*value));
1018       JSObject::cast(*holder).WriteToField(descriptor_number(),
1019                                            property_details_, *value);
1020     } else {
1021       DCHECK_EQ(kDescriptor, property_details_.location());
1022       DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
1023     }
1024   } else if (holder->IsJSGlobalObject(isolate_)) {
1025     GlobalDictionary dictionary =
1026         JSGlobalObject::cast(*holder).global_dictionary(isolate_);
1027     dictionary.CellAt(isolate_, dictionary_entry()).set_value(*value);
1028   } else {
1029     DCHECK_IMPLIES(holder->IsJSProxy(isolate_), name()->IsPrivate(isolate_));
1030     if (V8_DICT_MODE_PROTOTYPES_BOOL) {
1031       OrderedNameDictionary dictionary =
1032           holder->property_dictionary_ordered(isolate_);
1033       dictionary.ValueAtPut(dictionary_entry(), *value);
1034     } else {
1035       NameDictionary dictionary = holder->property_dictionary(isolate_);
1036       dictionary.ValueAtPut(dictionary_entry(), *value);
1037     }
1038   }
1039 }
1040 
1041 template <bool is_element>
SkipInterceptor(JSObject holder)1042 bool LookupIterator::SkipInterceptor(JSObject holder) {
1043   InterceptorInfo info = GetInterceptor<is_element>(holder);
1044   if (!is_element && name_->IsSymbol(isolate_) &&
1045       !info.can_intercept_symbols()) {
1046     return true;
1047   }
1048   if (info.non_masking()) {
1049     switch (interceptor_state_) {
1050       case InterceptorState::kUninitialized:
1051         interceptor_state_ = InterceptorState::kSkipNonMasking;
1052         V8_FALLTHROUGH;
1053       case InterceptorState::kSkipNonMasking:
1054         return true;
1055       case InterceptorState::kProcessNonMasking:
1056         return false;
1057     }
1058   }
1059   return interceptor_state_ == InterceptorState::kProcessNonMasking;
1060 }
1061 
NextHolder(Map map)1062 JSReceiver LookupIterator::NextHolder(Map map) {
1063   DisallowHeapAllocation no_gc;
1064   if (map.prototype(isolate_) == ReadOnlyRoots(isolate_).null_value()) {
1065     return JSReceiver();
1066   }
1067   if (!check_prototype_chain() && !map.IsJSGlobalProxyMap()) {
1068     return JSReceiver();
1069   }
1070   return JSReceiver::cast(map.prototype(isolate_));
1071 }
1072 
NotFound(JSReceiver const holder) const1073 LookupIterator::State LookupIterator::NotFound(JSReceiver const holder) const {
1074   if (!holder.IsJSTypedArray(isolate_)) return NOT_FOUND;
1075   if (IsElement()) return INTEGER_INDEXED_EXOTIC;
1076   if (!name_->IsString(isolate_)) return NOT_FOUND;
1077   return IsSpecialIndex(String::cast(*name_)) ? INTEGER_INDEXED_EXOTIC
1078                                               : NOT_FOUND;
1079 }
1080 
1081 namespace {
1082 
1083 template <bool is_element>
HasInterceptor(Map map,size_t index)1084 bool HasInterceptor(Map map, size_t index) {
1085   if (is_element) {
1086     if (index > JSArray::kMaxArrayIndex) {
1087       // There is currently no way to install interceptors on an object with
1088       // typed array elements.
1089       DCHECK(!map.has_typed_array_elements());
1090       return map.has_named_interceptor();
1091     }
1092     return map.has_indexed_interceptor();
1093   } else {
1094     return map.has_named_interceptor();
1095   }
1096 }
1097 
1098 }  // namespace
1099 
1100 template <bool is_element>
LookupInSpecialHolder(Map const map,JSReceiver const holder)1101 LookupIterator::State LookupIterator::LookupInSpecialHolder(
1102     Map const map, JSReceiver const holder) {
1103   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
1104   switch (state_) {
1105     case NOT_FOUND:
1106       if (map.IsJSProxyMap()) {
1107         if (is_element || !name_->IsPrivate(isolate_)) return JSPROXY;
1108       }
1109       if (map.is_access_check_needed()) {
1110         if (is_element || !name_->IsPrivate(isolate_)) return ACCESS_CHECK;
1111       }
1112       V8_FALLTHROUGH;
1113     case ACCESS_CHECK:
1114       if (check_interceptor() && HasInterceptor<is_element>(map, index_) &&
1115           !SkipInterceptor<is_element>(JSObject::cast(holder))) {
1116         if (is_element || !name_->IsPrivate(isolate_)) return INTERCEPTOR;
1117       }
1118       V8_FALLTHROUGH;
1119     case INTERCEPTOR:
1120       if (map.IsJSGlobalObjectMap() && !is_js_array_element(is_element)) {
1121         GlobalDictionary dict =
1122             JSGlobalObject::cast(holder).global_dictionary(isolate_);
1123         number_ = dict.FindEntry(isolate(), name_);
1124         if (number_.is_not_found()) return NOT_FOUND;
1125         PropertyCell cell = dict.CellAt(isolate_, number_);
1126         if (cell.value(isolate_).IsTheHole(isolate_)) return NOT_FOUND;
1127         property_details_ = cell.property_details();
1128         has_property_ = true;
1129         switch (property_details_.kind()) {
1130           case v8::internal::kData:
1131             return DATA;
1132           case v8::internal::kAccessor:
1133             return ACCESSOR;
1134         }
1135       }
1136       return LookupInRegularHolder<is_element>(map, holder);
1137     case ACCESSOR:
1138     case DATA:
1139       return NOT_FOUND;
1140     case INTEGER_INDEXED_EXOTIC:
1141     case JSPROXY:
1142     case TRANSITION:
1143       UNREACHABLE();
1144   }
1145   UNREACHABLE();
1146 }
1147 
1148 template <bool is_element>
LookupInRegularHolder(Map const map,JSReceiver const holder)1149 LookupIterator::State LookupIterator::LookupInRegularHolder(
1150     Map const map, JSReceiver const holder) {
1151   DisallowHeapAllocation no_gc;
1152   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
1153     return NOT_FOUND;
1154   }
1155 
1156   if (is_element && IsElement(holder)) {
1157     JSObject js_object = JSObject::cast(holder);
1158     ElementsAccessor* accessor = js_object.GetElementsAccessor(isolate_);
1159     FixedArrayBase backing_store = js_object.elements(isolate_);
1160     number_ =
1161         accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
1162     if (number_.is_not_found()) {
1163       return holder.IsJSTypedArray(isolate_) ? INTEGER_INDEXED_EXOTIC
1164                                              : NOT_FOUND;
1165     }
1166     property_details_ = accessor->GetDetails(js_object, number_);
1167     if (map.has_frozen_elements()) {
1168       property_details_ = property_details_.CopyAddAttributes(FROZEN);
1169     } else if (map.has_sealed_elements()) {
1170       property_details_ = property_details_.CopyAddAttributes(SEALED);
1171     }
1172   } else if (!map.is_dictionary_map()) {
1173     DescriptorArray descriptors =
1174         map.instance_descriptors(isolate_, kRelaxedLoad);
1175     number_ = descriptors.SearchWithCache(isolate_, *name_, map);
1176     if (number_.is_not_found()) return NotFound(holder);
1177     property_details_ = descriptors.GetDetails(number_);
1178   } else {
1179     DCHECK_IMPLIES(holder.IsJSProxy(isolate_), name()->IsPrivate(isolate_));
1180     if (V8_DICT_MODE_PROTOTYPES_BOOL) {
1181       OrderedNameDictionary dict = holder.property_dictionary_ordered(isolate_);
1182       number_ = dict.FindEntry(isolate(), *name_);
1183       if (number_.is_not_found()) return NotFound(holder);
1184       property_details_ = dict.DetailsAt(number_);
1185     } else {
1186       NameDictionary dict = holder.property_dictionary(isolate_);
1187       number_ = dict.FindEntry(isolate(), name_);
1188       if (number_.is_not_found()) return NotFound(holder);
1189       property_details_ = dict.DetailsAt(number_);
1190     }
1191   }
1192   has_property_ = true;
1193   switch (property_details_.kind()) {
1194     case v8::internal::kData:
1195       return DATA;
1196     case v8::internal::kAccessor:
1197       return ACCESSOR;
1198   }
1199 
1200   UNREACHABLE();
1201 }
1202 
GetInterceptorForFailedAccessCheck() const1203 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
1204     const {
1205   DCHECK_EQ(ACCESS_CHECK, state_);
1206   DisallowHeapAllocation no_gc;
1207   AccessCheckInfo access_check_info =
1208       AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
1209   if (!access_check_info.is_null()) {
1210     // There is currently no way to create objects with typed array elements
1211     // and access checks.
1212     DCHECK(!holder_->map().has_typed_array_elements());
1213     Object interceptor = is_js_array_element(IsElement())
1214                              ? access_check_info.indexed_interceptor()
1215                              : access_check_info.named_interceptor();
1216     if (interceptor != Object()) {
1217       return handle(InterceptorInfo::cast(interceptor), isolate_);
1218     }
1219   }
1220   return Handle<InterceptorInfo>();
1221 }
1222 
TryLookupCachedProperty()1223 bool LookupIterator::TryLookupCachedProperty() {
1224   return state() == LookupIterator::ACCESSOR &&
1225          GetAccessors()->IsAccessorPair(isolate_) && LookupCachedProperty();
1226 }
1227 
LookupCachedProperty()1228 bool LookupIterator::LookupCachedProperty() {
1229   DCHECK_EQ(state(), LookupIterator::ACCESSOR);
1230   DCHECK(GetAccessors()->IsAccessorPair(isolate_));
1231 
1232   AccessorPair accessor_pair = AccessorPair::cast(*GetAccessors());
1233   Handle<Object> getter(accessor_pair.getter(isolate_), isolate());
1234   MaybeHandle<Name> maybe_name =
1235       FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
1236   if (maybe_name.is_null()) return false;
1237 
1238   // We have found a cached property! Modify the iterator accordingly.
1239   name_ = maybe_name.ToHandleChecked();
1240   Restart();
1241   CHECK_EQ(state(), LookupIterator::DATA);
1242   return true;
1243 }
1244 
1245 }  // namespace internal
1246 }  // namespace v8
1247