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