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/isolate-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
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 
Next()48 void LookupIterator::Next() {
49   DCHECK_NE(JSPROXY, state_);
50   DCHECK_NE(TRANSITION, state_);
51   DisallowHeapAllocation no_gc;
52   has_property_ = false;
53 
54   JSReceiver* holder = *holder_;
55   Map* map = *holder_map_;
56 
57   // Perform lookup on current holder.
58   state_ = LookupInHolder(map, holder);
59   if (IsFound()) return;
60 
61   // Continue lookup if lookup on current holder failed.
62   do {
63     JSReceiver* maybe_holder = NextHolder(map);
64     if (maybe_holder == nullptr) {
65       if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
66         RestartLookupForNonMaskingInterceptors();
67         return;
68       }
69       break;
70     }
71     holder = maybe_holder;
72     map = holder->map();
73     state_ = LookupInHolder(map, holder);
74   } while (!IsFound());
75 
76   if (holder != *holder_) {
77     holder_ = handle(holder, isolate_);
78     holder_map_ = handle(map, isolate_);
79   }
80 }
81 
82 
RestartInternal(InterceptorState interceptor_state)83 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
84   state_ = NOT_FOUND;
85   interceptor_state_ = interceptor_state;
86   property_details_ = PropertyDetails::Empty();
87   holder_ = initial_holder_;
88   holder_map_ = handle(holder_->map(), isolate_);
89   number_ = DescriptorArray::kNotFound;
90   Next();
91 }
92 
93 
94 // static
GetRootForNonJSReceiver(Isolate * isolate,Handle<Object> receiver,uint32_t index)95 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
96     Isolate* isolate, Handle<Object> receiver, uint32_t index) {
97   // Strings are the only objects with properties (only elements) directly on
98   // the wrapper. Hence we can skip generating the wrapper for all other cases.
99   if (index != kMaxUInt32 && receiver->IsString() &&
100       index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
101     // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
102     // context, ensuring that we don't leak it into JS?
103     Handle<JSFunction> constructor = isolate->string_function();
104     Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
105     Handle<JSValue>::cast(result)->set_value(*receiver);
106     return result;
107   }
108   auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
109   if (root->IsNull()) {
110     unsigned int magic = 0xbbbbbbbb;
111     isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
112   }
113   return Handle<JSReceiver>::cast(root);
114 }
115 
116 
GetReceiverMap() const117 Handle<Map> LookupIterator::GetReceiverMap() const {
118   if (receiver_->IsNumber()) return factory()->heap_number_map();
119   return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
120 }
121 
122 
GetStoreTarget() const123 Handle<JSObject> LookupIterator::GetStoreTarget() const {
124   if (receiver_->IsJSGlobalProxy()) {
125     PrototypeIterator iter(isolate(), receiver_);
126     if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
127     return PrototypeIterator::GetCurrent<JSGlobalObject>(iter);
128   }
129   return Handle<JSObject>::cast(receiver_);
130 }
131 
132 
HasAccess() const133 bool LookupIterator::HasAccess() const {
134   DCHECK_EQ(ACCESS_CHECK, state_);
135   return isolate_->MayAccess(handle(isolate_->context()),
136                              GetHolder<JSObject>());
137 }
138 
139 
ReloadPropertyInformation()140 void LookupIterator::ReloadPropertyInformation() {
141   state_ = BEFORE_PROPERTY;
142   interceptor_state_ = InterceptorState::kUninitialized;
143   state_ = LookupInHolder(*holder_map_, *holder_);
144   DCHECK(IsFound() || holder_map_->is_dictionary_map());
145 }
146 
147 
ReloadHolderMap()148 void LookupIterator::ReloadHolderMap() {
149   DCHECK_EQ(DATA, state_);
150   DCHECK(IsElement());
151   DCHECK(JSObject::cast(*holder_)->HasFixedTypedArrayElements());
152   if (*holder_map_ != holder_->map()) {
153     holder_map_ = handle(holder_->map(), isolate_);
154   }
155 }
156 
157 
PrepareForDataProperty(Handle<Object> value)158 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
159   DCHECK(state_ == DATA || state_ == ACCESSOR);
160   DCHECK(HolderIsReceiverOrHiddenPrototype());
161 
162   Handle<JSObject> holder = GetHolder<JSObject>();
163 
164   if (IsElement()) {
165     ElementsKind kind = holder_map_->elements_kind();
166     ElementsKind to = value->OptimalElementsKind();
167     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
168     to = GetMoreGeneralElementsKind(kind, to);
169     JSObject::TransitionElementsKind(holder, to);
170     holder_map_ = handle(holder->map(), isolate_);
171 
172     // Copy the backing store if it is copy-on-write.
173     if (IsFastSmiOrObjectElementsKind(to)) {
174       JSObject::EnsureWritableFastElements(holder);
175     }
176 
177   } else {
178     if (holder_map_->is_dictionary_map()) return;
179     holder_map_ =
180         Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
181   }
182 
183   JSObject::MigrateToMap(holder, holder_map_);
184   ReloadPropertyInformation();
185 }
186 
187 
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)188 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
189                                              PropertyAttributes attributes) {
190   DCHECK(state_ == DATA || state_ == ACCESSOR);
191   DCHECK(HolderIsReceiverOrHiddenPrototype());
192   Handle<JSObject> holder = GetHolder<JSObject>();
193   if (IsElement()) {
194     DCHECK(!holder->HasFixedTypedArrayElements());
195     DCHECK(attributes != NONE || !holder->HasFastElements());
196     Handle<FixedArrayBase> elements(holder->elements());
197     holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
198                                                attributes);
199   } else if (holder_map_->is_dictionary_map()) {
200     PropertyDetails details(attributes, v8::internal::DATA, 0,
201                             PropertyCellType::kMutable);
202     JSObject::SetNormalizedProperty(holder, name(), value, details);
203   } else {
204     holder_map_ = Map::ReconfigureExistingProperty(
205         holder_map_, descriptor_number(), i::kData, attributes);
206     holder_map_ =
207         Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
208     JSObject::MigrateToMap(holder, holder_map_);
209   }
210 
211   ReloadPropertyInformation();
212   WriteDataValue(value);
213 
214 #if VERIFY_HEAP
215   if (FLAG_verify_heap) {
216     holder->JSObjectVerify();
217   }
218 #endif
219 }
220 
221 
PrepareTransitionToDataProperty(Handle<Object> value,PropertyAttributes attributes,Object::StoreFromKeyed store_mode)222 void LookupIterator::PrepareTransitionToDataProperty(
223     Handle<Object> value, PropertyAttributes attributes,
224     Object::StoreFromKeyed store_mode) {
225   if (state_ == TRANSITION) return;
226   DCHECK(state_ != LookupIterator::ACCESSOR ||
227          (GetAccessors()->IsAccessorInfo() &&
228           AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
229   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
230   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
231   // Can only be called when the receiver is a JSObject. JSProxy has to be
232   // handled via a trap. Adding properties to primitive values is not
233   // observable.
234   Handle<JSObject> receiver = GetStoreTarget();
235 
236   if (!isolate()->IsInternallyUsedPropertyName(name()) &&
237       !receiver->map()->is_extensible()) {
238     return;
239   }
240 
241   auto transition = Map::TransitionToDataProperty(
242       handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
243   state_ = TRANSITION;
244   transition_ = transition;
245 
246   if (receiver->IsJSGlobalObject()) {
247     // Install a property cell.
248     InternalizeName();
249     auto cell = JSGlobalObject::EnsurePropertyCell(
250         Handle<JSGlobalObject>::cast(receiver), name());
251     DCHECK(cell->value()->IsTheHole());
252     transition_ = cell;
253   } else if (!transition->is_dictionary_map()) {
254     property_details_ = transition->GetLastDescriptorDetails();
255     has_property_ = true;
256   }
257 }
258 
259 
ApplyTransitionToDataProperty()260 void LookupIterator::ApplyTransitionToDataProperty() {
261   DCHECK_EQ(TRANSITION, state_);
262 
263   Handle<JSObject> receiver = GetStoreTarget();
264   if (receiver->IsJSGlobalObject()) return;
265   holder_ = receiver;
266   holder_map_ = transition_map();
267   JSObject::MigrateToMap(receiver, holder_map_);
268   ReloadPropertyInformation();
269 }
270 
271 
Delete()272 void LookupIterator::Delete() {
273   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
274   if (IsElement()) {
275     Handle<JSObject> object = Handle<JSObject>::cast(holder);
276     ElementsAccessor* accessor = object->GetElementsAccessor();
277     accessor->Delete(object, number_);
278   } else {
279     PropertyNormalizationMode mode = holder->map()->is_prototype_map()
280                                          ? KEEP_INOBJECT_PROPERTIES
281                                          : CLEAR_INOBJECT_PROPERTIES;
282 
283     if (holder->HasFastProperties()) {
284       JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
285                                     "DeletingProperty");
286       holder_map_ = handle(holder->map(), isolate_);
287       ReloadPropertyInformation();
288     }
289     // TODO(verwaest): Get rid of the name_ argument.
290     JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
291     if (holder->IsJSObject()) {
292       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
293     }
294   }
295 }
296 
297 
TransitionToAccessorProperty(AccessorComponent component,Handle<Object> accessor,PropertyAttributes attributes)298 void LookupIterator::TransitionToAccessorProperty(
299     AccessorComponent component, Handle<Object> accessor,
300     PropertyAttributes attributes) {
301   DCHECK(!accessor->IsNull());
302   // Can only be called when the receiver is a JSObject. JSProxy has to be
303   // handled via a trap. Adding properties to primitive values is not
304   // observable.
305   Handle<JSObject> receiver = GetStoreTarget();
306 
307   if (!IsElement() && !receiver->map()->is_dictionary_map()) {
308     holder_ = receiver;
309     holder_map_ = Map::TransitionToAccessorProperty(
310         handle(receiver->map(), isolate_), name_, component, accessor,
311         attributes);
312     JSObject::MigrateToMap(receiver, holder_map_);
313 
314     ReloadPropertyInformation();
315 
316     if (!holder_map_->is_dictionary_map()) return;
317   }
318 
319   Handle<AccessorPair> pair;
320   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
321     pair = Handle<AccessorPair>::cast(GetAccessors());
322     // If the component and attributes are identical, nothing has to be done.
323     if (pair->get(component) == *accessor) {
324       if (property_details().attributes() == attributes) return;
325     } else {
326       pair = AccessorPair::Copy(pair);
327       pair->set(component, *accessor);
328     }
329   } else {
330     pair = factory()->NewAccessorPair();
331     pair->set(component, *accessor);
332   }
333 
334   TransitionToAccessorPair(pair, attributes);
335 
336 #if VERIFY_HEAP
337   if (FLAG_verify_heap) {
338     receiver->JSObjectVerify();
339   }
340 #endif
341 }
342 
343 
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)344 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
345                                               PropertyAttributes attributes) {
346   Handle<JSObject> receiver = GetStoreTarget();
347   holder_ = receiver;
348 
349   PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
350                           PropertyCellType::kMutable);
351 
352   if (IsElement()) {
353     // TODO(verwaest): Move code into the element accessor.
354     Handle<SeededNumberDictionary> dictionary =
355         JSObject::NormalizeElements(receiver);
356 
357     // We unconditionally pass used_as_prototype=false here because the call
358     // to RequireSlowElements takes care of the required IC clearing and
359     // we don't want to walk the heap twice.
360     dictionary =
361         SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
362     receiver->RequireSlowElements(*dictionary);
363 
364     if (receiver->HasSlowArgumentsElements()) {
365       FixedArray* parameter_map = FixedArray::cast(receiver->elements());
366       uint32_t length = parameter_map->length() - 2;
367       if (number_ < length) {
368         parameter_map->set(number_ + 2, heap()->the_hole_value());
369       }
370       FixedArray::cast(receiver->elements())->set(1, *dictionary);
371     } else {
372       receiver->set_elements(*dictionary);
373     }
374   } else {
375     PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
376                                          ? KEEP_INOBJECT_PROPERTIES
377                                          : CLEAR_INOBJECT_PROPERTIES;
378     // Normalize object to make this operation simple.
379     JSObject::NormalizeProperties(receiver, mode, 0,
380                                   "TransitionToAccessorPair");
381 
382     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
383     JSObject::ReoptimizeIfPrototype(receiver);
384   }
385 
386   holder_map_ = handle(receiver->map(), isolate_);
387   ReloadPropertyInformation();
388 }
389 
390 
HolderIsReceiverOrHiddenPrototype() const391 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
392   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
393   return InternalHolderIsReceiverOrHiddenPrototype();
394 }
395 
InternalHolderIsReceiverOrHiddenPrototype() const396 bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const {
397   // Optimization that only works if configuration_ is not mutable.
398   if (!check_prototype_chain()) return true;
399   DisallowHeapAllocation no_gc;
400   if (!receiver_->IsJSReceiver()) return false;
401   Object* current = *receiver_;
402   JSReceiver* holder = *holder_;
403   // JSProxy do not occur as hidden prototypes.
404   if (current->IsJSProxy()) {
405     return JSReceiver::cast(current) == holder;
406   }
407   PrototypeIterator iter(isolate(), current,
408                          PrototypeIterator::START_AT_RECEIVER);
409   do {
410     if (iter.GetCurrent<JSReceiver>() == holder) return true;
411     DCHECK(!current->IsJSProxy());
412     iter.Advance();
413   } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
414   return false;
415 }
416 
417 
FetchValue() const418 Handle<Object> LookupIterator::FetchValue() const {
419   Object* result = NULL;
420   if (IsElement()) {
421     Handle<JSObject> holder = GetHolder<JSObject>();
422     // TODO(verwaest): Optimize.
423     if (holder->IsStringObjectWithCharacterAt(index_)) {
424       Handle<JSValue> js_value = Handle<JSValue>::cast(holder);
425       Handle<String> string(String::cast(js_value->value()));
426       return factory()->LookupSingleCharacterStringFromCode(
427           String::Flatten(string)->Get(index_));
428     }
429 
430     ElementsAccessor* accessor = holder->GetElementsAccessor();
431     return accessor->Get(handle(holder->elements()), number_);
432   } else if (holder_map_->IsJSGlobalObjectMap()) {
433     Handle<JSObject> holder = GetHolder<JSObject>();
434     result = holder->global_dictionary()->ValueAt(number_);
435     DCHECK(result->IsPropertyCell());
436     result = PropertyCell::cast(result)->value();
437   } else if (holder_map_->is_dictionary_map()) {
438     result = holder_->property_dictionary()->ValueAt(number_);
439   } else if (property_details_.type() == v8::internal::DATA) {
440     Handle<JSObject> holder = GetHolder<JSObject>();
441     FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
442     return JSObject::FastPropertyAt(holder, property_details_.representation(),
443                                     field_index);
444   } else {
445     result = holder_map_->instance_descriptors()->GetValue(number_);
446   }
447   return handle(result, isolate_);
448 }
449 
450 
GetAccessorIndex() const451 int LookupIterator::GetAccessorIndex() const {
452   DCHECK(has_property_);
453   DCHECK(!holder_map_->is_dictionary_map());
454   DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
455   return descriptor_number();
456 }
457 
458 
GetConstantIndex() const459 int LookupIterator::GetConstantIndex() const {
460   DCHECK(has_property_);
461   DCHECK(!holder_map_->is_dictionary_map());
462   DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
463   DCHECK(!IsElement());
464   return descriptor_number();
465 }
466 
467 
GetFieldIndex() const468 FieldIndex LookupIterator::GetFieldIndex() const {
469   DCHECK(has_property_);
470   DCHECK(!holder_map_->is_dictionary_map());
471   DCHECK_EQ(v8::internal::DATA, property_details_.type());
472   DCHECK(!IsElement());
473   int index =
474       holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
475   bool is_double = representation().IsDouble();
476   return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
477 }
478 
479 
GetFieldType() const480 Handle<HeapType> LookupIterator::GetFieldType() const {
481   DCHECK(has_property_);
482   DCHECK(!holder_map_->is_dictionary_map());
483   DCHECK_EQ(v8::internal::DATA, property_details_.type());
484   return handle(
485       holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
486       isolate_);
487 }
488 
489 
GetPropertyCell() const490 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
491   DCHECK(!IsElement());
492   Handle<JSObject> holder = GetHolder<JSObject>();
493   Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
494   Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
495   DCHECK(value->IsPropertyCell());
496   return handle(PropertyCell::cast(value));
497 }
498 
499 
GetAccessors() const500 Handle<Object> LookupIterator::GetAccessors() const {
501   DCHECK_EQ(ACCESSOR, state_);
502   return FetchValue();
503 }
504 
505 
GetDataValue() const506 Handle<Object> LookupIterator::GetDataValue() const {
507   DCHECK_EQ(DATA, state_);
508   Handle<Object> value = FetchValue();
509   return value;
510 }
511 
512 
WriteDataValue(Handle<Object> value)513 void LookupIterator::WriteDataValue(Handle<Object> value) {
514   DCHECK_EQ(DATA, state_);
515   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
516   if (IsElement()) {
517     Handle<JSObject> object = Handle<JSObject>::cast(holder);
518     ElementsAccessor* accessor = object->GetElementsAccessor();
519     accessor->Set(object->elements(), number_, *value);
520   } else if (holder->IsJSGlobalObject()) {
521     Handle<GlobalDictionary> property_dictionary =
522         handle(JSObject::cast(*holder)->global_dictionary());
523     PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
524                              property_details_);
525   } else if (holder_map_->is_dictionary_map()) {
526     NameDictionary* property_dictionary = holder->property_dictionary();
527     property_dictionary->ValueAtPut(dictionary_entry(), *value);
528   } else if (property_details_.type() == v8::internal::DATA) {
529     JSObject::cast(*holder)->WriteToField(descriptor_number(), *value);
530   } else {
531     DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
532   }
533 }
534 
535 
IsIntegerIndexedExotic(JSReceiver * holder)536 bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
537   DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic);
538   if (exotic_index_state_ == ExoticIndexState::kExotic) return true;
539   if (!InternalHolderIsReceiverOrHiddenPrototype()) {
540     exotic_index_state_ = ExoticIndexState::kNotExotic;
541     return false;
542   }
543   DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
544   bool result = false;
545   // Compute and cache result.
546   if (IsElement()) {
547     result = index_ >= JSTypedArray::cast(holder)->length_value();
548   } else if (name()->IsString()) {
549     Handle<String> name_string = Handle<String>::cast(name());
550     if (name_string->length() != 0) {
551       result = IsSpecialIndex(isolate_->unicode_cache(), *name_string);
552     }
553   }
554   exotic_index_state_ =
555       result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic;
556   return result;
557 }
558 
559 
InternalizeName()560 void LookupIterator::InternalizeName() {
561   if (name_->IsUniqueName()) return;
562   name_ = factory()->InternalizeString(Handle<String>::cast(name_));
563 }
564 
565 
HasInterceptor(Map * map) const566 bool LookupIterator::HasInterceptor(Map* map) const {
567   if (IsElement()) return map->has_indexed_interceptor();
568   return map->has_named_interceptor();
569 }
570 
571 
SkipInterceptor(JSObject * holder)572 bool LookupIterator::SkipInterceptor(JSObject* holder) {
573   auto info = GetInterceptor(holder);
574   // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
575   if (info->non_masking()) {
576     switch (interceptor_state_) {
577       case InterceptorState::kUninitialized:
578         interceptor_state_ = InterceptorState::kSkipNonMasking;
579       // Fall through.
580       case InterceptorState::kSkipNonMasking:
581         return true;
582       case InterceptorState::kProcessNonMasking:
583         return false;
584     }
585   }
586   return interceptor_state_ == InterceptorState::kProcessNonMasking;
587 }
588 
589 
NextHolder(Map * map)590 JSReceiver* LookupIterator::NextHolder(Map* map) {
591   DisallowHeapAllocation no_gc;
592   if (!map->prototype()->IsJSReceiver()) return NULL;
593 
594   JSReceiver* next = JSReceiver::cast(map->prototype());
595   DCHECK(!next->map()->IsJSGlobalObjectMap() ||
596          next->map()->is_hidden_prototype());
597 
598   if (!check_prototype_chain() &&
599       !(check_hidden() && next->map()->is_hidden_prototype()) &&
600       // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even
601       // when not checking other hidden prototypes.
602       !map->IsJSGlobalProxyMap()) {
603     return NULL;
604   }
605 
606   return next;
607 }
608 
609 
LookupInHolder(Map * const map,JSReceiver * const holder)610 LookupIterator::State LookupIterator::LookupInHolder(Map* const map,
611                                                      JSReceiver* const holder) {
612   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
613   DisallowHeapAllocation no_gc;
614   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
615     return LookupNonMaskingInterceptorInHolder(map, holder);
616   }
617   switch (state_) {
618     case NOT_FOUND:
619       if (map->IsJSProxyMap()) {
620         // Do not leak private property names.
621         if (IsElement() || !name_->IsPrivate()) return JSPROXY;
622       }
623       if (map->is_access_check_needed() &&
624           (IsElement() || !isolate_->IsInternallyUsedPropertyName(name_))) {
625         return ACCESS_CHECK;
626       }
627     // Fall through.
628     case ACCESS_CHECK:
629       if (exotic_index_state_ != ExoticIndexState::kNotExotic &&
630           holder->IsJSTypedArray() && IsIntegerIndexedExotic(holder)) {
631         return INTEGER_INDEXED_EXOTIC;
632       }
633       if (check_interceptor() && HasInterceptor(map) &&
634           !SkipInterceptor(JSObject::cast(holder))) {
635         // Do not leak private property names.
636         if (!name_.is_null() && name_->IsPrivate()) return NOT_FOUND;
637         return INTERCEPTOR;
638       }
639     // Fall through.
640     case INTERCEPTOR:
641       if (IsElement()) {
642         // TODO(verwaest): Optimize.
643         if (holder->IsStringObjectWithCharacterAt(index_)) {
644           PropertyAttributes attributes =
645               static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
646           property_details_ = PropertyDetails(attributes, v8::internal::DATA, 0,
647                                               PropertyCellType::kNoCell);
648         } else {
649           JSObject* js_object = JSObject::cast(holder);
650           if (js_object->elements() == isolate()->heap()->empty_fixed_array()) {
651             return NOT_FOUND;
652           }
653 
654           ElementsAccessor* accessor = js_object->GetElementsAccessor();
655           FixedArrayBase* backing_store = js_object->elements();
656           number_ =
657               accessor->GetEntryForIndex(js_object, backing_store, index_);
658           if (number_ == kMaxUInt32) return NOT_FOUND;
659           property_details_ = accessor->GetDetails(backing_store, number_);
660         }
661       } else if (!map->is_dictionary_map()) {
662         DescriptorArray* descriptors = map->instance_descriptors();
663         int number = descriptors->SearchWithCache(*name_, map);
664         if (number == DescriptorArray::kNotFound) return NOT_FOUND;
665         number_ = static_cast<uint32_t>(number);
666         property_details_ = descriptors->GetDetails(number_);
667       } else if (map->IsJSGlobalObjectMap()) {
668         GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
669         int number = dict->FindEntry(name_);
670         if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
671         number_ = static_cast<uint32_t>(number);
672         DCHECK(dict->ValueAt(number_)->IsPropertyCell());
673         PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
674         if (cell->value()->IsTheHole()) return NOT_FOUND;
675         property_details_ = cell->property_details();
676       } else {
677         NameDictionary* dict = holder->property_dictionary();
678         int number = dict->FindEntry(name_);
679         if (number == NameDictionary::kNotFound) return NOT_FOUND;
680         number_ = static_cast<uint32_t>(number);
681         property_details_ = dict->DetailsAt(number_);
682       }
683       has_property_ = true;
684       switch (property_details_.kind()) {
685         case v8::internal::kData:
686           return DATA;
687         case v8::internal::kAccessor:
688           return ACCESSOR;
689       }
690     case ACCESSOR:
691     case DATA:
692       return NOT_FOUND;
693     case INTEGER_INDEXED_EXOTIC:
694     case JSPROXY:
695     case TRANSITION:
696       UNREACHABLE();
697   }
698   UNREACHABLE();
699   return state_;
700 }
701 
702 
LookupNonMaskingInterceptorInHolder(Map * const map,JSReceiver * const holder)703 LookupIterator::State LookupIterator::LookupNonMaskingInterceptorInHolder(
704     Map* const map, JSReceiver* const holder) {
705   switch (state_) {
706     case NOT_FOUND:
707       if (check_interceptor() && HasInterceptor(map) &&
708           !SkipInterceptor(JSObject::cast(holder))) {
709         return INTERCEPTOR;
710       }
711     // Fall through.
712     default:
713       return NOT_FOUND;
714   }
715   UNREACHABLE();
716   return state_;
717 }
718 
719 }  // namespace internal
720 }  // namespace v8
721