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/lookup.h"
6 
7 #include "src/bootstrapper.h"
8 #include "src/deoptimizer.h"
9 #include "src/elements.h"
10 #include "src/field-type.h"
11 #include "src/isolate-inl.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 // static
PropertyOrElement(Isolate * isolate,Handle<Object> receiver,Handle<Object> key,bool * success,Configuration configuration)17 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
18                                                  Handle<Object> receiver,
19                                                  Handle<Object> key,
20                                                  bool* success,
21                                                  Configuration configuration) {
22   uint32_t index = 0;
23   if (key->ToArrayIndex(&index)) {
24     *success = true;
25     return LookupIterator(isolate, receiver, index, configuration);
26   }
27 
28   Handle<Name> name;
29   *success = Object::ToName(isolate, key).ToHandle(&name);
30   if (!*success) {
31     DCHECK(isolate->has_pending_exception());
32     // Return an unusable dummy.
33     return LookupIterator(receiver, isolate->factory()->empty_string());
34   }
35 
36   if (name->AsArrayIndex(&index)) {
37     LookupIterator it(isolate, receiver, index, configuration);
38     // Here we try to avoid having to rebuild the string later
39     // by storing it on the indexed LookupIterator.
40     it.name_ = name;
41     return it;
42   }
43 
44   return LookupIterator(receiver, name, configuration);
45 }
46 
47 template <bool is_element>
Start()48 void LookupIterator::Start() {
49   DisallowHeapAllocation no_gc;
50 
51   has_property_ = false;
52   state_ = NOT_FOUND;
53   holder_ = initial_holder_;
54 
55   JSReceiver* holder = *holder_;
56   Map* map = holder->map();
57 
58   state_ = LookupInHolder<is_element>(map, holder);
59   if (IsFound()) return;
60 
61   NextInternal<is_element>(map, holder);
62 }
63 
64 template void LookupIterator::Start<true>();
65 template void LookupIterator::Start<false>();
66 
Next()67 void LookupIterator::Next() {
68   DCHECK_NE(JSPROXY, state_);
69   DCHECK_NE(TRANSITION, state_);
70   DisallowHeapAllocation no_gc;
71   has_property_ = false;
72 
73   JSReceiver* holder = *holder_;
74   Map* map = holder->map();
75 
76   if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
77     state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
78                          : LookupInSpecialHolder<false>(map, holder);
79     if (IsFound()) return;
80   }
81 
82   IsElement() ? NextInternal<true>(map, holder)
83               : NextInternal<false>(map, holder);
84 }
85 
86 template <bool is_element>
NextInternal(Map * map,JSReceiver * holder)87 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
88   do {
89     JSReceiver* maybe_holder = NextHolder(map);
90     if (maybe_holder == nullptr) {
91       if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
92         RestartLookupForNonMaskingInterceptors<is_element>();
93         return;
94       }
95       state_ = NOT_FOUND;
96       if (holder != *holder_) holder_ = handle(holder, isolate_);
97       return;
98     }
99     holder = maybe_holder;
100     map = holder->map();
101     state_ = LookupInHolder<is_element>(map, holder);
102   } while (!IsFound());
103 
104   holder_ = handle(holder, isolate_);
105 }
106 
107 template <bool is_element>
RestartInternal(InterceptorState interceptor_state)108 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
109   interceptor_state_ = interceptor_state;
110   property_details_ = PropertyDetails::Empty();
111   number_ = DescriptorArray::kNotFound;
112   Start<is_element>();
113 }
114 
115 template void LookupIterator::RestartInternal<true>(InterceptorState);
116 template void LookupIterator::RestartInternal<false>(InterceptorState);
117 
118 // static
GetRootForNonJSReceiver(Isolate * isolate,Handle<Object> receiver,uint32_t index)119 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
120     Isolate* isolate, Handle<Object> receiver, uint32_t index) {
121   // Strings are the only objects with properties (only elements) directly on
122   // the wrapper. Hence we can skip generating the wrapper for all other cases.
123   if (index != kMaxUInt32 && receiver->IsString() &&
124       index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
125     // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
126     // context, ensuring that we don't leak it into JS?
127     Handle<JSFunction> constructor = isolate->string_function();
128     Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
129     Handle<JSValue>::cast(result)->set_value(*receiver);
130     return result;
131   }
132   auto root =
133       handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
134   if (root->IsNull(isolate)) {
135     unsigned int magic = 0xbbbbbbbb;
136     isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
137   }
138   return Handle<JSReceiver>::cast(root);
139 }
140 
141 
GetReceiverMap() const142 Handle<Map> LookupIterator::GetReceiverMap() const {
143   if (receiver_->IsNumber()) return factory()->heap_number_map();
144   return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
145 }
146 
HasAccess() const147 bool LookupIterator::HasAccess() const {
148   DCHECK_EQ(ACCESS_CHECK, state_);
149   return isolate_->MayAccess(handle(isolate_->context()),
150                              GetHolder<JSObject>());
151 }
152 
153 template <bool is_element>
ReloadPropertyInformation()154 void LookupIterator::ReloadPropertyInformation() {
155   state_ = BEFORE_PROPERTY;
156   interceptor_state_ = InterceptorState::kUninitialized;
157   state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
158   DCHECK(IsFound() || !holder_->HasFastProperties());
159 }
160 
InternalUpdateProtector()161 void LookupIterator::InternalUpdateProtector() {
162   if (isolate_->bootstrapper()->IsActive()) return;
163 
164   if (*name_ == heap()->constructor_string()) {
165     if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
166     // Setting the constructor property could change an instance's @@species
167     if (holder_->IsJSArray()) {
168       isolate_->CountUsage(
169           v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
170       isolate_->InvalidateArraySpeciesProtector();
171     } else if (holder_->map()->is_prototype_map()) {
172       DisallowHeapAllocation no_gc;
173       // Setting the constructor of Array.prototype of any realm also needs
174       // to invalidate the species protector
175       if (isolate_->IsInAnyContext(*holder_,
176                                    Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
177         isolate_->CountUsage(v8::Isolate::UseCounterFeature::
178                                  kArrayPrototypeConstructorModified);
179         isolate_->InvalidateArraySpeciesProtector();
180       }
181     }
182   } else if (*name_ == heap()->species_symbol()) {
183     if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
184     // Setting the Symbol.species property of any Array constructor invalidates
185     // the species protector
186     if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
187       isolate_->CountUsage(
188           v8::Isolate::UseCounterFeature::kArraySpeciesModified);
189       isolate_->InvalidateArraySpeciesProtector();
190     }
191   } else if (*name_ == heap()->is_concat_spreadable_symbol()) {
192     if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
193     isolate_->InvalidateIsConcatSpreadableProtector();
194   } else if (*name_ == heap()->has_instance_symbol()) {
195     if (!isolate_->IsHasInstanceLookupChainIntact()) return;
196     isolate_->InvalidateHasInstanceProtector();
197   } else if (*name_ == heap()->iterator_symbol()) {
198     if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
199     if (holder_->IsJSArray()) {
200       isolate_->InvalidateArrayIteratorProtector();
201     }
202   }
203 }
204 
PrepareForDataProperty(Handle<Object> value)205 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
206   DCHECK(state_ == DATA || state_ == ACCESSOR);
207   DCHECK(HolderIsReceiverOrHiddenPrototype());
208 
209   Handle<JSObject> holder = GetHolder<JSObject>();
210 
211   if (IsElement()) {
212     ElementsKind kind = holder->GetElementsKind();
213     ElementsKind to = value->OptimalElementsKind();
214     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
215     to = GetMoreGeneralElementsKind(kind, to);
216 
217     if (kind != to) {
218       JSObject::TransitionElementsKind(holder, to);
219     }
220 
221     // Copy the backing store if it is copy-on-write.
222     if (IsFastSmiOrObjectElementsKind(to)) {
223       JSObject::EnsureWritableFastElements(holder);
224     }
225     return;
226   }
227 
228   if (holder->IsJSGlobalObject()) {
229     Handle<GlobalDictionary> dictionary(holder->global_dictionary());
230     Handle<PropertyCell> cell(
231         PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
232     DCHECK(!cell->IsTheHole(isolate_));
233     property_details_ = cell->property_details();
234     PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
235                                   property_details_);
236     return;
237   }
238   if (!holder->HasFastProperties()) return;
239 
240   Handle<Map> old_map(holder->map(), isolate_);
241   Handle<Map> new_map =
242       Map::PrepareForDataProperty(old_map, descriptor_number(), value);
243 
244   if (old_map.is_identical_to(new_map)) {
245     // Update the property details if the representation was None.
246     if (representation().IsNone()) {
247       property_details_ =
248           new_map->instance_descriptors()->GetDetails(descriptor_number());
249     }
250     return;
251   }
252 
253   JSObject::MigrateToMap(holder, new_map);
254   ReloadPropertyInformation<false>();
255 }
256 
257 
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)258 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
259                                              PropertyAttributes attributes) {
260   DCHECK(state_ == DATA || state_ == ACCESSOR);
261   DCHECK(HolderIsReceiverOrHiddenPrototype());
262   Handle<JSObject> holder = GetHolder<JSObject>();
263   if (IsElement()) {
264     DCHECK(!holder->HasFixedTypedArrayElements());
265     DCHECK(attributes != NONE || !holder->HasFastElements());
266     Handle<FixedArrayBase> elements(holder->elements());
267     holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
268                                                attributes);
269     ReloadPropertyInformation<true>();
270   } else if (holder->HasFastProperties()) {
271     Handle<Map> old_map(holder->map(), isolate_);
272     Handle<Map> new_map = Map::ReconfigureExistingProperty(
273         old_map, descriptor_number(), i::kData, attributes);
274     new_map = Map::PrepareForDataProperty(new_map, descriptor_number(), value);
275     JSObject::MigrateToMap(holder, new_map);
276     ReloadPropertyInformation<false>();
277   } else {
278     PropertyDetails details(attributes, v8::internal::DATA, 0,
279                             PropertyCellType::kMutable);
280     if (holder->IsJSGlobalObject()) {
281       Handle<GlobalDictionary> dictionary(holder->global_dictionary());
282 
283       Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
284           dictionary, dictionary_entry(), value, details);
285       cell->set_value(*value);
286       property_details_ = cell->property_details();
287     } else {
288       Handle<NameDictionary> dictionary(holder->property_dictionary());
289       PropertyDetails original_details =
290           dictionary->DetailsAt(dictionary_entry());
291       int enumeration_index = original_details.dictionary_index();
292       DCHECK(enumeration_index > 0);
293       details = details.set_index(enumeration_index);
294       dictionary->SetEntry(dictionary_entry(), name(), value, details);
295       property_details_ = details;
296     }
297     state_ = DATA;
298   }
299 
300   WriteDataValue(value);
301 
302 #if VERIFY_HEAP
303   if (FLAG_verify_heap) {
304     holder->JSObjectVerify();
305   }
306 #endif
307 }
308 
309 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
310 // via a trap. Adding properties to primitive values is not observable.
PrepareTransitionToDataProperty(Handle<JSObject> receiver,Handle<Object> value,PropertyAttributes attributes,Object::StoreFromKeyed store_mode)311 void LookupIterator::PrepareTransitionToDataProperty(
312     Handle<JSObject> receiver, Handle<Object> value,
313     PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
314   DCHECK(receiver.is_identical_to(GetStoreTarget()));
315   if (state_ == TRANSITION) return;
316 
317   if (!IsElement() && name()->IsPrivate()) {
318     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
319   }
320 
321   DCHECK(state_ != LookupIterator::ACCESSOR ||
322          (GetAccessors()->IsAccessorInfo() &&
323           AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
324   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
325   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
326 
327   Handle<Map> map(receiver->map(), isolate_);
328 
329   // Dictionary maps can always have additional data properties.
330   if (map->is_dictionary_map()) {
331     state_ = TRANSITION;
332     if (map->IsJSGlobalObjectMap()) {
333       // Install a property cell.
334       Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
335       int entry;
336       Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
337           global, name(), PropertyCellType::kUninitialized, &entry);
338       Handle<GlobalDictionary> dictionary(global->global_dictionary(),
339                                           isolate_);
340       DCHECK(cell->value()->IsTheHole(isolate_));
341       DCHECK(!value->IsTheHole(isolate_));
342       transition_ = cell;
343       // Assign an enumeration index to the property and update
344       // SetNextEnumerationIndex.
345       int index = dictionary->NextEnumerationIndex();
346       dictionary->SetNextEnumerationIndex(index + 1);
347       property_details_ = PropertyDetails(attributes, i::DATA, index,
348                                           PropertyCellType::kUninitialized);
349       PropertyCellType new_type =
350           PropertyCell::UpdatedType(cell, value, property_details_);
351       property_details_ = property_details_.set_cell_type(new_type);
352       cell->set_property_details(property_details_);
353       number_ = entry;
354       has_property_ = true;
355     } else {
356       // Don't set enumeration index (it will be set during value store).
357       property_details_ =
358           PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
359       transition_ = map;
360     }
361     return;
362   }
363 
364   Handle<Map> transition =
365       Map::TransitionToDataProperty(map, name_, value, attributes, store_mode);
366   state_ = TRANSITION;
367   transition_ = transition;
368 
369   if (transition->is_dictionary_map()) {
370     // Don't set enumeration index (it will be set during value store).
371     property_details_ =
372         PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
373   } else {
374     property_details_ = transition->GetLastDescriptorDetails();
375     has_property_ = true;
376   }
377 }
378 
ApplyTransitionToDataProperty(Handle<JSObject> receiver)379 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
380   DCHECK_EQ(TRANSITION, state_);
381 
382   DCHECK(receiver.is_identical_to(GetStoreTarget()));
383   holder_ = receiver;
384   if (receiver->IsJSGlobalObject()) {
385     state_ = DATA;
386     return;
387   }
388   Handle<Map> transition = transition_map();
389   bool simple_transition = transition->GetBackPointer() == receiver->map();
390   JSObject::MigrateToMap(receiver, transition);
391 
392   if (simple_transition) {
393     int number = transition->LastAdded();
394     number_ = static_cast<uint32_t>(number);
395     property_details_ = transition->GetLastDescriptorDetails();
396     state_ = DATA;
397   } else if (receiver->map()->is_dictionary_map()) {
398     Handle<NameDictionary> dictionary(receiver->property_dictionary(),
399                                       isolate_);
400     int entry;
401     dictionary = NameDictionary::Add(dictionary, name(),
402                                      isolate_->factory()->uninitialized_value(),
403                                      property_details_, &entry);
404     receiver->set_properties(*dictionary);
405     // Reload details containing proper enumeration index value.
406     property_details_ = dictionary->DetailsAt(entry);
407     number_ = entry;
408     has_property_ = true;
409     state_ = DATA;
410 
411   } else {
412     ReloadPropertyInformation<false>();
413   }
414 }
415 
416 
Delete()417 void LookupIterator::Delete() {
418   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
419   if (IsElement()) {
420     Handle<JSObject> object = Handle<JSObject>::cast(holder);
421     ElementsAccessor* accessor = object->GetElementsAccessor();
422     accessor->Delete(object, number_);
423   } else {
424     bool is_prototype_map = holder->map()->is_prototype_map();
425     RuntimeCallTimerScope stats_scope(
426         isolate_, is_prototype_map
427                       ? &RuntimeCallStats::PrototypeObject_DeleteProperty
428                       : &RuntimeCallStats::Object_DeleteProperty);
429 
430     PropertyNormalizationMode mode =
431         is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
432 
433     if (holder->HasFastProperties()) {
434       JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
435                                     "DeletingProperty");
436       ReloadPropertyInformation<false>();
437     }
438     // TODO(verwaest): Get rid of the name_ argument.
439     JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
440     if (holder->IsJSObject()) {
441       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
442     }
443   }
444   state_ = NOT_FOUND;
445 }
446 
TransitionToAccessorProperty(Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)447 void LookupIterator::TransitionToAccessorProperty(
448     Handle<Object> getter, Handle<Object> setter,
449     PropertyAttributes attributes) {
450   DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
451   // Can only be called when the receiver is a JSObject. JSProxy has to be
452   // handled via a trap. Adding properties to primitive values is not
453   // observable.
454   Handle<JSObject> receiver = GetStoreTarget();
455   if (!IsElement() && name()->IsPrivate()) {
456     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
457   }
458 
459   if (!IsElement() && !receiver->map()->is_dictionary_map()) {
460     Handle<Map> old_map(receiver->map(), isolate_);
461 
462     if (!holder_.is_identical_to(receiver)) {
463       holder_ = receiver;
464       state_ = NOT_FOUND;
465     } else if (state_ == INTERCEPTOR) {
466       LookupInRegularHolder<false>(*old_map, *holder_);
467     }
468     int descriptor =
469         IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
470 
471     Handle<Map> new_map = Map::TransitionToAccessorProperty(
472         isolate_, old_map, name_, descriptor, getter, setter, attributes);
473     bool simple_transition = new_map->GetBackPointer() == receiver->map();
474     JSObject::MigrateToMap(receiver, new_map);
475 
476     if (simple_transition) {
477       int number = new_map->LastAdded();
478       number_ = static_cast<uint32_t>(number);
479       property_details_ = new_map->GetLastDescriptorDetails();
480       state_ = ACCESSOR;
481       return;
482     }
483 
484     ReloadPropertyInformation<false>();
485     if (!new_map->is_dictionary_map()) return;
486   }
487 
488   Handle<AccessorPair> pair;
489   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
490     pair = Handle<AccessorPair>::cast(GetAccessors());
491     // If the component and attributes are identical, nothing has to be done.
492     if (pair->Equals(*getter, *setter)) {
493       if (property_details().attributes() == attributes) {
494         if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
495         return;
496       }
497     } else {
498       pair = AccessorPair::Copy(pair);
499       pair->SetComponents(*getter, *setter);
500     }
501   } else {
502     pair = factory()->NewAccessorPair();
503     pair->SetComponents(*getter, *setter);
504   }
505 
506   TransitionToAccessorPair(pair, attributes);
507 
508 #if VERIFY_HEAP
509   if (FLAG_verify_heap) {
510     receiver->JSObjectVerify();
511   }
512 #endif
513 }
514 
515 
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)516 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
517                                               PropertyAttributes attributes) {
518   Handle<JSObject> receiver = GetStoreTarget();
519   holder_ = receiver;
520 
521   PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
522                           PropertyCellType::kMutable);
523 
524   if (IsElement()) {
525     // TODO(verwaest): Move code into the element accessor.
526     Handle<SeededNumberDictionary> dictionary =
527         JSObject::NormalizeElements(receiver);
528 
529     // We unconditionally pass used_as_prototype=false here because the call
530     // to RequireSlowElements takes care of the required IC clearing and
531     // we don't want to walk the heap twice.
532     dictionary =
533         SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
534     receiver->RequireSlowElements(*dictionary);
535 
536     if (receiver->HasSlowArgumentsElements()) {
537       FixedArray* parameter_map = FixedArray::cast(receiver->elements());
538       uint32_t length = parameter_map->length() - 2;
539       if (number_ < length) {
540         parameter_map->set(number_ + 2, heap()->the_hole_value());
541       }
542       FixedArray::cast(receiver->elements())->set(1, *dictionary);
543     } else {
544       receiver->set_elements(*dictionary);
545     }
546 
547     ReloadPropertyInformation<true>();
548   } else {
549     PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
550                                          ? KEEP_INOBJECT_PROPERTIES
551                                          : CLEAR_INOBJECT_PROPERTIES;
552     // Normalize object to make this operation simple.
553     JSObject::NormalizeProperties(receiver, mode, 0,
554                                   "TransitionToAccessorPair");
555 
556     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
557     JSObject::ReoptimizeIfPrototype(receiver);
558 
559     ReloadPropertyInformation<false>();
560   }
561 }
562 
563 
HolderIsReceiverOrHiddenPrototype() const564 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
565   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
566   // Optimization that only works if configuration_ is not mutable.
567   if (!check_prototype_chain()) return true;
568   DisallowHeapAllocation no_gc;
569   if (*receiver_ == *holder_) return true;
570   if (!receiver_->IsJSReceiver()) return false;
571   JSReceiver* current = JSReceiver::cast(*receiver_);
572   JSReceiver* object = *holder_;
573   if (!current->map()->has_hidden_prototype()) return false;
574   // JSProxy do not occur as hidden prototypes.
575   if (object->IsJSProxy()) return false;
576   PrototypeIterator iter(isolate(), current, kStartAtPrototype,
577                          PrototypeIterator::END_AT_NON_HIDDEN);
578   while (!iter.IsAtEnd()) {
579     if (iter.GetCurrent<JSReceiver>() == object) return true;
580     iter.Advance();
581   }
582   return false;
583 }
584 
585 
FetchValue() const586 Handle<Object> LookupIterator::FetchValue() const {
587   Object* result = NULL;
588   if (IsElement()) {
589     Handle<JSObject> holder = GetHolder<JSObject>();
590     ElementsAccessor* accessor = holder->GetElementsAccessor();
591     return accessor->Get(holder, number_);
592   } else if (holder_->IsJSGlobalObject()) {
593     Handle<JSObject> holder = GetHolder<JSObject>();
594     result = holder->global_dictionary()->ValueAt(number_);
595     DCHECK(result->IsPropertyCell());
596     result = PropertyCell::cast(result)->value();
597   } else if (!holder_->HasFastProperties()) {
598     result = holder_->property_dictionary()->ValueAt(number_);
599   } else if (property_details_.type() == v8::internal::DATA) {
600     Handle<JSObject> holder = GetHolder<JSObject>();
601     FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
602     return JSObject::FastPropertyAt(holder, property_details_.representation(),
603                                     field_index);
604   } else {
605     result = holder_->map()->instance_descriptors()->GetValue(number_);
606   }
607   return handle(result, isolate_);
608 }
609 
GetFieldDescriptorIndex() const610 int LookupIterator::GetFieldDescriptorIndex() const {
611   DCHECK(has_property_);
612   DCHECK(holder_->HasFastProperties());
613   DCHECK_EQ(v8::internal::DATA, property_details_.type());
614   return descriptor_number();
615 }
616 
GetAccessorIndex() const617 int LookupIterator::GetAccessorIndex() const {
618   DCHECK(has_property_);
619   DCHECK(holder_->HasFastProperties());
620   DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
621   return descriptor_number();
622 }
623 
624 
GetConstantIndex() const625 int LookupIterator::GetConstantIndex() const {
626   DCHECK(has_property_);
627   DCHECK(holder_->HasFastProperties());
628   DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
629   DCHECK(!IsElement());
630   return descriptor_number();
631 }
632 
633 
GetFieldIndex() const634 FieldIndex LookupIterator::GetFieldIndex() const {
635   DCHECK(has_property_);
636   DCHECK(holder_->HasFastProperties());
637   DCHECK_EQ(v8::internal::DATA, property_details_.type());
638   DCHECK(!IsElement());
639   Map* holder_map = holder_->map();
640   int index =
641       holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
642   bool is_double = representation().IsDouble();
643   return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
644 }
645 
GetFieldType() const646 Handle<FieldType> LookupIterator::GetFieldType() const {
647   DCHECK(has_property_);
648   DCHECK(holder_->HasFastProperties());
649   DCHECK_EQ(v8::internal::DATA, property_details_.type());
650   return handle(
651       holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
652       isolate_);
653 }
654 
655 
GetPropertyCell() const656 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
657   DCHECK(!IsElement());
658   Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
659   Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
660   DCHECK(value->IsPropertyCell());
661   return handle(PropertyCell::cast(value), isolate_);
662 }
663 
664 
GetAccessors() const665 Handle<Object> LookupIterator::GetAccessors() const {
666   DCHECK_EQ(ACCESSOR, state_);
667   return FetchValue();
668 }
669 
670 
GetDataValue() const671 Handle<Object> LookupIterator::GetDataValue() const {
672   DCHECK_EQ(DATA, state_);
673   Handle<Object> value = FetchValue();
674   return value;
675 }
676 
677 
WriteDataValue(Handle<Object> value)678 void LookupIterator::WriteDataValue(Handle<Object> value) {
679   DCHECK_EQ(DATA, state_);
680   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
681   if (IsElement()) {
682     Handle<JSObject> object = Handle<JSObject>::cast(holder);
683     ElementsAccessor* accessor = object->GetElementsAccessor();
684     accessor->Set(object, number_, *value);
685   } else if (holder->HasFastProperties()) {
686     if (property_details_.type() == v8::internal::DATA) {
687       JSObject::cast(*holder)->WriteToField(descriptor_number(),
688                                             property_details_, *value);
689     } else {
690       DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
691     }
692   } else if (holder->IsJSGlobalObject()) {
693     GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
694     Object* cell = dictionary->ValueAt(dictionary_entry());
695     DCHECK(cell->IsPropertyCell());
696     PropertyCell::cast(cell)->set_value(*value);
697   } else {
698     NameDictionary* dictionary = holder->property_dictionary();
699     dictionary->ValueAtPut(dictionary_entry(), *value);
700   }
701 }
702 
703 template <bool is_element>
SkipInterceptor(JSObject * holder)704 bool LookupIterator::SkipInterceptor(JSObject* holder) {
705   auto info = GetInterceptor<is_element>(holder);
706   // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
707   if (info->non_masking()) {
708     switch (interceptor_state_) {
709       case InterceptorState::kUninitialized:
710         interceptor_state_ = InterceptorState::kSkipNonMasking;
711       // Fall through.
712       case InterceptorState::kSkipNonMasking:
713         return true;
714       case InterceptorState::kProcessNonMasking:
715         return false;
716     }
717   }
718   return interceptor_state_ == InterceptorState::kProcessNonMasking;
719 }
720 
NextHolder(Map * map)721 JSReceiver* LookupIterator::NextHolder(Map* map) {
722   DisallowHeapAllocation no_gc;
723   if (map->prototype() == heap()->null_value()) return NULL;
724   if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
725   return JSReceiver::cast(map->prototype());
726 }
727 
NotFound(JSReceiver * const holder) const728 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
729   DCHECK(!IsElement());
730   if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
731 
732   Handle<String> name_string = Handle<String>::cast(name_);
733   if (name_string->length() == 0) return NOT_FOUND;
734 
735   return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
736              ? INTEGER_INDEXED_EXOTIC
737              : NOT_FOUND;
738 }
739 
740 namespace {
741 
742 template <bool is_element>
HasInterceptor(Map * map)743 bool HasInterceptor(Map* map) {
744   return is_element ? map->has_indexed_interceptor()
745                     : map->has_named_interceptor();
746 }
747 
748 }  // namespace
749 
750 template <bool is_element>
LookupInSpecialHolder(Map * const map,JSReceiver * const holder)751 LookupIterator::State LookupIterator::LookupInSpecialHolder(
752     Map* const map, JSReceiver* const holder) {
753   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
754   switch (state_) {
755     case NOT_FOUND:
756       if (map->IsJSProxyMap()) {
757         if (is_element || !name_->IsPrivate()) return JSPROXY;
758       }
759       if (map->is_access_check_needed()) {
760         if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
761       }
762     // Fall through.
763     case ACCESS_CHECK:
764       if (check_interceptor() && HasInterceptor<is_element>(map) &&
765           !SkipInterceptor<is_element>(JSObject::cast(holder))) {
766         if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
767       }
768     // Fall through.
769     case INTERCEPTOR:
770       if (!is_element && map->IsJSGlobalObjectMap()) {
771         GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
772         int number = dict->FindEntry(name_);
773         if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
774         number_ = static_cast<uint32_t>(number);
775         DCHECK(dict->ValueAt(number_)->IsPropertyCell());
776         PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
777         if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
778         property_details_ = cell->property_details();
779         has_property_ = true;
780         switch (property_details_.kind()) {
781           case v8::internal::kData:
782             return DATA;
783           case v8::internal::kAccessor:
784             return ACCESSOR;
785         }
786       }
787       return LookupInRegularHolder<is_element>(map, holder);
788     case ACCESSOR:
789     case DATA:
790       return NOT_FOUND;
791     case INTEGER_INDEXED_EXOTIC:
792     case JSPROXY:
793     case TRANSITION:
794       UNREACHABLE();
795   }
796   UNREACHABLE();
797   return NOT_FOUND;
798 }
799 
800 template <bool is_element>
LookupInRegularHolder(Map * const map,JSReceiver * const holder)801 LookupIterator::State LookupIterator::LookupInRegularHolder(
802     Map* const map, JSReceiver* const holder) {
803   DisallowHeapAllocation no_gc;
804   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
805     return NOT_FOUND;
806   }
807 
808   if (is_element) {
809     JSObject* js_object = JSObject::cast(holder);
810     ElementsAccessor* accessor = js_object->GetElementsAccessor();
811     FixedArrayBase* backing_store = js_object->elements();
812     number_ =
813         accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
814     if (number_ == kMaxUInt32) {
815       return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
816     }
817     property_details_ = accessor->GetDetails(js_object, number_);
818   } else if (!map->is_dictionary_map()) {
819     DescriptorArray* descriptors = map->instance_descriptors();
820     int number = descriptors->SearchWithCache(isolate_, *name_, map);
821     if (number == DescriptorArray::kNotFound) return NotFound(holder);
822     number_ = static_cast<uint32_t>(number);
823     property_details_ = descriptors->GetDetails(number_);
824   } else {
825     NameDictionary* dict = holder->property_dictionary();
826     int number = dict->FindEntry(name_);
827     if (number == NameDictionary::kNotFound) return NotFound(holder);
828     number_ = static_cast<uint32_t>(number);
829     property_details_ = dict->DetailsAt(number_);
830   }
831   has_property_ = true;
832   switch (property_details_.kind()) {
833     case v8::internal::kData:
834       return DATA;
835     case v8::internal::kAccessor:
836       return ACCESSOR;
837   }
838 
839   UNREACHABLE();
840   return state_;
841 }
842 
GetInterceptorForFailedAccessCheck() const843 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
844     const {
845   DCHECK_EQ(ACCESS_CHECK, state_);
846   DisallowHeapAllocation no_gc;
847   AccessCheckInfo* access_check_info =
848       AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
849   if (access_check_info) {
850     Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
851                                       : access_check_info->named_interceptor();
852     if (interceptor) {
853       return handle(InterceptorInfo::cast(interceptor), isolate_);
854     }
855   }
856   return Handle<InterceptorInfo>();
857 }
858 
TryLookupCachedProperty()859 bool LookupIterator::TryLookupCachedProperty() {
860   return state() == LookupIterator::ACCESSOR &&
861          GetAccessors()->IsAccessorPair() && LookupCachedProperty();
862 }
863 
LookupCachedProperty()864 bool LookupIterator::LookupCachedProperty() {
865   DCHECK_EQ(state(), LookupIterator::ACCESSOR);
866   DCHECK(GetAccessors()->IsAccessorPair());
867 
868   AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
869   Handle<Object> getter(accessor_pair->getter(), isolate());
870   MaybeHandle<Name> maybe_name =
871       FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
872   if (maybe_name.is_null()) return false;
873 
874   // We have found a cached property! Modify the iterator accordingly.
875   name_ = maybe_name.ToHandleChecked();
876   Restart();
877   CHECK_EQ(state(), LookupIterator::DATA);
878   return true;
879 }
880 
881 }  // namespace internal
882 }  // namespace v8
883