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