• 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/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->IsSpecialReceiverMap()) {
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()->iterator_symbol()) {
195     if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
196     if (holder_->IsJSArray()) {
197       isolate_->InvalidateArrayIteratorProtector();
198     }
199   }
200 }
201 
PrepareForDataProperty(Handle<Object> value)202 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
203   DCHECK(state_ == DATA || state_ == ACCESSOR);
204   DCHECK(HolderIsReceiverOrHiddenPrototype());
205 
206   Handle<JSObject> holder = GetHolder<JSObject>();
207 
208   if (IsElement()) {
209     ElementsKind kind = holder->GetElementsKind();
210     ElementsKind to = value->OptimalElementsKind();
211     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
212     to = GetMoreGeneralElementsKind(kind, to);
213 
214     if (kind != to) {
215       JSObject::TransitionElementsKind(holder, to);
216     }
217 
218     // Copy the backing store if it is copy-on-write.
219     if (IsFastSmiOrObjectElementsKind(to)) {
220       JSObject::EnsureWritableFastElements(holder);
221     }
222     return;
223   }
224 
225   if (holder->IsJSGlobalObject()) {
226     Handle<GlobalDictionary> dictionary(holder->global_dictionary());
227     Handle<PropertyCell> cell(
228         PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
229     DCHECK(!cell->IsTheHole(isolate_));
230     property_details_ = cell->property_details();
231     PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
232                                   property_details_);
233     return;
234   }
235   if (!holder->HasFastProperties()) return;
236 
237   PropertyConstness new_constness = kConst;
238   if (FLAG_track_constant_fields) {
239     if (constness() == kConst) {
240       DCHECK_EQ(kData, property_details_.kind());
241       // Check that current value matches new value otherwise we should make
242       // the property mutable.
243       if (!IsConstFieldValueEqualTo(*value)) new_constness = kMutable;
244     }
245   } else {
246     new_constness = kMutable;
247   }
248 
249   Handle<Map> old_map(holder->map(), isolate_);
250   Handle<Map> new_map = Map::PrepareForDataProperty(
251       old_map, descriptor_number(), new_constness, value);
252 
253   if (old_map.is_identical_to(new_map)) {
254     // Update the property details if the representation was None.
255     if (representation().IsNone()) {
256       property_details_ =
257           new_map->instance_descriptors()->GetDetails(descriptor_number());
258     }
259     return;
260   }
261 
262   JSObject::MigrateToMap(holder, new_map);
263   ReloadPropertyInformation<false>();
264 }
265 
266 
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)267 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
268                                              PropertyAttributes attributes) {
269   DCHECK(state_ == DATA || state_ == ACCESSOR);
270   DCHECK(HolderIsReceiverOrHiddenPrototype());
271   Handle<JSObject> holder = GetHolder<JSObject>();
272   if (IsElement()) {
273     DCHECK(!holder->HasFixedTypedArrayElements());
274     DCHECK(attributes != NONE || !holder->HasFastElements());
275     Handle<FixedArrayBase> elements(holder->elements());
276     holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
277                                                attributes);
278     ReloadPropertyInformation<true>();
279   } else if (holder->HasFastProperties()) {
280     Handle<Map> old_map(holder->map(), isolate_);
281     Handle<Map> new_map = Map::ReconfigureExistingProperty(
282         old_map, descriptor_number(), i::kData, attributes);
283     // Force mutable to avoid changing constant value by reconfiguring
284     // kData -> kAccessor -> kData.
285     new_map = Map::PrepareForDataProperty(new_map, descriptor_number(),
286                                           kMutable, value);
287     JSObject::MigrateToMap(holder, new_map);
288     ReloadPropertyInformation<false>();
289   } else {
290     PropertyDetails details(kData, attributes, 0, PropertyCellType::kMutable);
291     if (holder->IsJSGlobalObject()) {
292       Handle<GlobalDictionary> dictionary(holder->global_dictionary());
293 
294       Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
295           dictionary, dictionary_entry(), value, details);
296       cell->set_value(*value);
297       property_details_ = cell->property_details();
298     } else {
299       Handle<NameDictionary> dictionary(holder->property_dictionary());
300       PropertyDetails original_details =
301           dictionary->DetailsAt(dictionary_entry());
302       int enumeration_index = original_details.dictionary_index();
303       DCHECK(enumeration_index > 0);
304       details = details.set_index(enumeration_index);
305       dictionary->SetEntry(dictionary_entry(), name(), value, details);
306       property_details_ = details;
307     }
308     state_ = DATA;
309   }
310 
311   WriteDataValue(value, true);
312 
313 #if VERIFY_HEAP
314   if (FLAG_verify_heap) {
315     holder->JSObjectVerify();
316   }
317 #endif
318 }
319 
320 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
321 // via a trap. Adding properties to primitive values is not observable.
PrepareTransitionToDataProperty(Handle<JSObject> receiver,Handle<Object> value,PropertyAttributes attributes,Object::StoreFromKeyed store_mode)322 void LookupIterator::PrepareTransitionToDataProperty(
323     Handle<JSObject> receiver, Handle<Object> value,
324     PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
325   DCHECK(receiver.is_identical_to(GetStoreTarget()));
326   if (state_ == TRANSITION) return;
327 
328   if (!IsElement() && name()->IsPrivate()) {
329     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
330   }
331 
332   DCHECK(state_ != LookupIterator::ACCESSOR ||
333          (GetAccessors()->IsAccessorInfo() &&
334           AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
335   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
336   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
337 
338   Handle<Map> map(receiver->map(), isolate_);
339 
340   // Dictionary maps can always have additional data properties.
341   if (map->is_dictionary_map()) {
342     state_ = TRANSITION;
343     if (map->IsJSGlobalObjectMap()) {
344       // Install a property cell.
345       Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
346       int entry;
347       Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
348           global, name(), PropertyCellType::kUninitialized, &entry);
349       Handle<GlobalDictionary> dictionary(global->global_dictionary(),
350                                           isolate_);
351       DCHECK(cell->value()->IsTheHole(isolate_));
352       DCHECK(!value->IsTheHole(isolate_));
353       transition_ = cell;
354       // Assign an enumeration index to the property and update
355       // SetNextEnumerationIndex.
356       int index = dictionary->NextEnumerationIndex();
357       dictionary->SetNextEnumerationIndex(index + 1);
358       property_details_ = PropertyDetails(kData, attributes, index,
359                                           PropertyCellType::kUninitialized);
360       PropertyCellType new_type =
361           PropertyCell::UpdatedType(cell, value, property_details_);
362       property_details_ = property_details_.set_cell_type(new_type);
363       cell->set_property_details(property_details_);
364       number_ = entry;
365       has_property_ = true;
366     } else {
367       // Don't set enumeration index (it will be set during value store).
368       property_details_ =
369           PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell);
370       transition_ = map;
371     }
372     return;
373   }
374 
375   Handle<Map> transition = Map::TransitionToDataProperty(
376       map, name_, value, attributes, kDefaultFieldConstness, store_mode);
377   state_ = TRANSITION;
378   transition_ = transition;
379 
380   if (transition->is_dictionary_map()) {
381     // Don't set enumeration index (it will be set during value store).
382     property_details_ =
383         PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell);
384   } else {
385     property_details_ = transition->GetLastDescriptorDetails();
386     has_property_ = true;
387   }
388 }
389 
ApplyTransitionToDataProperty(Handle<JSObject> receiver)390 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
391   DCHECK_EQ(TRANSITION, state_);
392 
393   DCHECK(receiver.is_identical_to(GetStoreTarget()));
394   holder_ = receiver;
395   if (receiver->IsJSGlobalObject()) {
396     state_ = DATA;
397     return;
398   }
399   Handle<Map> transition = transition_map();
400   bool simple_transition = transition->GetBackPointer() == receiver->map();
401   JSObject::MigrateToMap(receiver, transition);
402 
403   if (simple_transition) {
404     int number = transition->LastAdded();
405     number_ = static_cast<uint32_t>(number);
406     property_details_ = transition->GetLastDescriptorDetails();
407     state_ = DATA;
408   } else if (receiver->map()->is_dictionary_map()) {
409     Handle<NameDictionary> dictionary(receiver->property_dictionary(),
410                                       isolate_);
411     int entry;
412     dictionary = NameDictionary::Add(dictionary, name(),
413                                      isolate_->factory()->uninitialized_value(),
414                                      property_details_, &entry);
415     receiver->set_properties(*dictionary);
416     // Reload details containing proper enumeration index value.
417     property_details_ = dictionary->DetailsAt(entry);
418     number_ = entry;
419     has_property_ = true;
420     state_ = DATA;
421 
422   } else {
423     ReloadPropertyInformation<false>();
424   }
425 }
426 
427 
Delete()428 void LookupIterator::Delete() {
429   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
430   if (IsElement()) {
431     Handle<JSObject> object = Handle<JSObject>::cast(holder);
432     ElementsAccessor* accessor = object->GetElementsAccessor();
433     accessor->Delete(object, number_);
434   } else {
435     bool is_prototype_map = holder->map()->is_prototype_map();
436     RuntimeCallTimerScope stats_scope(
437         isolate_, is_prototype_map
438                       ? &RuntimeCallStats::PrototypeObject_DeleteProperty
439                       : &RuntimeCallStats::Object_DeleteProperty);
440 
441     PropertyNormalizationMode mode =
442         is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
443 
444     if (holder->HasFastProperties()) {
445       JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
446                                     "DeletingProperty");
447       ReloadPropertyInformation<false>();
448     }
449     // TODO(verwaest): Get rid of the name_ argument.
450     JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
451     if (holder->IsJSObject()) {
452       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
453     }
454   }
455   state_ = NOT_FOUND;
456 }
457 
TransitionToAccessorProperty(Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)458 void LookupIterator::TransitionToAccessorProperty(
459     Handle<Object> getter, Handle<Object> setter,
460     PropertyAttributes attributes) {
461   DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
462   // Can only be called when the receiver is a JSObject. JSProxy has to be
463   // handled via a trap. Adding properties to primitive values is not
464   // observable.
465   Handle<JSObject> receiver = GetStoreTarget();
466   if (!IsElement() && name()->IsPrivate()) {
467     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
468   }
469 
470   if (!IsElement() && !receiver->map()->is_dictionary_map()) {
471     Handle<Map> old_map(receiver->map(), isolate_);
472 
473     if (!holder_.is_identical_to(receiver)) {
474       holder_ = receiver;
475       state_ = NOT_FOUND;
476     } else if (state_ == INTERCEPTOR) {
477       LookupInRegularHolder<false>(*old_map, *holder_);
478     }
479     int descriptor =
480         IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
481 
482     Handle<Map> new_map = Map::TransitionToAccessorProperty(
483         isolate_, old_map, name_, descriptor, getter, setter, attributes);
484     bool simple_transition = new_map->GetBackPointer() == receiver->map();
485     JSObject::MigrateToMap(receiver, new_map);
486 
487     if (simple_transition) {
488       int number = new_map->LastAdded();
489       number_ = static_cast<uint32_t>(number);
490       property_details_ = new_map->GetLastDescriptorDetails();
491       state_ = ACCESSOR;
492       return;
493     }
494 
495     ReloadPropertyInformation<false>();
496     if (!new_map->is_dictionary_map()) return;
497   }
498 
499   Handle<AccessorPair> pair;
500   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
501     pair = Handle<AccessorPair>::cast(GetAccessors());
502     // If the component and attributes are identical, nothing has to be done.
503     if (pair->Equals(*getter, *setter)) {
504       if (property_details().attributes() == attributes) {
505         if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
506         return;
507       }
508     } else {
509       pair = AccessorPair::Copy(pair);
510       pair->SetComponents(*getter, *setter);
511     }
512   } else {
513     pair = factory()->NewAccessorPair();
514     pair->SetComponents(*getter, *setter);
515   }
516 
517   TransitionToAccessorPair(pair, attributes);
518 
519 #if VERIFY_HEAP
520   if (FLAG_verify_heap) {
521     receiver->JSObjectVerify();
522   }
523 #endif
524 }
525 
526 
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)527 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
528                                               PropertyAttributes attributes) {
529   Handle<JSObject> receiver = GetStoreTarget();
530   holder_ = receiver;
531 
532   PropertyDetails details(kAccessor, attributes, 0, PropertyCellType::kMutable);
533 
534   if (IsElement()) {
535     // TODO(verwaest): Move code into the element accessor.
536     Handle<SeededNumberDictionary> dictionary =
537         JSObject::NormalizeElements(receiver);
538 
539     dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details,
540                                              receiver);
541     receiver->RequireSlowElements(*dictionary);
542 
543     if (receiver->HasSlowArgumentsElements()) {
544       FixedArray* parameter_map = FixedArray::cast(receiver->elements());
545       uint32_t length = parameter_map->length() - 2;
546       if (number_ < length) {
547         parameter_map->set(number_ + 2, heap()->the_hole_value());
548       }
549       FixedArray::cast(receiver->elements())->set(1, *dictionary);
550     } else {
551       receiver->set_elements(*dictionary);
552     }
553 
554     ReloadPropertyInformation<true>();
555   } else {
556     PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
557                                          ? KEEP_INOBJECT_PROPERTIES
558                                          : CLEAR_INOBJECT_PROPERTIES;
559     // Normalize object to make this operation simple.
560     JSObject::NormalizeProperties(receiver, mode, 0,
561                                   "TransitionToAccessorPair");
562 
563     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
564     JSObject::ReoptimizeIfPrototype(receiver);
565 
566     ReloadPropertyInformation<false>();
567   }
568 }
569 
570 
HolderIsReceiverOrHiddenPrototype() const571 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
572   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
573   // Optimization that only works if configuration_ is not mutable.
574   if (!check_prototype_chain()) return true;
575   DisallowHeapAllocation no_gc;
576   if (*receiver_ == *holder_) return true;
577   if (!receiver_->IsJSReceiver()) return false;
578   JSReceiver* current = JSReceiver::cast(*receiver_);
579   JSReceiver* object = *holder_;
580   if (!current->map()->has_hidden_prototype()) return false;
581   // JSProxy do not occur as hidden prototypes.
582   if (object->IsJSProxy()) return false;
583   PrototypeIterator iter(isolate(), current, kStartAtPrototype,
584                          PrototypeIterator::END_AT_NON_HIDDEN);
585   while (!iter.IsAtEnd()) {
586     if (iter.GetCurrent<JSReceiver>() == object) return true;
587     iter.Advance();
588   }
589   return false;
590 }
591 
592 
FetchValue() const593 Handle<Object> LookupIterator::FetchValue() const {
594   Object* result = NULL;
595   if (IsElement()) {
596     Handle<JSObject> holder = GetHolder<JSObject>();
597     ElementsAccessor* accessor = holder->GetElementsAccessor();
598     return accessor->Get(holder, number_);
599   } else if (holder_->IsJSGlobalObject()) {
600     Handle<JSObject> holder = GetHolder<JSObject>();
601     result = holder->global_dictionary()->ValueAt(number_);
602     DCHECK(result->IsPropertyCell());
603     result = PropertyCell::cast(result)->value();
604   } else if (!holder_->HasFastProperties()) {
605     result = holder_->property_dictionary()->ValueAt(number_);
606   } else if (property_details_.location() == kField) {
607     DCHECK_EQ(kData, property_details_.kind());
608     Handle<JSObject> holder = GetHolder<JSObject>();
609     FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
610     return JSObject::FastPropertyAt(holder, property_details_.representation(),
611                                     field_index);
612   } else {
613     result = holder_->map()->instance_descriptors()->GetValue(number_);
614   }
615   return handle(result, isolate_);
616 }
617 
IsConstFieldValueEqualTo(Object * value) const618 bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const {
619   DCHECK(!IsElement());
620   DCHECK(holder_->HasFastProperties());
621   DCHECK_EQ(kField, property_details_.location());
622   DCHECK_EQ(kConst, property_details_.constness());
623   Handle<JSObject> holder = GetHolder<JSObject>();
624   FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
625   if (property_details_.representation().IsDouble()) {
626     if (!value->IsNumber()) return false;
627     uint64_t bits;
628     if (holder->IsUnboxedDoubleField(field_index)) {
629       bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
630     } else {
631       Object* current_value = holder->RawFastPropertyAt(field_index);
632       DCHECK(current_value->IsMutableHeapNumber());
633       bits = HeapNumber::cast(current_value)->value_as_bits();
634     }
635     // Use bit representation of double to to check for hole double, since
636     // manipulating the signaling NaN used for the hole in C++, e.g. with
637     // bit_cast or value(), will change its value on ia32 (the x87 stack is
638     // used to return values and stores to the stack silently clear the
639     // signalling bit).
640     if (bits == kHoleNanInt64) {
641       // Uninitialized double field.
642       return true;
643     }
644     return bit_cast<double>(bits) == value->Number();
645   } else {
646     Object* current_value = holder->RawFastPropertyAt(field_index);
647     return current_value->IsUninitialized(isolate()) || current_value == value;
648   }
649 }
650 
GetFieldDescriptorIndex() const651 int LookupIterator::GetFieldDescriptorIndex() const {
652   DCHECK(has_property_);
653   DCHECK(holder_->HasFastProperties());
654   DCHECK_EQ(kField, property_details_.location());
655   DCHECK_EQ(kData, property_details_.kind());
656   return descriptor_number();
657 }
658 
GetAccessorIndex() const659 int LookupIterator::GetAccessorIndex() const {
660   DCHECK(has_property_);
661   DCHECK(holder_->HasFastProperties());
662   DCHECK_EQ(kDescriptor, property_details_.location());
663   DCHECK_EQ(kAccessor, property_details_.kind());
664   return descriptor_number();
665 }
666 
667 
GetConstantIndex() const668 int LookupIterator::GetConstantIndex() const {
669   DCHECK(has_property_);
670   DCHECK(holder_->HasFastProperties());
671   DCHECK_EQ(kDescriptor, property_details_.location());
672   DCHECK_EQ(kData, property_details_.kind());
673   DCHECK(!FLAG_track_constant_fields);
674   DCHECK(!IsElement());
675   return descriptor_number();
676 }
677 
GetFieldOwnerMap() const678 Handle<Map> LookupIterator::GetFieldOwnerMap() const {
679   DCHECK(has_property_);
680   DCHECK(holder_->HasFastProperties());
681   DCHECK_EQ(kField, property_details_.location());
682   DCHECK(!IsElement());
683   Map* holder_map = holder_->map();
684   return handle(holder_map->FindFieldOwner(descriptor_number()), isolate_);
685 }
686 
GetFieldIndex() const687 FieldIndex LookupIterator::GetFieldIndex() const {
688   DCHECK(has_property_);
689   DCHECK(holder_->HasFastProperties());
690   DCHECK_EQ(kField, property_details_.location());
691   DCHECK(!IsElement());
692   Map* holder_map = holder_->map();
693   int index =
694       holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
695   bool is_double = representation().IsDouble();
696   return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
697 }
698 
GetFieldType() const699 Handle<FieldType> LookupIterator::GetFieldType() const {
700   DCHECK(has_property_);
701   DCHECK(holder_->HasFastProperties());
702   DCHECK_EQ(kField, property_details_.location());
703   return handle(
704       holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
705       isolate_);
706 }
707 
708 
GetPropertyCell() const709 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
710   DCHECK(!IsElement());
711   Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
712   Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
713   DCHECK(value->IsPropertyCell());
714   return handle(PropertyCell::cast(value), isolate_);
715 }
716 
717 
GetAccessors() const718 Handle<Object> LookupIterator::GetAccessors() const {
719   DCHECK_EQ(ACCESSOR, state_);
720   return FetchValue();
721 }
722 
723 
GetDataValue() const724 Handle<Object> LookupIterator::GetDataValue() const {
725   DCHECK_EQ(DATA, state_);
726   Handle<Object> value = FetchValue();
727   return value;
728 }
729 
WriteDataValue(Handle<Object> value,bool initializing_store)730 void LookupIterator::WriteDataValue(Handle<Object> value,
731                                     bool initializing_store) {
732   DCHECK_EQ(DATA, state_);
733   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
734   if (IsElement()) {
735     Handle<JSObject> object = Handle<JSObject>::cast(holder);
736     ElementsAccessor* accessor = object->GetElementsAccessor();
737     accessor->Set(object, number_, *value);
738   } else if (holder->HasFastProperties()) {
739     if (property_details_.location() == kField) {
740       // Check that in case of kConst field the existing value is equal to
741       // |value|.
742       DCHECK_IMPLIES(
743           !initializing_store && property_details_.constness() == kConst,
744           IsConstFieldValueEqualTo(*value));
745       JSObject::cast(*holder)->WriteToField(descriptor_number(),
746                                             property_details_, *value);
747     } else {
748       DCHECK_EQ(kDescriptor, property_details_.location());
749       DCHECK_EQ(kConst, property_details_.constness());
750     }
751   } else if (holder->IsJSGlobalObject()) {
752     GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
753     Object* cell = dictionary->ValueAt(dictionary_entry());
754     DCHECK(cell->IsPropertyCell());
755     PropertyCell::cast(cell)->set_value(*value);
756   } else {
757     NameDictionary* dictionary = holder->property_dictionary();
758     dictionary->ValueAtPut(dictionary_entry(), *value);
759   }
760 }
761 
762 template <bool is_element>
SkipInterceptor(JSObject * holder)763 bool LookupIterator::SkipInterceptor(JSObject* holder) {
764   auto info = GetInterceptor<is_element>(holder);
765   // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
766   if (info->non_masking()) {
767     switch (interceptor_state_) {
768       case InterceptorState::kUninitialized:
769         interceptor_state_ = InterceptorState::kSkipNonMasking;
770       // Fall through.
771       case InterceptorState::kSkipNonMasking:
772         return true;
773       case InterceptorState::kProcessNonMasking:
774         return false;
775     }
776   }
777   return interceptor_state_ == InterceptorState::kProcessNonMasking;
778 }
779 
NextHolder(Map * map)780 JSReceiver* LookupIterator::NextHolder(Map* map) {
781   DisallowHeapAllocation no_gc;
782   if (map->prototype() == heap()->null_value()) return NULL;
783   if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
784   return JSReceiver::cast(map->prototype());
785 }
786 
NotFound(JSReceiver * const holder) const787 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
788   DCHECK(!IsElement());
789   if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
790 
791   Handle<String> name_string = Handle<String>::cast(name_);
792   if (name_string->length() == 0) return NOT_FOUND;
793 
794   return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
795              ? INTEGER_INDEXED_EXOTIC
796              : NOT_FOUND;
797 }
798 
799 namespace {
800 
801 template <bool is_element>
HasInterceptor(Map * map)802 bool HasInterceptor(Map* map) {
803   return is_element ? map->has_indexed_interceptor()
804                     : map->has_named_interceptor();
805 }
806 
807 }  // namespace
808 
809 template <bool is_element>
LookupInSpecialHolder(Map * const map,JSReceiver * const holder)810 LookupIterator::State LookupIterator::LookupInSpecialHolder(
811     Map* const map, JSReceiver* const holder) {
812   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
813   switch (state_) {
814     case NOT_FOUND:
815       if (map->IsJSProxyMap()) {
816         if (is_element || !name_->IsPrivate()) return JSPROXY;
817       }
818       if (map->is_access_check_needed()) {
819         if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
820       }
821     // Fall through.
822     case ACCESS_CHECK:
823       if (check_interceptor() && HasInterceptor<is_element>(map) &&
824           !SkipInterceptor<is_element>(JSObject::cast(holder))) {
825         if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
826       }
827     // Fall through.
828     case INTERCEPTOR:
829       if (!is_element && map->IsJSGlobalObjectMap()) {
830         GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
831         int number = dict->FindEntry(name_);
832         if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
833         number_ = static_cast<uint32_t>(number);
834         DCHECK(dict->ValueAt(number_)->IsPropertyCell());
835         PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
836         if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
837         property_details_ = cell->property_details();
838         has_property_ = true;
839         switch (property_details_.kind()) {
840           case v8::internal::kData:
841             return DATA;
842           case v8::internal::kAccessor:
843             return ACCESSOR;
844         }
845       }
846       return LookupInRegularHolder<is_element>(map, holder);
847     case ACCESSOR:
848     case DATA:
849       return NOT_FOUND;
850     case INTEGER_INDEXED_EXOTIC:
851     case JSPROXY:
852     case TRANSITION:
853       UNREACHABLE();
854   }
855   UNREACHABLE();
856   return NOT_FOUND;
857 }
858 
859 template <bool is_element>
LookupInRegularHolder(Map * const map,JSReceiver * const holder)860 LookupIterator::State LookupIterator::LookupInRegularHolder(
861     Map* const map, JSReceiver* const holder) {
862   DisallowHeapAllocation no_gc;
863   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
864     return NOT_FOUND;
865   }
866 
867   if (is_element) {
868     JSObject* js_object = JSObject::cast(holder);
869     ElementsAccessor* accessor = js_object->GetElementsAccessor();
870     FixedArrayBase* backing_store = js_object->elements();
871     number_ =
872         accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
873     if (number_ == kMaxUInt32) {
874       return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
875     }
876     property_details_ = accessor->GetDetails(js_object, number_);
877   } else if (!map->is_dictionary_map()) {
878     DescriptorArray* descriptors = map->instance_descriptors();
879     int number = descriptors->SearchWithCache(isolate_, *name_, map);
880     if (number == DescriptorArray::kNotFound) return NotFound(holder);
881     number_ = static_cast<uint32_t>(number);
882     property_details_ = descriptors->GetDetails(number_);
883   } else {
884     NameDictionary* dict = holder->property_dictionary();
885     int number = dict->FindEntry(name_);
886     if (number == NameDictionary::kNotFound) return NotFound(holder);
887     number_ = static_cast<uint32_t>(number);
888     property_details_ = dict->DetailsAt(number_);
889   }
890   has_property_ = true;
891   switch (property_details_.kind()) {
892     case v8::internal::kData:
893       return DATA;
894     case v8::internal::kAccessor:
895       return ACCESSOR;
896   }
897 
898   UNREACHABLE();
899   return state_;
900 }
901 
GetInterceptorForFailedAccessCheck() const902 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
903     const {
904   DCHECK_EQ(ACCESS_CHECK, state_);
905   DisallowHeapAllocation no_gc;
906   AccessCheckInfo* access_check_info =
907       AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
908   if (access_check_info) {
909     Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
910                                       : access_check_info->named_interceptor();
911     if (interceptor) {
912       return handle(InterceptorInfo::cast(interceptor), isolate_);
913     }
914   }
915   return Handle<InterceptorInfo>();
916 }
917 
TryLookupCachedProperty()918 bool LookupIterator::TryLookupCachedProperty() {
919   return state() == LookupIterator::ACCESSOR &&
920          GetAccessors()->IsAccessorPair() && LookupCachedProperty();
921 }
922 
LookupCachedProperty()923 bool LookupIterator::LookupCachedProperty() {
924   DCHECK_EQ(state(), LookupIterator::ACCESSOR);
925   DCHECK(GetAccessors()->IsAccessorPair());
926 
927   AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
928   Handle<Object> getter(accessor_pair->getter(), isolate());
929   MaybeHandle<Name> maybe_name =
930       FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
931   if (maybe_name.is_null()) return false;
932 
933   // We have found a cached property! Modify the iterator accordingly.
934   name_ = maybe_name.ToHandleChecked();
935   Restart();
936   CHECK_EQ(state(), LookupIterator::DATA);
937   return true;
938 }
939 
940 }  // namespace internal
941 }  // namespace v8
942