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 | |
17 | namespace v8 { |
18 | namespace internal { |
19 | |
20 | // static |
21 | LookupIterator 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 |
51 | LookupIterator 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 |
85 | LookupIterator 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 | |
127 | LookupIterator::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 | |
145 | template <bool is_element> |
146 | void 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 | |
162 | template void LookupIterator::Start<true>(); |
163 | template void LookupIterator::Start<false>(); |
164 | |
165 | void 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 | |
184 | template <bool is_element> |
185 | void 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 | |
205 | template <bool is_element> |
206 | void 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 | |
213 | template void LookupIterator::RestartInternal<true>(InterceptorState); |
214 | template void LookupIterator::RestartInternal<false>(InterceptorState); |
215 | |
216 | // static |
217 | Handle<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 | |
239 | Handle<Map> LookupIterator::GetReceiverMap() const { |
240 | if (receiver_->IsNumber()) return factory()->heap_number_map(); |
241 | return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); |
242 | } |
243 | |
244 | bool LookupIterator::HasAccess() const { |
245 | DCHECK_EQ(ACCESS_CHECK, state_); |
246 | return isolate_->MayAccess(handle(isolate_->context(), isolate_), |
247 | GetHolder<JSObject>()); |
248 | } |
249 | |
250 | template <bool is_element> |
251 | void 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 | |
258 | namespace { |
259 | |
260 | bool 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 | |
278 | void 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 | |
439 | void 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 | |
511 | void 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. |
589 | void 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 | |
659 | void 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 | |
714 | void 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 | |
744 | void 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 | |
813 | void 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 | |
859 | bool 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 | |
866 | bool 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 | |
888 | Handle<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 | |
911 | bool 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 | |
948 | int 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 | |
956 | int 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 | |
965 | int 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 | |
975 | Handle<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 | |
985 | FieldIndex 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 | |
993 | Handle<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 | |
1003 | Handle<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 | |
1011 | Handle<Object> LookupIterator::GetAccessors() const { |
1012 | DCHECK_EQ(ACCESSOR, state_); |
1013 | return FetchValue(); |
1014 | } |
1015 | |
1016 | |
1017 | Handle<Object> LookupIterator::GetDataValue() const { |
1018 | DCHECK_EQ(DATA, state_); |
1019 | Handle<Object> value = FetchValue(); |
1020 | return value; |
1021 | } |
1022 | |
1023 | void 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 | |
1055 | template <bool is_element> |
1056 | bool 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 | |
1075 | JSReceiver 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 | |
1086 | LookupIterator::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 | |
1093 | namespace { |
1094 | |
1095 | template <bool is_element> |
1096 | bool HasInterceptor(Map map) { |
1097 | return is_element ? map->has_indexed_interceptor() |
1098 | : map->has_named_interceptor(); |
1099 | } |
1100 | |
1101 | } // namespace |
1102 | |
1103 | template <bool is_element> |
1104 | LookupIterator::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 | |
1152 | template <bool is_element> |
1153 | LookupIterator::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 | |
1200 | Handle<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 | |
1216 | bool LookupIterator::TryLookupCachedProperty() { |
1217 | return state() == LookupIterator::ACCESSOR && |
1218 | GetAccessors()->IsAccessorPair() && LookupCachedProperty(); |
1219 | } |
1220 | |
1221 | bool 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 | |