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/counters.h"
9#include "src/deoptimizer.h"
10#include "src/elements.h"
11#include "src/field-type.h"
12#include "src/isolate-inl.h"
13#include "src/objects/hash-table-inl.h"
14#include "src/objects/heap-number-inl.h"
15#include "src/objects/struct-inl.h"
16
17namespace v8 {
18namespace internal {
19
20// static
21LookupIterator LookupIterator::PropertyOrElement(
22 Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
23 bool* success, Handle<JSReceiver> holder, Configuration configuration) {
24 uint32_t index = 0;
25 if (key->ToArrayIndex(&index)) {
26 *success = true;
27 return LookupIterator(isolate, receiver, index, holder, configuration);
28 }
29
30 Handle<Name> name;
31 *success = Object::ToName(isolate, key).ToHandle(&name);
32 if (!*success) {
33 DCHECK(isolate->has_pending_exception());
34 // Return an unusable dummy.
35 return LookupIterator(isolate, receiver,
36 isolate->factory()->empty_string());
37 }
38
39 if (name->AsArrayIndex(&index)) {
40 LookupIterator it(isolate, receiver, index, holder, configuration);
41 // Here we try to avoid having to rebuild the string later
42 // by storing it on the indexed LookupIterator.
43 it.name_ = name;
44 return it;
45 }
46
47 return LookupIterator(receiver, name, holder, configuration);
48}
49
50// static
51LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
52 Handle<Object> receiver,
53 Handle<Object> key,
54 bool* success,
55 Configuration configuration) {
56 // TODO(mslekova): come up with better way to avoid duplication
57 uint32_t index = 0;
58 if (key->ToArrayIndex(&index)) {
59 *success = true;
60 return LookupIterator(isolate, receiver, index, configuration);
61 }
62
63 Handle<Name> name;
64 *success = Object::ToName(isolate, key).ToHandle(&name);
65 if (!*success) {
66 DCHECK(isolate->has_pending_exception());
67 // Return an unusable dummy.
68 return LookupIterator(isolate, receiver,
69 isolate->factory()->empty_string());
70 }
71
72 if (name->AsArrayIndex(&index)) {
73 LookupIterator it(isolate, receiver, index, configuration);
74 // Here we try to avoid having to rebuild the string later
75 // by storing it on the indexed LookupIterator.
76 it.name_ = name;
77 return it;
78 }
79
80 return LookupIterator(isolate, receiver, name, configuration);
81}
82
83// TODO(ishell): Consider removing this way of LookupIterator creation.
84// static
85LookupIterator LookupIterator::ForTransitionHandler(
86 Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
87 Handle<Object> value, MaybeHandle<Map> maybe_transition_map) {
88 Handle<Map> transition_map;
89 if (!maybe_transition_map.ToHandle(&transition_map) ||
90 !transition_map->IsPrototypeValidityCellValid()) {
91 // This map is not a valid transition handler, so full lookup is required.
92 return LookupIterator(isolate, receiver, name);
93 }
94
95 PropertyDetails details = PropertyDetails::Empty();
96 bool has_property;
97 if (transition_map->is_dictionary_map()) {
98 details = PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
99 has_property = false;
100 } else {
101 details = transition_map->GetLastDescriptorDetails();
102 has_property = true;
103 }
104#ifdef DEBUG
105 if (name->IsPrivate()) {
106 DCHECK_EQ(DONT_ENUM, details.attributes());
107 } else {
108 DCHECK_EQ(NONE, details.attributes());
109 }
110#endif
111 LookupIterator it(isolate, receiver, name, transition_map, details,
112 has_property);
113
114 if (!transition_map->is_dictionary_map()) {
115 int descriptor_number = transition_map->LastAdded();
116 Handle<Map> new_map =
117 Map::PrepareForDataProperty(isolate, transition_map, descriptor_number,
118 PropertyConstness::kConst, value);
119 // Reload information; this is no-op if nothing changed.
120 it.property_details_ =
121 new_map->instance_descriptors()->GetDetails(descriptor_number);
122 it.transition_ = new_map;
123 }
124 return it;
125}
126
127LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
128 Handle<Name> name, Handle<Map> transition_map,
129 PropertyDetails details, bool has_property)
130 : configuration_(DEFAULT),
131 state_(TRANSITION),
132 has_property_(has_property),
133 interceptor_state_(InterceptorState::kUninitialized),
134 property_details_(details),
135 isolate_(isolate),
136 name_(name),
137 transition_(transition_map),
138 receiver_(receiver),
139 initial_holder_(GetRoot(isolate, receiver)),
140 index_(kMaxUInt32),
141 number_(static_cast<uint32_t>(DescriptorArray::kNotFound)) {
142 holder_ = initial_holder_;
143}
144
145template <bool is_element>
146void LookupIterator::Start() {
147 DisallowHeapAllocation no_gc;
148
149 has_property_ = false;
150 state_ = NOT_FOUND;
151 holder_ = initial_holder_;
152
153 JSReceiver holder = *holder_;
154 Map map = holder->map();
155
156 state_ = LookupInHolder<is_element>(map, holder);
157 if (IsFound()) return;
158
159 NextInternal<is_element>(map, holder);
160}
161
162template void LookupIterator::Start<true>();
163template void LookupIterator::Start<false>();
164
165void LookupIterator::Next() {
166 DCHECK_NE(JSPROXY, state_);
167 DCHECK_NE(TRANSITION, state_);
168 DisallowHeapAllocation no_gc;
169 has_property_ = false;
170
171 JSReceiver holder = *holder_;
172 Map map = holder->map();
173
174 if (map->IsSpecialReceiverMap()) {
175 state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
176 : LookupInSpecialHolder<false>(map, holder);
177 if (IsFound()) return;
178 }
179
180 IsElement() ? NextInternal<true>(map, holder)
181 : NextInternal<false>(map, holder);
182}
183
184template <bool is_element>
185void LookupIterator::NextInternal(Map map, JSReceiver holder) {
186 do {
187 JSReceiver maybe_holder = NextHolder(map);
188 if (maybe_holder.is_null()) {
189 if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
190 RestartLookupForNonMaskingInterceptors<is_element>();
191 return;
192 }
193 state_ = NOT_FOUND;
194 if (holder != *holder_) holder_ = handle(holder, isolate_);
195 return;
196 }
197 holder = maybe_holder;
198 map = holder->map();
199 state_ = LookupInHolder<is_element>(map, holder);
200 } while (!IsFound());
201
202 holder_ = handle(holder, isolate_);
203}
204
205template <bool is_element>
206void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
207 interceptor_state_ = interceptor_state;
208 property_details_ = PropertyDetails::Empty();
209 number_ = static_cast<uint32_t>(DescriptorArray::kNotFound);
210 Start<is_element>();
211}
212
213template void LookupIterator::RestartInternal<true>(InterceptorState);
214template void LookupIterator::RestartInternal<false>(InterceptorState);
215
216// static
217Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
218 Isolate* isolate, Handle<Object> receiver, uint32_t index) {
219 // Strings are the only objects with properties (only elements) directly on
220 // the wrapper. Hence we can skip generating the wrapper for all other cases.
221 if (receiver->IsString() &&
222 index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
223 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
224 // context, ensuring that we don't leak it into JS?
225 Handle<JSFunction> constructor = isolate->string_function();
226 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
227 Handle<JSValue>::cast(result)->set_value(*receiver);
228 return result;
229 }
230 auto root =
231 handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
232 if (root->IsNull(isolate)) {
233 isolate->PushStackTraceAndDie(reinterpret_cast<void*>(receiver->ptr()));
234 }
235 return Handle<JSReceiver>::cast(root);
236}
237
238
239Handle<Map> LookupIterator::GetReceiverMap() const {
240 if (receiver_->IsNumber()) return factory()->heap_number_map();
241 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
242}
243
244bool LookupIterator::HasAccess() const {
245 DCHECK_EQ(ACCESS_CHECK, state_);
246 return isolate_->MayAccess(handle(isolate_->context(), isolate_),
247 GetHolder<JSObject>());
248}
249
250template <bool is_element>
251void LookupIterator::ReloadPropertyInformation() {
252 state_ = BEFORE_PROPERTY;
253 interceptor_state_ = InterceptorState::kUninitialized;
254 state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
255 DCHECK(IsFound() || !holder_->HasFastProperties());
256}
257
258namespace {
259
260bool IsTypedArrayFunctionInAnyContext(Isolate* isolate, JSReceiver holder) {
261 static uint32_t context_slots[] = {
262#define TYPED_ARRAY_CONTEXT_SLOTS(Type, type, TYPE, ctype) \
263 Context::TYPE##_ARRAY_FUN_INDEX,
264
265 TYPED_ARRAYS(TYPED_ARRAY_CONTEXT_SLOTS)
266#undef TYPED_ARRAY_CONTEXT_SLOTS
267 };
268
269 if (!holder->IsJSFunction()) return false;
270
271 return std::any_of(
272 std::begin(context_slots), std::end(context_slots),
273 [=](uint32_t slot) { return isolate->IsInAnyContext(holder, slot); });
274}
275
276} // namespace
277
278void LookupIterator::InternalUpdateProtector() {
279 if (isolate_->bootstrapper()->IsActive()) return;
280
281 ReadOnlyRoots roots(heap());
282 if (*name_ == roots.constructor_string()) {
283 if (!isolate_->IsArraySpeciesLookupChainIntact() &&
284 !isolate_->IsPromiseSpeciesLookupChainIntact() &&
285 !isolate_->IsRegExpSpeciesLookupChainIntact() &&
286 !isolate_->IsTypedArraySpeciesLookupChainIntact()) {
287 return;
288 }
289 // Setting the constructor property could change an instance's @@species
290 if (holder_->IsJSArray()) {
291 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
292 isolate_->CountUsage(
293 v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
294 isolate_->InvalidateArraySpeciesProtector();
295 return;
296 } else if (holder_->IsJSPromise()) {
297 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
298 isolate_->InvalidatePromiseSpeciesProtector();
299 return;
300 } else if (holder_->IsJSRegExp()) {
301 if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
302 isolate_->InvalidateRegExpSpeciesProtector();
303 return;
304 } else if (holder_->IsJSTypedArray()) {
305 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
306 isolate_->InvalidateTypedArraySpeciesProtector();
307 return;
308 }
309 if (holder_->map()->is_prototype_map()) {
310 DisallowHeapAllocation no_gc;
311 // Setting the constructor of any prototype with the @@species protector
312 // (of any realm) also needs to invalidate the protector.
313 // For typed arrays, we check a prototype of this holder since TypedArrays
314 // have different prototypes for each type, and their parent prototype is
315 // pointing the same TYPED_ARRAY_PROTOTYPE.
316 if (isolate_->IsInAnyContext(*holder_,
317 Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
318 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
319 isolate_->CountUsage(
320 v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified);
321 isolate_->InvalidateArraySpeciesProtector();
322 } else if (isolate_->IsInAnyContext(*holder_,
323 Context::PROMISE_PROTOTYPE_INDEX)) {
324 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
325 isolate_->InvalidatePromiseSpeciesProtector();
326 } else if (isolate_->IsInAnyContext(*holder_,
327 Context::REGEXP_PROTOTYPE_INDEX)) {
328 if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
329 isolate_->InvalidateRegExpSpeciesProtector();
330 } else if (isolate_->IsInAnyContext(
331 holder_->map()->prototype(),
332 Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
333 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
334 isolate_->InvalidateTypedArraySpeciesProtector();
335 }
336 }
337 } else if (*name_ == roots.next_string()) {
338 if (isolate_->IsInAnyContext(
339 *holder_, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)) {
340 // Setting the next property of %ArrayIteratorPrototype% also needs to
341 // invalidate the array iterator protector.
342 if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
343 isolate_->InvalidateArrayIteratorProtector();
344 } else if (isolate_->IsInAnyContext(
345 *holder_, Context::INITIAL_MAP_ITERATOR_PROTOTYPE_INDEX)) {
346 if (!isolate_->IsMapIteratorLookupChainIntact()) return;
347 isolate_->InvalidateMapIteratorProtector();
348 } else if (isolate_->IsInAnyContext(
349 *holder_, Context::INITIAL_SET_ITERATOR_PROTOTYPE_INDEX)) {
350 if (!isolate_->IsSetIteratorLookupChainIntact()) return;
351 isolate_->InvalidateSetIteratorProtector();
352 } else if (isolate_->IsInAnyContext(
353 *receiver_,
354 Context::INITIAL_STRING_ITERATOR_PROTOTYPE_INDEX)) {
355 // Setting the next property of %StringIteratorPrototype% invalidates the
356 // string iterator protector.
357 if (!isolate_->IsStringIteratorLookupChainIntact()) return;
358 isolate_->InvalidateStringIteratorProtector();
359 }
360 } else if (*name_ == roots.species_symbol()) {
361 if (!isolate_->IsArraySpeciesLookupChainIntact() &&
362 !isolate_->IsPromiseSpeciesLookupChainIntact() &&
363 !isolate_->IsRegExpSpeciesLookupChainIntact() &&
364 !isolate_->IsTypedArraySpeciesLookupChainIntact()) {
365 return;
366 }
367 // Setting the Symbol.species property of any Array, Promise or TypedArray
368 // constructor invalidates the @@species protector
369 if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
370 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
371 isolate_->CountUsage(
372 v8::Isolate::UseCounterFeature::kArraySpeciesModified);
373 isolate_->InvalidateArraySpeciesProtector();
374 } else if (isolate_->IsInAnyContext(*holder_,
375 Context::PROMISE_FUNCTION_INDEX)) {
376 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
377 isolate_->InvalidatePromiseSpeciesProtector();
378 } else if (isolate_->IsInAnyContext(*holder_,
379 Context::REGEXP_FUNCTION_INDEX)) {
380 if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
381 isolate_->InvalidateRegExpSpeciesProtector();
382 } else if (IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
383 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
384 isolate_->InvalidateTypedArraySpeciesProtector();
385 }
386 } else if (*name_ == roots.is_concat_spreadable_symbol()) {
387 if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
388 isolate_->InvalidateIsConcatSpreadableProtector();
389 } else if (*name_ == roots.iterator_symbol()) {
390 if (holder_->IsJSArray()) {
391 if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
392 isolate_->InvalidateArrayIteratorProtector();
393 } else if (isolate_->IsInAnyContext(
394 *holder_, Context::INITIAL_ITERATOR_PROTOTYPE_INDEX)) {
395 if (isolate_->IsMapIteratorLookupChainIntact()) {
396 isolate_->InvalidateMapIteratorProtector();
397 }
398 if (isolate_->IsSetIteratorLookupChainIntact()) {
399 isolate_->InvalidateSetIteratorProtector();
400 }
401 } else if (isolate_->IsInAnyContext(*holder_,
402 Context::INITIAL_SET_PROTOTYPE_INDEX)) {
403 if (!isolate_->IsSetIteratorLookupChainIntact()) return;
404 isolate_->InvalidateSetIteratorProtector();
405 } else if (isolate_->IsInAnyContext(
406 *receiver_, Context::INITIAL_STRING_PROTOTYPE_INDEX)) {
407 // Setting the Symbol.iterator property of String.prototype invalidates
408 // the string iterator protector. Symbol.iterator can also be set on a
409 // String wrapper, but not on a primitive string. We only support
410 // protector for primitive strings.
411 if (!isolate_->IsStringIteratorLookupChainIntact()) return;
412 isolate_->InvalidateStringIteratorProtector();
413 }
414 } else if (*name_ == roots.resolve_string()) {
415 if (!isolate_->IsPromiseResolveLookupChainIntact()) return;
416 // Setting the "resolve" property on any %Promise% intrinsic object
417 // invalidates the Promise.resolve protector.
418 if (isolate_->IsInAnyContext(*holder_, Context::PROMISE_FUNCTION_INDEX)) {
419 isolate_->InvalidatePromiseResolveProtector();
420 }
421 } else if (*name_ == roots.then_string()) {
422 if (!isolate_->IsPromiseThenLookupChainIntact()) return;
423 // Setting the "then" property on any JSPromise instance or on the
424 // initial %PromisePrototype% invalidates the Promise#then protector.
425 // Also setting the "then" property on the initial %ObjectPrototype%
426 // invalidates the Promise#then protector, since we use this protector
427 // to guard the fast-path in AsyncGeneratorResolve, where we can skip
428 // the ResolvePromise step and go directly to FulfillPromise if we
429 // know that the Object.prototype doesn't contain a "then" method.
430 if (holder_->IsJSPromise() ||
431 isolate_->IsInAnyContext(*holder_,
432 Context::INITIAL_OBJECT_PROTOTYPE_INDEX) ||
433 isolate_->IsInAnyContext(*holder_, Context::PROMISE_PROTOTYPE_INDEX)) {
434 isolate_->InvalidatePromiseThenProtector();
435 }
436 }
437}
438
439void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
440 DCHECK(state_ == DATA || state_ == ACCESSOR);
441 DCHECK(HolderIsReceiverOrHiddenPrototype());
442
443 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
444 // JSProxy does not have fast properties so we do an early return.
445 DCHECK_IMPLIES(holder->IsJSProxy(), !holder->HasFastProperties());
446 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
447 if (holder->IsJSProxy()) return;
448
449 Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
450
451 if (IsElement()) {
452 ElementsKind kind = holder_obj->GetElementsKind();
453 ElementsKind to = value->OptimalElementsKind();
454 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
455 to = GetMoreGeneralElementsKind(kind, to);
456
457 if (kind != to) {
458 JSObject::TransitionElementsKind(holder_obj, to);
459 }
460
461 // Copy the backing store if it is copy-on-write.
462 if (IsSmiOrObjectElementsKind(to) || IsSealedElementsKind(to)) {
463 JSObject::EnsureWritableFastElements(holder_obj);
464 }
465 return;
466 }
467
468 if (holder_obj->IsJSGlobalObject()) {
469 Handle<GlobalDictionary> dictionary(
470 JSGlobalObject::cast(*holder_obj)->global_dictionary(), isolate());
471 Handle<PropertyCell> cell(dictionary->CellAt(dictionary_entry()),
472 isolate());
473 property_details_ = cell->property_details();
474 PropertyCell::PrepareForValue(isolate(), dictionary, dictionary_entry(),
475 value, property_details_);
476 return;
477 }
478 if (!holder_obj->HasFastProperties()) return;
479
480 PropertyConstness new_constness = PropertyConstness::kConst;
481 if (FLAG_track_constant_fields) {
482 if (constness() == PropertyConstness::kConst) {
483 DCHECK_EQ(kData, property_details_.kind());
484 // Check that current value matches new value otherwise we should make
485 // the property mutable.
486 if (!IsConstFieldValueEqualTo(*value))
487 new_constness = PropertyConstness::kMutable;
488 }
489 } else {
490 new_constness = PropertyConstness::kMutable;
491 }
492
493 Handle<Map> old_map(holder_obj->map(), isolate_);
494 Handle<Map> new_map = Map::PrepareForDataProperty(
495 isolate(), old_map, descriptor_number(), new_constness, value);
496
497 if (old_map.is_identical_to(new_map)) {
498 // Update the property details if the representation was None.
499 if (constness() != new_constness || representation().IsNone()) {
500 property_details_ =
501 new_map->instance_descriptors()->GetDetails(descriptor_number());
502 }
503 return;
504 }
505
506 JSObject::MigrateToMap(holder_obj, new_map);
507 ReloadPropertyInformation<false>();
508}
509
510
511void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
512 PropertyAttributes attributes) {
513 DCHECK(state_ == DATA || state_ == ACCESSOR);
514 DCHECK(HolderIsReceiverOrHiddenPrototype());
515
516 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
517
518 // Property details can never change for private properties.
519 if (holder->IsJSProxy()) {
520 DCHECK(name()->IsPrivate());
521 return;
522 }
523
524 Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
525 if (IsElement()) {
526 DCHECK(!holder_obj->HasFixedTypedArrayElements());
527 DCHECK(attributes != NONE || !holder_obj->HasFastElements());
528 Handle<FixedArrayBase> elements(holder_obj->elements(), isolate());
529 holder_obj->GetElementsAccessor()->Reconfigure(holder_obj, elements,
530 number_, value, attributes);
531 ReloadPropertyInformation<true>();
532 } else if (holder_obj->HasFastProperties()) {
533 Handle<Map> old_map(holder_obj->map(), isolate_);
534 Handle<Map> new_map = Map::ReconfigureExistingProperty(
535 isolate_, old_map, descriptor_number(), i::kData, attributes);
536 // Force mutable to avoid changing constant value by reconfiguring
537 // kData -> kAccessor -> kData.
538 new_map =
539 Map::PrepareForDataProperty(isolate(), new_map, descriptor_number(),
540 PropertyConstness::kMutable, value);
541 JSObject::MigrateToMap(holder_obj, new_map);
542 ReloadPropertyInformation<false>();
543 }
544
545 if (!IsElement() && !holder_obj->HasFastProperties()) {
546 PropertyDetails details(kData, attributes, PropertyCellType::kMutable);
547 if (holder_obj->map()->is_prototype_map() &&
548 (property_details_.attributes() & READ_ONLY) == 0 &&
549 (attributes & READ_ONLY) != 0) {
550 // Invalidate prototype validity cell when a property is reconfigured
551 // from writable to read-only as this may invalidate transitioning store
552 // IC handlers.
553 JSObject::InvalidatePrototypeChains(holder->map());
554 }
555 if (holder_obj->IsJSGlobalObject()) {
556 Handle<GlobalDictionary> dictionary(
557 JSGlobalObject::cast(*holder_obj)->global_dictionary(), isolate());
558
559 Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
560 isolate(), dictionary, dictionary_entry(), value, details);
561 cell->set_value(*value);
562 property_details_ = cell->property_details();
563 } else {
564 Handle<NameDictionary> dictionary(holder_obj->property_dictionary(),
565 isolate());
566 PropertyDetails original_details =
567 dictionary->DetailsAt(dictionary_entry());
568 int enumeration_index = original_details.dictionary_index();
569 DCHECK_GT(enumeration_index, 0);
570 details = details.set_index(enumeration_index);
571 dictionary->SetEntry(isolate(), dictionary_entry(), *name(), *value,
572 details);
573 property_details_ = details;
574 }
575 state_ = DATA;
576 }
577
578 WriteDataValue(value, true);
579
580#if VERIFY_HEAP
581 if (FLAG_verify_heap) {
582 holder->HeapObjectVerify(isolate());
583 }
584#endif
585}
586
587// Can only be called when the receiver is a JSObject. JSProxy has to be handled
588// via a trap. Adding properties to primitive values is not observable.
589void LookupIterator::PrepareTransitionToDataProperty(
590 Handle<JSReceiver> receiver, Handle<Object> value,
591 PropertyAttributes attributes, StoreOrigin store_origin) {
592 DCHECK_IMPLIES(receiver->IsJSProxy(), name()->IsPrivate());
593 DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
594 if (state_ == TRANSITION) return;
595
596 if (!IsElement() && name()->IsPrivate()) {
597 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
598 }
599
600 DCHECK(state_ != LookupIterator::ACCESSOR ||
601 (GetAccessors()->IsAccessorInfo() &&
602 AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
603 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
604 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
605
606 Handle<Map> map(receiver->map(), isolate_);
607
608 // Dictionary maps can always have additional data properties.
609 if (map->is_dictionary_map()) {
610 state_ = TRANSITION;
611 if (map->IsJSGlobalObjectMap()) {
612 // Install a property cell.
613 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
614 int entry;
615 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
616 global, name(), PropertyCellType::kUninitialized, &entry);
617 Handle<GlobalDictionary> dictionary(global->global_dictionary(),
618 isolate_);
619 DCHECK(cell->value()->IsTheHole(isolate_));
620 DCHECK(!value->IsTheHole(isolate_));
621 transition_ = cell;
622 // Assign an enumeration index to the property and update
623 // SetNextEnumerationIndex.
624 int index = dictionary->NextEnumerationIndex();
625 dictionary->SetNextEnumerationIndex(index + 1);
626 property_details_ = PropertyDetails(
627 kData, attributes, PropertyCellType::kUninitialized, index);
628 PropertyCellType new_type =
629 PropertyCell::UpdatedType(isolate(), cell, value, property_details_);
630 property_details_ = property_details_.set_cell_type(new_type);
631 cell->set_property_details(property_details_);
632 number_ = entry;
633 has_property_ = true;
634 } else {
635 // Don't set enumeration index (it will be set during value store).
636 property_details_ =
637 PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
638 transition_ = map;
639 }
640 return;
641 }
642
643 Handle<Map> transition =
644 Map::TransitionToDataProperty(isolate_, map, name_, value, attributes,
645 kDefaultFieldConstness, store_origin);
646 state_ = TRANSITION;
647 transition_ = transition;
648
649 if (transition->is_dictionary_map()) {
650 // Don't set enumeration index (it will be set during value store).
651 property_details_ =
652 PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
653 } else {
654 property_details_ = transition->GetLastDescriptorDetails();
655 has_property_ = true;
656 }
657}
658
659void LookupIterator::ApplyTransitionToDataProperty(
660 Handle<JSReceiver> receiver) {
661 DCHECK_EQ(TRANSITION, state_);
662
663 DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
664 holder_ = receiver;
665 if (receiver->IsJSGlobalObject()) {
666 JSObject::InvalidatePrototypeChains(receiver->map());
667 state_ = DATA;
668 return;
669 }
670 Handle<Map> transition = transition_map();
671 bool simple_transition = transition->GetBackPointer() == receiver->map();
672
673 if (configuration_ == DEFAULT && !transition->is_dictionary_map() &&
674 !transition->IsPrototypeValidityCellValid()) {
675 // Only LookupIterator instances with DEFAULT (full prototype chain)
676 // configuration can produce valid transition handler maps.
677 Handle<Object> validity_cell =
678 Map::GetOrCreatePrototypeChainValidityCell(transition, isolate());
679 transition->set_prototype_validity_cell(*validity_cell);
680 }
681
682 if (!receiver->IsJSProxy()) {
683 JSObject::MigrateToMap(Handle<JSObject>::cast(receiver), transition);
684 }
685
686 if (simple_transition) {
687 int number = transition->LastAdded();
688 number_ = static_cast<uint32_t>(number);
689 property_details_ = transition->GetLastDescriptorDetails();
690 state_ = DATA;
691 } else if (receiver->map()->is_dictionary_map()) {
692 Handle<NameDictionary> dictionary(receiver->property_dictionary(),
693 isolate_);
694 int entry;
695 if (receiver->map()->is_prototype_map() && receiver->IsJSObject()) {
696 JSObject::InvalidatePrototypeChains(receiver->map());
697 }
698 dictionary = NameDictionary::Add(isolate(), dictionary, name(),
699 isolate_->factory()->uninitialized_value(),
700 property_details_, &entry);
701 receiver->SetProperties(*dictionary);
702 // Reload details containing proper enumeration index value.
703 property_details_ = dictionary->DetailsAt(entry);
704 number_ = entry;
705 has_property_ = true;
706 state_ = DATA;
707
708 } else {
709 ReloadPropertyInformation<false>();
710 }
711}
712
713
714void LookupIterator::Delete() {
715 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
716 if (IsElement()) {
717 Handle<JSObject> object = Handle<JSObject>::cast(holder);
718 ElementsAccessor* accessor = object->GetElementsAccessor();
719 accessor->Delete(object, number_);
720 } else {
721 DCHECK(!name()->IsPrivateName());
722 bool is_prototype_map = holder->map()->is_prototype_map();
723 RuntimeCallTimerScope stats_scope(
724 isolate_, is_prototype_map
725 ? RuntimeCallCounterId::kPrototypeObject_DeleteProperty
726 : RuntimeCallCounterId::kObject_DeleteProperty);
727
728 PropertyNormalizationMode mode =
729 is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
730
731 if (holder->HasFastProperties()) {
732 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
733 "DeletingProperty");
734 ReloadPropertyInformation<false>();
735 }
736 JSReceiver::DeleteNormalizedProperty(holder, number_);
737 if (holder->IsJSObject()) {
738 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
739 }
740 }
741 state_ = NOT_FOUND;
742}
743
744void LookupIterator::TransitionToAccessorProperty(
745 Handle<Object> getter, Handle<Object> setter,
746 PropertyAttributes attributes) {
747 DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
748 // Can only be called when the receiver is a JSObject. JSProxy has to be
749 // handled via a trap. Adding properties to primitive values is not
750 // observable.
751 Handle<JSObject> receiver = GetStoreTarget<JSObject>();
752 if (!IsElement() && name()->IsPrivate()) {
753 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
754 }
755
756 if (!IsElement() && !receiver->map()->is_dictionary_map()) {
757 Handle<Map> old_map(receiver->map(), isolate_);
758
759 if (!holder_.is_identical_to(receiver)) {
760 holder_ = receiver;
761 state_ = NOT_FOUND;
762 } else if (state_ == INTERCEPTOR) {
763 LookupInRegularHolder<false>(*old_map, *holder_);
764 }
765 int descriptor =
766 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
767
768 Handle<Map> new_map = Map::TransitionToAccessorProperty(
769 isolate_, old_map, name_, descriptor, getter, setter, attributes);
770 bool simple_transition = new_map->GetBackPointer() == receiver->map();
771 JSObject::MigrateToMap(receiver, new_map);
772
773 if (simple_transition) {
774 int number = new_map->LastAdded();
775 number_ = static_cast<uint32_t>(number);
776 property_details_ = new_map->GetLastDescriptorDetails();
777 state_ = ACCESSOR;
778 return;
779 }
780
781 ReloadPropertyInformation<false>();
782 if (!new_map->is_dictionary_map()) return;
783 }
784
785 Handle<AccessorPair> pair;
786 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
787 pair = Handle<AccessorPair>::cast(GetAccessors());
788 // If the component and attributes are identical, nothing has to be done.
789 if (pair->Equals(*getter, *setter)) {
790 if (property_details().attributes() == attributes) {
791 if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
792 return;
793 }
794 } else {
795 pair = AccessorPair::Copy(isolate(), pair);
796 pair->SetComponents(*getter, *setter);
797 }
798 } else {
799 pair = factory()->NewAccessorPair();
800 pair->SetComponents(*getter, *setter);
801 }
802
803 TransitionToAccessorPair(pair, attributes);
804
805#if VERIFY_HEAP
806 if (FLAG_verify_heap) {
807 receiver->JSObjectVerify(isolate());
808 }
809#endif
810}
811
812
813void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
814 PropertyAttributes attributes) {
815 Handle<JSObject> receiver = GetStoreTarget<JSObject>();
816 holder_ = receiver;
817
818 PropertyDetails details(kAccessor, attributes, PropertyCellType::kMutable);
819
820 if (IsElement()) {
821 // TODO(verwaest): Move code into the element accessor.
822 isolate_->CountUsage(v8::Isolate::kIndexAccessor);
823 Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(receiver);
824
825 dictionary = NumberDictionary::Set(isolate_, dictionary, index_, pair,
826 receiver, details);
827 receiver->RequireSlowElements(*dictionary);
828
829 if (receiver->HasSlowArgumentsElements()) {
830 FixedArray parameter_map = FixedArray::cast(receiver->elements());
831 uint32_t length = parameter_map->length() - 2;
832 if (number_ < length) {
833 parameter_map->set(number_ + 2, ReadOnlyRoots(heap()).the_hole_value());
834 }
835 FixedArray::cast(receiver->elements())->set(1, *dictionary);
836 } else {
837 receiver->set_elements(*dictionary);
838 }
839
840 ReloadPropertyInformation<true>();
841 } else {
842 PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES;
843 if (receiver->map()->is_prototype_map()) {
844 JSObject::InvalidatePrototypeChains(receiver->map());
845 mode = KEEP_INOBJECT_PROPERTIES;
846 }
847
848 // Normalize object to make this operation simple.
849 JSObject::NormalizeProperties(receiver, mode, 0,
850 "TransitionToAccessorPair");
851
852 JSObject::SetNormalizedProperty(receiver, name_, pair, details);
853 JSObject::ReoptimizeIfPrototype(receiver);
854
855 ReloadPropertyInformation<false>();
856 }
857}
858
859bool LookupIterator::HolderIsReceiver() const {
860 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
861 // Optimization that only works if configuration_ is not mutable.
862 if (!check_prototype_chain()) return true;
863 return *receiver_ == *holder_;
864}
865
866bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
867 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
868 // Optimization that only works if configuration_ is not mutable.
869 if (!check_prototype_chain()) return true;
870 DisallowHeapAllocation no_gc;
871 if (*receiver_ == *holder_) return true;
872 if (!receiver_->IsJSReceiver()) return false;
873 JSReceiver current = JSReceiver::cast(*receiver_);
874 JSReceiver object = *holder_;
875 if (!current->map()->has_hidden_prototype()) return false;
876 // JSProxy do not occur as hidden prototypes.
877 if (object->IsJSProxy()) return false;
878 PrototypeIterator iter(isolate(), current, kStartAtPrototype,
879 PrototypeIterator::END_AT_NON_HIDDEN);
880 while (!iter.IsAtEnd()) {
881 if (iter.GetCurrent<JSReceiver>() == object) return true;
882 iter.Advance();
883 }
884 return false;
885}
886
887
888Handle<Object> LookupIterator::FetchValue() const {
889 Object result;
890 if (IsElement()) {
891 Handle<JSObject> holder = GetHolder<JSObject>();
892 ElementsAccessor* accessor = holder->GetElementsAccessor();
893 return accessor->Get(holder, number_);
894 } else if (holder_->IsJSGlobalObject()) {
895 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
896 result = holder->global_dictionary()->ValueAt(number_);
897 } else if (!holder_->HasFastProperties()) {
898 result = holder_->property_dictionary()->ValueAt(number_);
899 } else if (property_details_.location() == kField) {
900 DCHECK_EQ(kData, property_details_.kind());
901 Handle<JSObject> holder = GetHolder<JSObject>();
902 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
903 return JSObject::FastPropertyAt(holder, property_details_.representation(),
904 field_index);
905 } else {
906 result = holder_->map()->instance_descriptors()->GetStrongValue(number_);
907 }
908 return handle(result, isolate_);
909}
910
911bool LookupIterator::IsConstFieldValueEqualTo(Object value) const {
912 DCHECK(!IsElement());
913 DCHECK(holder_->HasFastProperties());
914 DCHECK_EQ(kField, property_details_.location());
915 DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
916 Handle<JSObject> holder = GetHolder<JSObject>();
917 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
918 if (property_details_.representation().IsDouble()) {
919 if (!value->IsNumber()) return false;
920 uint64_t bits;
921 if (holder->IsUnboxedDoubleField(field_index)) {
922 bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
923 } else {
924 Object current_value = holder->RawFastPropertyAt(field_index);
925 DCHECK(current_value->IsMutableHeapNumber());
926 bits = MutableHeapNumber::cast(current_value)->value_as_bits();
927 }
928 // Use bit representation of double to to check for hole double, since
929 // manipulating the signaling NaN used for the hole in C++, e.g. with
930 // bit_cast or value(), will change its value on ia32 (the x87 stack is
931 // used to return values and stores to the stack silently clear the
932 // signalling bit).
933 if (bits == kHoleNanInt64) {
934 // Uninitialized double field.
935 return true;
936 }
937 return Object::SameNumberValue(bit_cast<double>(bits), value->Number());
938 } else {
939 Object current_value = holder->RawFastPropertyAt(field_index);
940 if (current_value->IsUninitialized(isolate()) || current_value == value) {
941 return true;
942 }
943 return current_value->IsNumber() && value->IsNumber() &&
944 Object::SameNumberValue(current_value->Number(), value->Number());
945 }
946}
947
948int LookupIterator::GetFieldDescriptorIndex() const {
949 DCHECK(has_property_);
950 DCHECK(holder_->HasFastProperties());
951 DCHECK_EQ(kField, property_details_.location());
952 DCHECK_EQ(kData, property_details_.kind());
953 return descriptor_number();
954}
955
956int LookupIterator::GetAccessorIndex() const {
957 DCHECK(has_property_);
958 DCHECK(holder_->HasFastProperties());
959 DCHECK_EQ(kDescriptor, property_details_.location());
960 DCHECK_EQ(kAccessor, property_details_.kind());
961 return descriptor_number();
962}
963
964
965int LookupIterator::GetConstantIndex() const {
966 DCHECK(has_property_);
967 DCHECK(holder_->HasFastProperties());
968 DCHECK_EQ(kDescriptor, property_details_.location());
969 DCHECK_EQ(kData, property_details_.kind());
970 DCHECK(!FLAG_track_constant_fields);
971 DCHECK(!IsElement());
972 return descriptor_number();
973}
974
975Handle<Map> LookupIterator::GetFieldOwnerMap() const {
976 DCHECK(has_property_);
977 DCHECK(holder_->HasFastProperties());
978 DCHECK_EQ(kField, property_details_.location());
979 DCHECK(!IsElement());
980 Map holder_map = holder_->map();
981 return handle(holder_map->FindFieldOwner(isolate(), descriptor_number()),
982 isolate_);
983}
984
985FieldIndex LookupIterator::GetFieldIndex() const {
986 DCHECK(has_property_);
987 DCHECK(holder_->HasFastProperties());
988 DCHECK_EQ(kField, property_details_.location());
989 DCHECK(!IsElement());
990 return FieldIndex::ForDescriptor(holder_->map(), descriptor_number());
991}
992
993Handle<FieldType> LookupIterator::GetFieldType() const {
994 DCHECK(has_property_);
995 DCHECK(holder_->HasFastProperties());
996 DCHECK_EQ(kField, property_details_.location());
997 return handle(
998 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
999 isolate_);
1000}
1001
1002
1003Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
1004 DCHECK(!IsElement());
1005 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
1006 return handle(holder->global_dictionary()->CellAt(dictionary_entry()),
1007 isolate_);
1008}
1009
1010
1011Handle<Object> LookupIterator::GetAccessors() const {
1012 DCHECK_EQ(ACCESSOR, state_);
1013 return FetchValue();
1014}
1015
1016
1017Handle<Object> LookupIterator::GetDataValue() const {
1018 DCHECK_EQ(DATA, state_);
1019 Handle<Object> value = FetchValue();
1020 return value;
1021}
1022
1023void LookupIterator::WriteDataValue(Handle<Object> value,
1024 bool initializing_store) {
1025 DCHECK_EQ(DATA, state_);
1026 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
1027 if (IsElement()) {
1028 Handle<JSObject> object = Handle<JSObject>::cast(holder);
1029 ElementsAccessor* accessor = object->GetElementsAccessor();
1030 accessor->Set(object, number_, *value);
1031 } else if (holder->HasFastProperties()) {
1032 if (property_details_.location() == kField) {
1033 // Check that in case of VariableMode::kConst field the existing value is
1034 // equal to |value|.
1035 DCHECK_IMPLIES(!initializing_store && property_details_.constness() ==
1036 PropertyConstness::kConst,
1037 IsConstFieldValueEqualTo(*value));
1038 JSObject::cast(*holder)->WriteToField(descriptor_number(),
1039 property_details_, *value);
1040 } else {
1041 DCHECK_EQ(kDescriptor, property_details_.location());
1042 DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
1043 }
1044 } else if (holder->IsJSGlobalObject()) {
1045 GlobalDictionary dictionary =
1046 JSGlobalObject::cast(*holder)->global_dictionary();
1047 dictionary->CellAt(dictionary_entry())->set_value(*value);
1048 } else {
1049 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
1050 NameDictionary dictionary = holder->property_dictionary();
1051 dictionary->ValueAtPut(dictionary_entry(), *value);
1052 }
1053}
1054
1055template <bool is_element>
1056bool LookupIterator::SkipInterceptor(JSObject holder) {
1057 auto info = GetInterceptor<is_element>(holder);
1058 if (!is_element && name_->IsSymbol() && !info->can_intercept_symbols()) {
1059 return true;
1060 }
1061 if (info->non_masking()) {
1062 switch (interceptor_state_) {
1063 case InterceptorState::kUninitialized:
1064 interceptor_state_ = InterceptorState::kSkipNonMasking;
1065 V8_FALLTHROUGH;
1066 case InterceptorState::kSkipNonMasking:
1067 return true;
1068 case InterceptorState::kProcessNonMasking:
1069 return false;
1070 }
1071 }
1072 return interceptor_state_ == InterceptorState::kProcessNonMasking;
1073}
1074
1075JSReceiver LookupIterator::NextHolder(Map map) {
1076 DisallowHeapAllocation no_gc;
1077 if (map->prototype() == ReadOnlyRoots(heap()).null_value()) {
1078 return JSReceiver();
1079 }
1080 if (!check_prototype_chain() && !map->has_hidden_prototype()) {
1081 return JSReceiver();
1082 }
1083 return JSReceiver::cast(map->prototype());
1084}
1085
1086LookupIterator::State LookupIterator::NotFound(JSReceiver const holder) const {
1087 DCHECK(!IsElement());
1088 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
1089 return IsSpecialIndex(String::cast(*name_)) ? INTEGER_INDEXED_EXOTIC
1090 : NOT_FOUND;
1091}
1092
1093namespace {
1094
1095template <bool is_element>
1096bool HasInterceptor(Map map) {
1097 return is_element ? map->has_indexed_interceptor()
1098 : map->has_named_interceptor();
1099}
1100
1101} // namespace
1102
1103template <bool is_element>
1104LookupIterator::State LookupIterator::LookupInSpecialHolder(
1105 Map const map, JSReceiver const holder) {
1106 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
1107 switch (state_) {
1108 case NOT_FOUND:
1109 if (map->IsJSProxyMap()) {
1110 if (is_element || !name_->IsPrivate()) return JSPROXY;
1111 }
1112 if (map->is_access_check_needed()) {
1113 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
1114 }
1115 V8_FALLTHROUGH;
1116 case ACCESS_CHECK:
1117 if (check_interceptor() && HasInterceptor<is_element>(map) &&
1118 !SkipInterceptor<is_element>(JSObject::cast(holder))) {
1119 if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
1120 }
1121 V8_FALLTHROUGH;
1122 case INTERCEPTOR:
1123 if (!is_element && map->IsJSGlobalObjectMap()) {
1124 GlobalDictionary dict =
1125 JSGlobalObject::cast(holder)->global_dictionary();
1126 int number = dict->FindEntry(isolate(), name_);
1127 if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
1128 number_ = static_cast<uint32_t>(number);
1129 PropertyCell cell = dict->CellAt(number_);
1130 if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
1131 property_details_ = cell->property_details();
1132 has_property_ = true;
1133 switch (property_details_.kind()) {
1134 case v8::internal::kData:
1135 return DATA;
1136 case v8::internal::kAccessor:
1137 return ACCESSOR;
1138 }
1139 }
1140 return LookupInRegularHolder<is_element>(map, holder);
1141 case ACCESSOR:
1142 case DATA:
1143 return NOT_FOUND;
1144 case INTEGER_INDEXED_EXOTIC:
1145 case JSPROXY:
1146 case TRANSITION:
1147 UNREACHABLE();
1148 }
1149 UNREACHABLE();
1150}
1151
1152template <bool is_element>
1153LookupIterator::State LookupIterator::LookupInRegularHolder(
1154 Map const map, JSReceiver const holder) {
1155 DisallowHeapAllocation no_gc;
1156 if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
1157 return NOT_FOUND;
1158 }
1159
1160 if (is_element) {
1161 JSObject js_object = JSObject::cast(holder);
1162 ElementsAccessor* accessor = js_object->GetElementsAccessor();
1163 FixedArrayBase backing_store = js_object->elements();
1164 number_ =
1165 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
1166 if (number_ == kMaxUInt32) {
1167 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
1168 }
1169 property_details_ = accessor->GetDetails(js_object, number_);
1170 if (map->is_frozen_or_sealed_elements()) {
1171 PropertyAttributes attrs =
1172 map->elements_kind() == PACKED_SEALED_ELEMENTS ? SEALED : FROZEN;
1173 property_details_ = property_details_.CopyAddAttributes(attrs);
1174 }
1175 } else if (!map->is_dictionary_map()) {
1176 DescriptorArray descriptors = map->instance_descriptors();
1177 int number = descriptors->SearchWithCache(isolate_, *name_, map);
1178 if (number == DescriptorArray::kNotFound) return NotFound(holder);
1179 number_ = static_cast<uint32_t>(number);
1180 property_details_ = descriptors->GetDetails(number_);
1181 } else {
1182 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
1183 NameDictionary dict = holder->property_dictionary();
1184 int number = dict->FindEntry(isolate(), name_);
1185 if (number == NameDictionary::kNotFound) return NotFound(holder);
1186 number_ = static_cast<uint32_t>(number);
1187 property_details_ = dict->DetailsAt(number_);
1188 }
1189 has_property_ = true;
1190 switch (property_details_.kind()) {
1191 case v8::internal::kData:
1192 return DATA;
1193 case v8::internal::kAccessor:
1194 return ACCESSOR;
1195 }
1196
1197 UNREACHABLE();
1198}
1199
1200Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
1201 const {
1202 DCHECK_EQ(ACCESS_CHECK, state_);
1203 DisallowHeapAllocation no_gc;
1204 AccessCheckInfo access_check_info =
1205 AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
1206 if (!access_check_info.is_null()) {
1207 Object interceptor = IsElement() ? access_check_info->indexed_interceptor()
1208 : access_check_info->named_interceptor();
1209 if (interceptor != Object()) {
1210 return handle(InterceptorInfo::cast(interceptor), isolate_);
1211 }
1212 }
1213 return Handle<InterceptorInfo>();
1214}
1215
1216bool LookupIterator::TryLookupCachedProperty() {
1217 return state() == LookupIterator::ACCESSOR &&
1218 GetAccessors()->IsAccessorPair() && LookupCachedProperty();
1219}
1220
1221bool LookupIterator::LookupCachedProperty() {
1222 DCHECK_EQ(state(), LookupIterator::ACCESSOR);
1223 DCHECK(GetAccessors()->IsAccessorPair());
1224
1225 AccessorPair accessor_pair = AccessorPair::cast(*GetAccessors());
1226 Handle<Object> getter(accessor_pair->getter(), isolate());
1227 MaybeHandle<Name> maybe_name =
1228 FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
1229 if (maybe_name.is_null()) return false;
1230
1231 // We have found a cached property! Modify the iterator accordingly.
1232 name_ = maybe_name.ToHandleChecked();
1233 Restart();
1234 CHECK_EQ(state(), LookupIterator::DATA);
1235 return true;
1236}
1237
1238} // namespace internal
1239} // namespace v8
1240