• 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 
17 // static
PropertyOrElement(Isolate * isolate,Handle<Object> receiver,Handle<Object> key,bool * success,Configuration configuration)18 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
19                                                  Handle<Object> receiver,
20                                                  Handle<Object> key,
21                                                  bool* success,
22                                                  Configuration configuration) {
23   uint32_t index = 0;
24   if (key->ToArrayIndex(&index)) {
25     *success = true;
26     return LookupIterator(isolate, receiver, index, configuration);
27   }
28 
29   Handle<Name> name;
30   *success = Object::ToName(isolate, key).ToHandle(&name);
31   if (!*success) {
32     DCHECK(isolate->has_pending_exception());
33     // Return an unusable dummy.
34     return LookupIterator(receiver, isolate->factory()->empty_string());
35   }
36 
37   if (name->AsArrayIndex(&index)) {
38     LookupIterator it(isolate, receiver, index, configuration);
39     // Here we try to avoid having to rebuild the string later
40     // by storing it on the indexed LookupIterator.
41     it.name_ = name;
42     return it;
43   }
44 
45   return LookupIterator(receiver, name, configuration);
46 }
47 
48 template <bool is_element>
Start()49 void LookupIterator::Start() {
50   DisallowHeapAllocation no_gc;
51 
52   has_property_ = false;
53   state_ = NOT_FOUND;
54   holder_ = initial_holder_;
55 
56   JSReceiver* holder = *holder_;
57   Map* map = holder->map();
58 
59   state_ = LookupInHolder<is_element>(map, holder);
60   if (IsFound()) return;
61 
62   NextInternal<is_element>(map, holder);
63 }
64 
65 template void LookupIterator::Start<true>();
66 template void LookupIterator::Start<false>();
67 
Next()68 void LookupIterator::Next() {
69   DCHECK_NE(JSPROXY, state_);
70   DCHECK_NE(TRANSITION, state_);
71   DisallowHeapAllocation no_gc;
72   has_property_ = false;
73 
74   JSReceiver* holder = *holder_;
75   Map* map = holder->map();
76 
77   if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
78     state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
79                          : LookupInSpecialHolder<false>(map, holder);
80     if (IsFound()) return;
81   }
82 
83   IsElement() ? NextInternal<true>(map, holder)
84               : NextInternal<false>(map, holder);
85 }
86 
87 template <bool is_element>
NextInternal(Map * map,JSReceiver * holder)88 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
89   do {
90     JSReceiver* maybe_holder = NextHolder(map);
91     if (maybe_holder == nullptr) {
92       if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
93         RestartLookupForNonMaskingInterceptors<is_element>();
94         return;
95       }
96       state_ = NOT_FOUND;
97       if (holder != *holder_) holder_ = handle(holder, isolate_);
98       return;
99     }
100     holder = maybe_holder;
101     map = holder->map();
102     state_ = LookupInHolder<is_element>(map, holder);
103   } while (!IsFound());
104 
105   holder_ = handle(holder, isolate_);
106 }
107 
108 template <bool is_element>
RestartInternal(InterceptorState interceptor_state)109 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
110   interceptor_state_ = interceptor_state;
111   property_details_ = PropertyDetails::Empty();
112   number_ = DescriptorArray::kNotFound;
113   Start<is_element>();
114 }
115 
116 template void LookupIterator::RestartInternal<true>(InterceptorState);
117 template void LookupIterator::RestartInternal<false>(InterceptorState);
118 
119 // static
GetRootForNonJSReceiver(Isolate * isolate,Handle<Object> receiver,uint32_t index)120 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
121     Isolate* isolate, Handle<Object> receiver, uint32_t index) {
122   // Strings are the only objects with properties (only elements) directly on
123   // the wrapper. Hence we can skip generating the wrapper for all other cases.
124   if (index != kMaxUInt32 && receiver->IsString() &&
125       index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
126     // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
127     // context, ensuring that we don't leak it into JS?
128     Handle<JSFunction> constructor = isolate->string_function();
129     Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
130     Handle<JSValue>::cast(result)->set_value(*receiver);
131     return result;
132   }
133   auto root = handle(receiver->GetRootMap(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   }
198 }
199 
PrepareForDataProperty(Handle<Object> value)200 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
201   DCHECK(state_ == DATA || state_ == ACCESSOR);
202   DCHECK(HolderIsReceiverOrHiddenPrototype());
203 
204   Handle<JSObject> holder = GetHolder<JSObject>();
205 
206   if (IsElement()) {
207     ElementsKind kind = holder->GetElementsKind();
208     ElementsKind to = value->OptimalElementsKind();
209     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
210     to = GetMoreGeneralElementsKind(kind, to);
211 
212     if (kind != to) {
213       JSObject::TransitionElementsKind(holder, to);
214     }
215 
216     // Copy the backing store if it is copy-on-write.
217     if (IsFastSmiOrObjectElementsKind(to)) {
218       JSObject::EnsureWritableFastElements(holder);
219     }
220     return;
221   }
222 
223   if (!holder->HasFastProperties()) return;
224 
225   Handle<Map> old_map(holder->map(), isolate_);
226   Handle<Map> new_map =
227       Map::PrepareForDataProperty(old_map, descriptor_number(), value);
228 
229   if (old_map.is_identical_to(new_map)) {
230     // Update the property details if the representation was None.
231     if (representation().IsNone()) {
232       property_details_ =
233           new_map->instance_descriptors()->GetDetails(descriptor_number());
234     }
235     return;
236   }
237 
238   JSObject::MigrateToMap(holder, new_map);
239   ReloadPropertyInformation<false>();
240 }
241 
242 
ReconfigureDataProperty(Handle<Object> value,PropertyAttributes attributes)243 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
244                                              PropertyAttributes attributes) {
245   DCHECK(state_ == DATA || state_ == ACCESSOR);
246   DCHECK(HolderIsReceiverOrHiddenPrototype());
247   Handle<JSObject> holder = GetHolder<JSObject>();
248   if (IsElement()) {
249     DCHECK(!holder->HasFixedTypedArrayElements());
250     DCHECK(attributes != NONE || !holder->HasFastElements());
251     Handle<FixedArrayBase> elements(holder->elements());
252     holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
253                                                attributes);
254     ReloadPropertyInformation<true>();
255   } else {
256     if (!holder->HasFastProperties()) {
257       PropertyDetails details(attributes, v8::internal::DATA, 0,
258                               PropertyCellType::kMutable);
259       JSObject::SetNormalizedProperty(holder, name(), value, details);
260     } else {
261       Handle<Map> old_map(holder->map(), isolate_);
262       Handle<Map> new_map = Map::ReconfigureExistingProperty(
263           old_map, descriptor_number(), i::kData, attributes);
264       new_map =
265           Map::PrepareForDataProperty(new_map, descriptor_number(), value);
266       JSObject::MigrateToMap(holder, new_map);
267     }
268     ReloadPropertyInformation<false>();
269   }
270 
271   WriteDataValue(value);
272 
273 #if VERIFY_HEAP
274   if (FLAG_verify_heap) {
275     holder->JSObjectVerify();
276   }
277 #endif
278 }
279 
280 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
281 // via a trap. Adding properties to primitive values is not observable.
PrepareTransitionToDataProperty(Handle<JSObject> receiver,Handle<Object> value,PropertyAttributes attributes,Object::StoreFromKeyed store_mode)282 void LookupIterator::PrepareTransitionToDataProperty(
283     Handle<JSObject> receiver, Handle<Object> value,
284     PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
285   DCHECK(receiver.is_identical_to(GetStoreTarget()));
286   if (state_ == TRANSITION) return;
287   DCHECK(state_ != LookupIterator::ACCESSOR ||
288          (GetAccessors()->IsAccessorInfo() &&
289           AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
290   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
291   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
292 
293   Handle<Map> map(receiver->map(), isolate_);
294 
295   // Dictionary maps can always have additional data properties.
296   if (map->is_dictionary_map()) {
297     state_ = TRANSITION;
298     if (map->IsJSGlobalObjectMap()) {
299       // Install a property cell.
300       auto cell = JSGlobalObject::EnsurePropertyCell(
301           Handle<JSGlobalObject>::cast(receiver), name());
302       DCHECK(cell->value()->IsTheHole(isolate_));
303       transition_ = cell;
304     } else {
305       transition_ = map;
306     }
307     return;
308   }
309 
310   Handle<Map> transition =
311       Map::TransitionToDataProperty(map, name_, value, attributes, store_mode);
312   state_ = TRANSITION;
313   transition_ = transition;
314 
315   if (!transition->is_dictionary_map()) {
316     property_details_ = transition->GetLastDescriptorDetails();
317     has_property_ = true;
318   }
319 }
320 
ApplyTransitionToDataProperty(Handle<JSObject> receiver)321 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
322   DCHECK_EQ(TRANSITION, state_);
323 
324   DCHECK(receiver.is_identical_to(GetStoreTarget()));
325 
326   if (receiver->IsJSGlobalObject()) return;
327   holder_ = receiver;
328   Handle<Map> transition = transition_map();
329   bool simple_transition = transition->GetBackPointer() == receiver->map();
330   JSObject::MigrateToMap(receiver, transition);
331 
332   if (simple_transition) {
333     int number = transition->LastAdded();
334     number_ = static_cast<uint32_t>(number);
335     property_details_ = transition->GetLastDescriptorDetails();
336     state_ = DATA;
337   } else {
338     ReloadPropertyInformation<false>();
339   }
340 }
341 
342 
Delete()343 void LookupIterator::Delete() {
344   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
345   if (IsElement()) {
346     Handle<JSObject> object = Handle<JSObject>::cast(holder);
347     ElementsAccessor* accessor = object->GetElementsAccessor();
348     accessor->Delete(object, number_);
349   } else {
350     bool is_prototype_map = holder->map()->is_prototype_map();
351     RuntimeCallTimerScope stats_scope(
352         isolate_, is_prototype_map
353                       ? &RuntimeCallStats::PrototypeObject_DeleteProperty
354                       : &RuntimeCallStats::Object_DeleteProperty);
355 
356     PropertyNormalizationMode mode =
357         is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
358 
359     if (holder->HasFastProperties()) {
360       JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
361                                     "DeletingProperty");
362       ReloadPropertyInformation<false>();
363     }
364     // TODO(verwaest): Get rid of the name_ argument.
365     JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
366     if (holder->IsJSObject()) {
367       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
368     }
369   }
370   state_ = NOT_FOUND;
371 }
372 
TransitionToAccessorProperty(Handle<Object> getter,Handle<Object> setter,PropertyAttributes attributes)373 void LookupIterator::TransitionToAccessorProperty(
374     Handle<Object> getter, Handle<Object> setter,
375     PropertyAttributes attributes) {
376   DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
377   // Can only be called when the receiver is a JSObject. JSProxy has to be
378   // handled via a trap. Adding properties to primitive values is not
379   // observable.
380   Handle<JSObject> receiver = GetStoreTarget();
381 
382   if (!IsElement() && !receiver->map()->is_dictionary_map()) {
383     Handle<Map> old_map(receiver->map(), isolate_);
384 
385     if (!holder_.is_identical_to(receiver)) {
386       holder_ = receiver;
387       state_ = NOT_FOUND;
388     } else if (state_ == INTERCEPTOR) {
389       LookupInRegularHolder<false>(*old_map, *holder_);
390     }
391     int descriptor =
392         IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
393 
394     Handle<Map> new_map = Map::TransitionToAccessorProperty(
395         isolate_, old_map, name_, descriptor, getter, setter, attributes);
396     bool simple_transition = new_map->GetBackPointer() == receiver->map();
397     JSObject::MigrateToMap(receiver, new_map);
398 
399     if (simple_transition) {
400       int number = new_map->LastAdded();
401       number_ = static_cast<uint32_t>(number);
402       property_details_ = new_map->GetLastDescriptorDetails();
403       state_ = ACCESSOR;
404       return;
405     }
406 
407     ReloadPropertyInformation<false>();
408     if (!new_map->is_dictionary_map()) return;
409   }
410 
411   Handle<AccessorPair> pair;
412   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
413     pair = Handle<AccessorPair>::cast(GetAccessors());
414     // If the component and attributes are identical, nothing has to be done.
415     if (pair->Equals(*getter, *setter)) {
416       if (property_details().attributes() == attributes) {
417         if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
418         return;
419       }
420     } else {
421       pair = AccessorPair::Copy(pair);
422       pair->SetComponents(*getter, *setter);
423     }
424   } else {
425     pair = factory()->NewAccessorPair();
426     pair->SetComponents(*getter, *setter);
427   }
428 
429   TransitionToAccessorPair(pair, attributes);
430 
431 #if VERIFY_HEAP
432   if (FLAG_verify_heap) {
433     receiver->JSObjectVerify();
434   }
435 #endif
436 }
437 
438 
TransitionToAccessorPair(Handle<Object> pair,PropertyAttributes attributes)439 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
440                                               PropertyAttributes attributes) {
441   Handle<JSObject> receiver = GetStoreTarget();
442   holder_ = receiver;
443 
444   PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
445                           PropertyCellType::kMutable);
446 
447   if (IsElement()) {
448     // TODO(verwaest): Move code into the element accessor.
449     Handle<SeededNumberDictionary> dictionary =
450         JSObject::NormalizeElements(receiver);
451 
452     // We unconditionally pass used_as_prototype=false here because the call
453     // to RequireSlowElements takes care of the required IC clearing and
454     // we don't want to walk the heap twice.
455     dictionary =
456         SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
457     receiver->RequireSlowElements(*dictionary);
458 
459     if (receiver->HasSlowArgumentsElements()) {
460       FixedArray* parameter_map = FixedArray::cast(receiver->elements());
461       uint32_t length = parameter_map->length() - 2;
462       if (number_ < length) {
463         parameter_map->set(number_ + 2, heap()->the_hole_value());
464       }
465       FixedArray::cast(receiver->elements())->set(1, *dictionary);
466     } else {
467       receiver->set_elements(*dictionary);
468     }
469 
470     ReloadPropertyInformation<true>();
471   } else {
472     PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
473                                          ? KEEP_INOBJECT_PROPERTIES
474                                          : CLEAR_INOBJECT_PROPERTIES;
475     // Normalize object to make this operation simple.
476     JSObject::NormalizeProperties(receiver, mode, 0,
477                                   "TransitionToAccessorPair");
478 
479     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
480     JSObject::ReoptimizeIfPrototype(receiver);
481 
482     ReloadPropertyInformation<false>();
483   }
484 }
485 
486 
HolderIsReceiverOrHiddenPrototype() const487 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
488   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
489   // Optimization that only works if configuration_ is not mutable.
490   if (!check_prototype_chain()) return true;
491   DisallowHeapAllocation no_gc;
492   if (*receiver_ == *holder_) return true;
493   if (!receiver_->IsJSReceiver()) return false;
494   JSReceiver* current = JSReceiver::cast(*receiver_);
495   JSReceiver* object = *holder_;
496   if (!current->map()->has_hidden_prototype()) return false;
497   // JSProxy do not occur as hidden prototypes.
498   if (object->IsJSProxy()) return false;
499   PrototypeIterator iter(isolate(), current, kStartAtPrototype,
500                          PrototypeIterator::END_AT_NON_HIDDEN);
501   while (!iter.IsAtEnd()) {
502     if (iter.GetCurrent<JSReceiver>() == object) return true;
503     iter.Advance();
504   }
505   return false;
506 }
507 
508 
FetchValue() const509 Handle<Object> LookupIterator::FetchValue() const {
510   Object* result = NULL;
511   if (IsElement()) {
512     Handle<JSObject> holder = GetHolder<JSObject>();
513     ElementsAccessor* accessor = holder->GetElementsAccessor();
514     return accessor->Get(holder, number_);
515   } else if (holder_->IsJSGlobalObject()) {
516     Handle<JSObject> holder = GetHolder<JSObject>();
517     result = holder->global_dictionary()->ValueAt(number_);
518     DCHECK(result->IsPropertyCell());
519     result = PropertyCell::cast(result)->value();
520   } else if (!holder_->HasFastProperties()) {
521     result = holder_->property_dictionary()->ValueAt(number_);
522   } else if (property_details_.type() == v8::internal::DATA) {
523     Handle<JSObject> holder = GetHolder<JSObject>();
524     FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
525     return JSObject::FastPropertyAt(holder, property_details_.representation(),
526                                     field_index);
527   } else {
528     result = holder_->map()->instance_descriptors()->GetValue(number_);
529   }
530   return handle(result, isolate_);
531 }
532 
533 
GetAccessorIndex() const534 int LookupIterator::GetAccessorIndex() const {
535   DCHECK(has_property_);
536   DCHECK(holder_->HasFastProperties());
537   DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
538   return descriptor_number();
539 }
540 
541 
GetConstantIndex() const542 int LookupIterator::GetConstantIndex() const {
543   DCHECK(has_property_);
544   DCHECK(holder_->HasFastProperties());
545   DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
546   DCHECK(!IsElement());
547   return descriptor_number();
548 }
549 
550 
GetFieldIndex() const551 FieldIndex LookupIterator::GetFieldIndex() const {
552   DCHECK(has_property_);
553   DCHECK(holder_->HasFastProperties());
554   DCHECK_EQ(v8::internal::DATA, property_details_.type());
555   DCHECK(!IsElement());
556   Map* holder_map = holder_->map();
557   int index =
558       holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
559   bool is_double = representation().IsDouble();
560   return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
561 }
562 
GetFieldType() const563 Handle<FieldType> LookupIterator::GetFieldType() const {
564   DCHECK(has_property_);
565   DCHECK(holder_->HasFastProperties());
566   DCHECK_EQ(v8::internal::DATA, property_details_.type());
567   return handle(
568       holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
569       isolate_);
570 }
571 
572 
GetPropertyCell() const573 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
574   DCHECK(!IsElement());
575   Handle<JSObject> holder = GetHolder<JSObject>();
576   Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
577   Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
578   DCHECK(value->IsPropertyCell());
579   return handle(PropertyCell::cast(value));
580 }
581 
582 
GetAccessors() const583 Handle<Object> LookupIterator::GetAccessors() const {
584   DCHECK_EQ(ACCESSOR, state_);
585   return FetchValue();
586 }
587 
588 
GetDataValue() const589 Handle<Object> LookupIterator::GetDataValue() const {
590   DCHECK_EQ(DATA, state_);
591   Handle<Object> value = FetchValue();
592   return value;
593 }
594 
595 
WriteDataValue(Handle<Object> value)596 void LookupIterator::WriteDataValue(Handle<Object> value) {
597   DCHECK_EQ(DATA, state_);
598   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
599   if (IsElement()) {
600     Handle<JSObject> object = Handle<JSObject>::cast(holder);
601     ElementsAccessor* accessor = object->GetElementsAccessor();
602     accessor->Set(object, number_, *value);
603   } else if (holder->HasFastProperties()) {
604     if (property_details_.type() == v8::internal::DATA) {
605       JSObject::cast(*holder)->WriteToField(descriptor_number(),
606                                             property_details_, *value);
607     } else {
608       DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
609     }
610   } else if (holder->IsJSGlobalObject()) {
611     Handle<GlobalDictionary> property_dictionary =
612         handle(JSObject::cast(*holder)->global_dictionary());
613     PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
614                              property_details_);
615   } else {
616     NameDictionary* property_dictionary = holder->property_dictionary();
617     property_dictionary->ValueAtPut(dictionary_entry(), *value);
618   }
619 }
620 
621 template <bool is_element>
SkipInterceptor(JSObject * holder)622 bool LookupIterator::SkipInterceptor(JSObject* holder) {
623   auto info = GetInterceptor<is_element>(holder);
624   // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
625   if (info->non_masking()) {
626     switch (interceptor_state_) {
627       case InterceptorState::kUninitialized:
628         interceptor_state_ = InterceptorState::kSkipNonMasking;
629       // Fall through.
630       case InterceptorState::kSkipNonMasking:
631         return true;
632       case InterceptorState::kProcessNonMasking:
633         return false;
634     }
635   }
636   return interceptor_state_ == InterceptorState::kProcessNonMasking;
637 }
638 
NextHolder(Map * map)639 JSReceiver* LookupIterator::NextHolder(Map* map) {
640   DisallowHeapAllocation no_gc;
641   if (map->prototype() == heap()->null_value()) return NULL;
642   if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
643   return JSReceiver::cast(map->prototype());
644 }
645 
NotFound(JSReceiver * const holder) const646 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
647   DCHECK(!IsElement());
648   if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
649 
650   Handle<String> name_string = Handle<String>::cast(name_);
651   if (name_string->length() == 0) return NOT_FOUND;
652 
653   return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
654              ? INTEGER_INDEXED_EXOTIC
655              : NOT_FOUND;
656 }
657 
658 namespace {
659 
660 template <bool is_element>
HasInterceptor(Map * map)661 bool HasInterceptor(Map* map) {
662   return is_element ? map->has_indexed_interceptor()
663                     : map->has_named_interceptor();
664 }
665 
666 }  // namespace
667 
668 template <bool is_element>
LookupInSpecialHolder(Map * const map,JSReceiver * const holder)669 LookupIterator::State LookupIterator::LookupInSpecialHolder(
670     Map* const map, JSReceiver* const holder) {
671   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
672   switch (state_) {
673     case NOT_FOUND:
674       if (map->IsJSProxyMap()) {
675         if (is_element || !name_->IsPrivate()) return JSPROXY;
676       }
677       if (map->is_access_check_needed()) {
678         if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
679       }
680     // Fall through.
681     case ACCESS_CHECK:
682       if (check_interceptor() && HasInterceptor<is_element>(map) &&
683           !SkipInterceptor<is_element>(JSObject::cast(holder))) {
684         if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
685       }
686     // Fall through.
687     case INTERCEPTOR:
688       if (!is_element && map->IsJSGlobalObjectMap()) {
689         GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
690         int number = dict->FindEntry(name_);
691         if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
692         number_ = static_cast<uint32_t>(number);
693         DCHECK(dict->ValueAt(number_)->IsPropertyCell());
694         PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
695         if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
696         property_details_ = cell->property_details();
697         has_property_ = true;
698         switch (property_details_.kind()) {
699           case v8::internal::kData:
700             return DATA;
701           case v8::internal::kAccessor:
702             return ACCESSOR;
703         }
704       }
705       return LookupInRegularHolder<is_element>(map, holder);
706     case ACCESSOR:
707     case DATA:
708       return NOT_FOUND;
709     case INTEGER_INDEXED_EXOTIC:
710     case JSPROXY:
711     case TRANSITION:
712       UNREACHABLE();
713   }
714   UNREACHABLE();
715   return NOT_FOUND;
716 }
717 
718 template <bool is_element>
LookupInRegularHolder(Map * const map,JSReceiver * const holder)719 LookupIterator::State LookupIterator::LookupInRegularHolder(
720     Map* const map, JSReceiver* const holder) {
721   DisallowHeapAllocation no_gc;
722   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
723     return NOT_FOUND;
724   }
725 
726   if (is_element) {
727     JSObject* js_object = JSObject::cast(holder);
728     ElementsAccessor* accessor = js_object->GetElementsAccessor();
729     FixedArrayBase* backing_store = js_object->elements();
730     number_ = accessor->GetEntryForIndex(js_object, backing_store, index_);
731     if (number_ == kMaxUInt32) {
732       return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
733     }
734     property_details_ = accessor->GetDetails(js_object, number_);
735   } else if (!map->is_dictionary_map()) {
736     DescriptorArray* descriptors = map->instance_descriptors();
737     int number = descriptors->SearchWithCache(isolate_, *name_, map);
738     if (number == DescriptorArray::kNotFound) return NotFound(holder);
739     number_ = static_cast<uint32_t>(number);
740     property_details_ = descriptors->GetDetails(number_);
741   } else {
742     NameDictionary* dict = holder->property_dictionary();
743     int number = dict->FindEntry(name_);
744     if (number == NameDictionary::kNotFound) return NotFound(holder);
745     number_ = static_cast<uint32_t>(number);
746     property_details_ = dict->DetailsAt(number_);
747   }
748   has_property_ = true;
749   switch (property_details_.kind()) {
750     case v8::internal::kData:
751       return DATA;
752     case v8::internal::kAccessor:
753       return ACCESSOR;
754   }
755 
756   UNREACHABLE();
757   return state_;
758 }
759 
GetInterceptorForFailedAccessCheck() const760 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
761     const {
762   DCHECK_EQ(ACCESS_CHECK, state_);
763   DisallowHeapAllocation no_gc;
764   AccessCheckInfo* access_check_info =
765       AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
766   if (access_check_info) {
767     Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
768                                       : access_check_info->named_interceptor();
769     if (interceptor) {
770       return handle(InterceptorInfo::cast(interceptor), isolate_);
771     }
772   }
773   return Handle<InterceptorInfo>();
774 }
775 
776 }  // namespace internal
777 }  // namespace v8
778