Line data Source code
1 : // Copyright 2014 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/lookup.h"
6 :
7 : #include "src/bootstrapper.h"
8 : #include "src/deoptimizer.h"
9 : #include "src/elements.h"
10 : #include "src/field-type.h"
11 : #include "src/isolate-inl.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 : // static
17 3816 : LookupIterator LookupIterator::PropertyOrElement(
18 : Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
19 : bool* success, Handle<JSReceiver> holder, Configuration configuration) {
20 3816 : uint32_t index = 0;
21 3816 : if (key->ToArrayIndex(&index)) {
22 2100 : *success = true;
23 2100 : return LookupIterator(isolate, receiver, index, holder, configuration);
24 : }
25 :
26 : Handle<Name> name;
27 3432 : *success = Object::ToName(isolate, key).ToHandle(&name);
28 1716 : if (!*success) {
29 : DCHECK(isolate->has_pending_exception());
30 : // Return an unusable dummy.
31 10 : return LookupIterator(receiver, isolate->factory()->empty_string());
32 : }
33 :
34 1706 : if (name->AsArrayIndex(&index)) {
35 0 : LookupIterator it(isolate, receiver, index, holder, configuration);
36 : // Here we try to avoid having to rebuild the string later
37 : // by storing it on the indexed LookupIterator.
38 0 : it.name_ = name;
39 0 : return it;
40 : }
41 :
42 1706 : return LookupIterator(receiver, name, holder, configuration);
43 : }
44 :
45 : // static
46 70376587 : LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
47 : Handle<Object> receiver,
48 : Handle<Object> key,
49 : bool* success,
50 : Configuration configuration) {
51 : // TODO(mslekova): come up with better way to avoid duplication
52 70376587 : uint32_t index = 0;
53 70376588 : if (key->ToArrayIndex(&index)) {
54 26396852 : *success = true;
55 26396852 : return LookupIterator(isolate, receiver, index, configuration);
56 : }
57 :
58 : Handle<Name> name;
59 87959471 : *success = Object::ToName(isolate, key).ToHandle(&name);
60 43979735 : if (!*success) {
61 : DCHECK(isolate->has_pending_exception());
62 : // Return an unusable dummy.
63 33 : return LookupIterator(receiver, isolate->factory()->empty_string());
64 : }
65 :
66 43979702 : if (name->AsArrayIndex(&index)) {
67 317996 : LookupIterator it(isolate, receiver, index, configuration);
68 : // Here we try to avoid having to rebuild the string later
69 : // by storing it on the indexed LookupIterator.
70 317996 : it.name_ = name;
71 317996 : return it;
72 : }
73 :
74 43661706 : return LookupIterator(receiver, name, configuration);
75 : }
76 :
77 : // static
78 4304546 : LookupIterator LookupIterator::ForTransitionHandler(
79 : Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
80 : Handle<Object> value, MaybeHandle<Object> handler,
81 : Handle<Map> transition_map) {
82 4304546 : if (handler.is_null()) return LookupIterator(receiver, name);
83 :
84 3084 : PropertyDetails details = PropertyDetails::Empty();
85 : bool has_property;
86 3084 : if (transition_map->is_dictionary_map()) {
87 : details = PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
88 : has_property = false;
89 : } else {
90 3084 : details = transition_map->GetLastDescriptorDetails();
91 : has_property = true;
92 : }
93 : LookupIterator it(isolate, receiver, name, transition_map, details,
94 3084 : has_property);
95 :
96 3084 : if (!transition_map->is_dictionary_map()) {
97 : PropertyConstness new_constness = kConst;
98 : if (FLAG_track_constant_fields) {
99 : if (it.constness() == kConst) {
100 : DCHECK_EQ(kData, it.property_details_.kind());
101 : // Check that current value matches new value otherwise we should make
102 : // the property mutable.
103 : if (!it.IsConstFieldValueEqualTo(*value)) new_constness = kMutable;
104 : }
105 : } else {
106 : new_constness = kMutable;
107 : }
108 :
109 : int descriptor_number = transition_map->LastAdded();
110 : Handle<Map> new_map = Map::PrepareForDataProperty(
111 3084 : transition_map, descriptor_number, new_constness, value);
112 : // Reload information; this is no-op if nothing changed.
113 : it.property_details_ =
114 3084 : new_map->instance_descriptors()->GetDetails(descriptor_number);
115 3084 : it.transition_ = new_map;
116 : }
117 3084 : return it;
118 : }
119 :
120 3084 : LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
121 : Handle<Name> name, Handle<Map> transition_map,
122 : PropertyDetails details, bool has_property)
123 : : configuration_(DEFAULT),
124 : state_(TRANSITION),
125 : has_property_(has_property),
126 : interceptor_state_(InterceptorState::kUninitialized),
127 : property_details_(details),
128 : isolate_(isolate),
129 : name_(name),
130 : transition_(transition_map),
131 : receiver_(receiver),
132 : initial_holder_(GetRoot(isolate, receiver)),
133 : index_(kMaxUInt32),
134 9252 : number_(static_cast<uint32_t>(DescriptorArray::kNotFound)) {
135 3084 : holder_ = initial_holder_;
136 3084 : }
137 :
138 : template <bool is_element>
139 311922834 : void LookupIterator::Start() {
140 : DisallowHeapAllocation no_gc;
141 :
142 311922834 : has_property_ = false;
143 311922834 : state_ = NOT_FOUND;
144 311922834 : holder_ = initial_holder_;
145 :
146 : JSReceiver* holder = *holder_;
147 : Map* map = holder->map();
148 :
149 311922834 : state_ = LookupInHolder<is_element>(map, holder);
150 623845601 : if (IsFound()) return;
151 :
152 179131044 : NextInternal<is_element>(map, holder);
153 : }
154 :
155 : template void LookupIterator::Start<true>();
156 : template void LookupIterator::Start<false>();
157 :
158 20181269 : void LookupIterator::Next() {
159 : DCHECK_NE(JSPROXY, state_);
160 : DCHECK_NE(TRANSITION, state_);
161 : DisallowHeapAllocation no_gc;
162 6988776 : has_property_ = false;
163 :
164 : JSReceiver* holder = *holder_;
165 : Map* map = holder->map();
166 :
167 6988776 : if (map->IsSpecialReceiverMap()) {
168 : state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
169 6767140 : : LookupInSpecialHolder<false>(map, holder);
170 13755916 : if (IsFound()) return;
171 : }
172 :
173 115434 : IsElement() ? NextInternal<true>(map, holder)
174 12735272 : : NextInternal<false>(map, holder);
175 : }
176 :
177 : template <bool is_element>
178 185556407 : void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
179 204752924 : do {
180 : JSReceiver* maybe_holder = NextHolder(map);
181 370409570 : if (maybe_holder == nullptr) {
182 165656648 : if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
183 0 : RestartLookupForNonMaskingInterceptors<is_element>();
184 0 : return;
185 : }
186 165656498 : state_ = NOT_FOUND;
187 262296775 : if (holder != *holder_) holder_ = handle(holder, isolate_);
188 : return;
189 : }
190 : holder = maybe_holder;
191 : map = holder->map();
192 204752922 : state_ = LookupInHolder<is_element>(map, holder);
193 : } while (!IsFound());
194 :
195 39799522 : holder_ = handle(holder, isolate_);
196 : }
197 :
198 : template <bool is_element>
199 4703202 : void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
200 4703202 : interceptor_state_ = interceptor_state;
201 4703202 : property_details_ = PropertyDetails::Empty();
202 4703202 : number_ = static_cast<uint32_t>(DescriptorArray::kNotFound);
203 4703202 : Start<is_element>();
204 4703202 : }
205 :
206 : template void LookupIterator::RestartInternal<true>(InterceptorState);
207 : template void LookupIterator::RestartInternal<false>(InterceptorState);
208 :
209 : // static
210 165137 : Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
211 : Isolate* isolate, Handle<Object> receiver, uint32_t index) {
212 : // Strings are the only objects with properties (only elements) directly on
213 : // the wrapper. Hence we can skip generating the wrapper for all other cases.
214 185182 : if (index != kMaxUInt32 && receiver->IsString() &&
215 4503 : index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
216 : // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
217 : // context, ensuring that we don't leak it into JS?
218 1212 : Handle<JSFunction> constructor = isolate->string_function();
219 1212 : Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
220 1212 : Handle<JSValue>::cast(result)->set_value(*receiver);
221 1212 : return result;
222 : }
223 : auto root =
224 163925 : handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
225 163925 : if (root->IsNull(isolate)) {
226 : unsigned int magic = 0xbbbbbbbb;
227 0 : isolate->PushStackTraceAndDie(magic, *receiver, nullptr, magic);
228 : }
229 : return Handle<JSReceiver>::cast(root);
230 : }
231 :
232 :
233 0 : Handle<Map> LookupIterator::GetReceiverMap() const {
234 0 : if (receiver_->IsNumber()) return factory()->heap_number_map();
235 0 : return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
236 : }
237 :
238 5814652 : bool LookupIterator::HasAccess() const {
239 : DCHECK_EQ(ACCESS_CHECK, state_);
240 : return isolate_->MayAccess(handle(isolate_->context()),
241 11629304 : GetHolder<JSObject>());
242 : }
243 :
244 : template <bool is_element>
245 10528352 : void LookupIterator::ReloadPropertyInformation() {
246 10528352 : state_ = BEFORE_PROPERTY;
247 10528352 : interceptor_state_ = InterceptorState::kUninitialized;
248 10528352 : state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
249 : DCHECK(IsFound() || !holder_->HasFastProperties());
250 10528353 : }
251 :
252 3615441 : void LookupIterator::InternalUpdateProtector() {
253 3615441 : if (isolate_->bootstrapper()->IsActive()) return;
254 :
255 215759 : if (*name_ == heap()->constructor_string()) {
256 195044 : if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
257 : // Setting the constructor property could change an instance's @@species
258 184741 : if (holder_->IsJSArray()) {
259 : isolate_->CountUsage(
260 60 : v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
261 60 : isolate_->InvalidateArraySpeciesProtector();
262 184681 : } else if (holder_->map()->is_prototype_map()) {
263 : DisallowHeapAllocation no_gc;
264 : // Setting the constructor of Array.prototype of any realm also needs
265 : // to invalidate the species protector
266 82695 : if (isolate_->IsInAnyContext(*holder_,
267 82695 : Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
268 : isolate_->CountUsage(v8::Isolate::UseCounterFeature::
269 0 : kArrayPrototypeConstructorModified);
270 0 : isolate_->InvalidateArraySpeciesProtector();
271 : }
272 : }
273 20715 : } else if (*name_ == heap()->species_symbol()) {
274 4429 : if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
275 : // Setting the Symbol.species property of any Array constructor invalidates
276 : // the species protector
277 4334 : if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
278 : isolate_->CountUsage(
279 21 : v8::Isolate::UseCounterFeature::kArraySpeciesModified);
280 21 : isolate_->InvalidateArraySpeciesProtector();
281 : }
282 16286 : } else if (*name_ == heap()->is_concat_spreadable_symbol()) {
283 990 : if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
284 58 : isolate_->InvalidateIsConcatSpreadableProtector();
285 15296 : } else if (*name_ == heap()->iterator_symbol()) {
286 15296 : if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
287 12726 : if (holder_->IsJSArray()) {
288 46 : isolate_->InvalidateArrayIteratorProtector();
289 : }
290 : }
291 : }
292 :
293 19483639 : void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
294 : DCHECK(state_ == DATA || state_ == ACCESSOR);
295 : DCHECK(HolderIsReceiverOrHiddenPrototype());
296 :
297 : Handle<JSObject> holder = GetHolder<JSObject>();
298 :
299 8957445 : if (IsElement()) {
300 : ElementsKind kind = holder->GetElementsKind();
301 880061 : ElementsKind to = value->OptimalElementsKind();
302 880061 : if (IsHoleyOrDictionaryElementsKind(kind)) to = GetHoleyElementsKind(to);
303 : to = GetMoreGeneralElementsKind(kind, to);
304 :
305 880061 : if (kind != to) {
306 17563 : JSObject::TransitionElementsKind(holder, to);
307 : }
308 :
309 : // Copy the backing store if it is copy-on-write.
310 880061 : if (IsSmiOrObjectElementsKind(to)) {
311 104465 : JSObject::EnsureWritableFastElements(holder);
312 : }
313 8945786 : return;
314 : }
315 :
316 8077384 : if (holder->IsJSGlobalObject()) {
317 : Handle<GlobalDictionary> dictionary(
318 : JSGlobalObject::cast(*holder)->global_dictionary());
319 : Handle<PropertyCell> cell(dictionary->CellAt(dictionary_entry()));
320 2296692 : property_details_ = cell->property_details();
321 : PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
322 2296692 : property_details_);
323 : return;
324 : }
325 5780692 : if (!holder->HasFastProperties()) return;
326 :
327 : PropertyConstness new_constness = kConst;
328 : if (FLAG_track_constant_fields) {
329 : if (constness() == kConst) {
330 : DCHECK_EQ(kData, property_details_.kind());
331 : // Check that current value matches new value otherwise we should make
332 : // the property mutable.
333 : if (!IsConstFieldValueEqualTo(*value)) new_constness = kMutable;
334 : }
335 : } else {
336 : new_constness = kMutable;
337 : }
338 :
339 5652787 : Handle<Map> old_map(holder->map(), isolate_);
340 : Handle<Map> new_map = Map::PrepareForDataProperty(
341 5652787 : old_map, descriptor_number(), new_constness, value);
342 :
343 5652787 : if (old_map.is_identical_to(new_map)) {
344 : // Update the property details if the representation was None.
345 11280500 : if (constness() != new_constness || representation().IsNone()) {
346 : property_details_ =
347 280023 : new_map->instance_descriptors()->GetDetails(descriptor_number());
348 : }
349 : return;
350 : }
351 :
352 11659 : JSObject::MigrateToMap(holder, new_map);
353 11659 : ReloadPropertyInformation<false>();
354 : }
355 :
356 :
357 36624 : void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
358 103223 : PropertyAttributes attributes) {
359 : DCHECK(state_ == DATA || state_ == ACCESSOR);
360 : DCHECK(HolderIsReceiverOrHiddenPrototype());
361 : Handle<JSObject> holder = GetHolder<JSObject>();
362 36624 : if (IsElement()) {
363 : DCHECK(!holder->HasFixedTypedArrayElements());
364 : DCHECK(attributes != NONE || !holder->HasFastElements());
365 : Handle<FixedArrayBase> elements(holder->elements());
366 21434 : holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
367 21434 : attributes);
368 21434 : ReloadPropertyInformation<true>();
369 15190 : } else if (holder->HasFastProperties()) {
370 13600 : Handle<Map> old_map(holder->map(), isolate_);
371 : Handle<Map> new_map = Map::ReconfigureExistingProperty(
372 13600 : old_map, descriptor_number(), i::kData, attributes);
373 : // Force mutable to avoid changing constant value by reconfiguring
374 : // kData -> kAccessor -> kData.
375 : new_map = Map::PrepareForDataProperty(new_map, descriptor_number(),
376 13600 : kMutable, value);
377 13600 : JSObject::MigrateToMap(holder, new_map);
378 13600 : ReloadPropertyInformation<false>();
379 : }
380 :
381 51814 : if (!IsElement() && !holder->HasFastProperties()) {
382 : PropertyDetails details(kData, attributes, PropertyCellType::kMutable);
383 1590 : if (holder->IsJSGlobalObject()) {
384 : Handle<GlobalDictionary> dictionary(
385 : JSGlobalObject::cast(*holder)->global_dictionary());
386 :
387 : Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
388 405 : dictionary, dictionary_entry(), value, details);
389 405 : cell->set_value(*value);
390 405 : property_details_ = cell->property_details();
391 : } else {
392 : Handle<NameDictionary> dictionary(holder->property_dictionary());
393 : PropertyDetails original_details =
394 : dictionary->DetailsAt(dictionary_entry());
395 : int enumeration_index = original_details.dictionary_index();
396 : DCHECK_GT(enumeration_index, 0);
397 : details = details.set_index(enumeration_index);
398 1185 : dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
399 1185 : property_details_ = details;
400 : }
401 1590 : state_ = DATA;
402 : }
403 :
404 36624 : WriteDataValue(value, true);
405 :
406 : #if VERIFY_HEAP
407 : if (FLAG_verify_heap) {
408 : holder->JSObjectVerify();
409 : }
410 : #endif
411 36624 : }
412 :
413 : // Can only be called when the receiver is a JSObject. JSProxy has to be handled
414 : // via a trap. Adding properties to primitive values is not observable.
415 : // Returns true if a new transition has been created, or false if an existing
416 : // transition was followed.
417 36905837 : bool LookupIterator::PrepareTransitionToDataProperty(
418 : Handle<JSObject> receiver, Handle<Object> value,
419 36302730 : PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
420 : DCHECK(receiver.is_identical_to(GetStoreTarget()));
421 36905837 : if (state_ == TRANSITION) return false;
422 :
423 72605460 : if (!IsElement() && name()->IsPrivate()) {
424 2864385 : attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
425 : }
426 :
427 : DCHECK(state_ != LookupIterator::ACCESSOR ||
428 : (GetAccessors()->IsAccessorInfo() &&
429 : AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
430 : DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
431 : DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
432 :
433 36302732 : Handle<Map> map(receiver->map(), isolate_);
434 :
435 : // Dictionary maps can always have additional data properties.
436 36302733 : if (map->is_dictionary_map()) {
437 15600457 : state_ = TRANSITION;
438 15600457 : if (map->IsJSGlobalObjectMap()) {
439 : // Install a property cell.
440 : Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
441 : int entry;
442 : Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
443 7166316 : global, name(), PropertyCellType::kUninitialized, &entry);
444 : Handle<GlobalDictionary> dictionary(global->global_dictionary(),
445 7166316 : isolate_);
446 : DCHECK(cell->value()->IsTheHole(isolate_));
447 : DCHECK(!value->IsTheHole(isolate_));
448 7166316 : transition_ = cell;
449 : // Assign an enumeration index to the property and update
450 : // SetNextEnumerationIndex.
451 : int index = dictionary->NextEnumerationIndex();
452 7166316 : dictionary->SetNextEnumerationIndex(index + 1);
453 : property_details_ = PropertyDetails(
454 7166316 : kData, attributes, PropertyCellType::kUninitialized, index);
455 : PropertyCellType new_type =
456 7166316 : PropertyCell::UpdatedType(cell, value, property_details_);
457 7166316 : property_details_ = property_details_.set_cell_type(new_type);
458 : cell->set_property_details(property_details_);
459 7166316 : number_ = entry;
460 7166316 : has_property_ = true;
461 : } else {
462 : // Don't set enumeration index (it will be set during value store).
463 : property_details_ =
464 8434141 : PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
465 8434141 : transition_ = map;
466 : }
467 : return false;
468 : }
469 :
470 : bool created_new_map;
471 : Handle<Map> transition = Map::TransitionToDataProperty(
472 : map, name_, value, attributes, kDefaultFieldConstness, store_mode,
473 20702276 : &created_new_map);
474 20702276 : state_ = TRANSITION;
475 20702276 : transition_ = transition;
476 :
477 20702276 : if (transition->is_dictionary_map()) {
478 : // Don't set enumeration index (it will be set during value store).
479 : property_details_ =
480 26085 : PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
481 : } else {
482 20676192 : property_details_ = transition->GetLastDescriptorDetails();
483 20676192 : has_property_ = true;
484 : }
485 20702277 : return created_new_map;
486 : }
487 :
488 36305302 : void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
489 : DCHECK_EQ(TRANSITION, state_);
490 :
491 : DCHECK(receiver.is_identical_to(GetStoreTarget()));
492 36305302 : holder_ = receiver;
493 36305304 : if (receiver->IsJSGlobalObject()) {
494 7165806 : state_ = DATA;
495 36305307 : return;
496 : }
497 : Handle<Map> transition = transition_map();
498 29139498 : bool simple_transition = transition->GetBackPointer() == receiver->map();
499 29139495 : JSObject::MigrateToMap(receiver, transition);
500 :
501 29139500 : if (simple_transition) {
502 : int number = transition->LastAdded();
503 11369063 : number_ = static_cast<uint32_t>(number);
504 11369063 : property_details_ = transition->GetLastDescriptorDetails();
505 11369063 : state_ = DATA;
506 17770437 : } else if (receiver->map()->is_dictionary_map()) {
507 : Handle<NameDictionary> dictionary(receiver->property_dictionary(),
508 8460226 : isolate_);
509 : int entry;
510 : dictionary = NameDictionary::Add(dictionary, name(),
511 : isolate_->factory()->uninitialized_value(),
512 16920452 : property_details_, &entry);
513 8460226 : receiver->SetProperties(*dictionary);
514 : // Reload details containing proper enumeration index value.
515 16920452 : property_details_ = dictionary->DetailsAt(entry);
516 8460226 : number_ = entry;
517 8460226 : has_property_ = true;
518 8460226 : state_ = DATA;
519 :
520 : } else {
521 9310211 : ReloadPropertyInformation<false>();
522 : }
523 : }
524 :
525 :
526 200852 : void LookupIterator::Delete() {
527 : Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
528 200852 : if (IsElement()) {
529 : Handle<JSObject> object = Handle<JSObject>::cast(holder);
530 111661 : ElementsAccessor* accessor = object->GetElementsAccessor();
531 111661 : accessor->Delete(object, number_);
532 : } else {
533 : bool is_prototype_map = holder->map()->is_prototype_map();
534 : RuntimeCallTimerScope stats_scope(
535 : isolate_, is_prototype_map
536 : ? &RuntimeCallStats::PrototypeObject_DeleteProperty
537 89191 : : &RuntimeCallStats::Object_DeleteProperty);
538 :
539 : PropertyNormalizationMode mode =
540 89191 : is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
541 :
542 89191 : if (holder->HasFastProperties()) {
543 : JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
544 75915 : "DeletingProperty");
545 75915 : ReloadPropertyInformation<false>();
546 : }
547 89191 : JSReceiver::DeleteNormalizedProperty(holder, number_);
548 89191 : if (holder->IsJSObject()) {
549 89185 : JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
550 : }
551 : }
552 200852 : state_ = NOT_FOUND;
553 200852 : }
554 :
555 654036 : void LookupIterator::TransitionToAccessorProperty(
556 : Handle<Object> getter, Handle<Object> setter,
557 2480971 : PropertyAttributes attributes) {
558 : DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
559 : // Can only be called when the receiver is a JSObject. JSProxy has to be
560 : // handled via a trap. Adding properties to primitive values is not
561 : // observable.
562 654036 : Handle<JSObject> receiver = GetStoreTarget();
563 1281911 : if (!IsElement() && name()->IsPrivate()) {
564 18 : attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
565 : }
566 :
567 1281911 : if (!IsElement() && !receiver->map()->is_dictionary_map()) {
568 606689 : Handle<Map> old_map(receiver->map(), isolate_);
569 :
570 606689 : if (!holder_.is_identical_to(receiver)) {
571 0 : holder_ = receiver;
572 0 : state_ = NOT_FOUND;
573 606689 : } else if (state_ == INTERCEPTOR) {
574 36 : LookupInRegularHolder<false>(*old_map, *holder_);
575 : }
576 : int descriptor =
577 606689 : IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
578 :
579 : Handle<Map> new_map = Map::TransitionToAccessorProperty(
580 606689 : isolate_, old_map, name_, descriptor, getter, setter, attributes);
581 606689 : bool simple_transition = new_map->GetBackPointer() == receiver->map();
582 606689 : JSObject::MigrateToMap(receiver, new_map);
583 :
584 606689 : if (simple_transition) {
585 : int number = new_map->LastAdded();
586 19502 : number_ = static_cast<uint32_t>(number);
587 19502 : property_details_ = new_map->GetLastDescriptorDetails();
588 19502 : state_ = ACCESSOR;
589 19502 : return;
590 : }
591 :
592 587187 : ReloadPropertyInformation<false>();
593 587187 : if (!new_map->is_dictionary_map()) return;
594 : }
595 :
596 : Handle<AccessorPair> pair;
597 299985 : if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
598 : pair = Handle<AccessorPair>::cast(GetAccessors());
599 : // If the component and attributes are identical, nothing has to be done.
600 11150 : if (pair->Equals(*getter, *setter)) {
601 29 : if (property_details().attributes() == attributes) {
602 0 : if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
603 : return;
604 : }
605 : } else {
606 11121 : pair = AccessorPair::Copy(pair);
607 11121 : pair->SetComponents(*getter, *setter);
608 : }
609 : } else {
610 277530 : pair = factory()->NewAccessorPair();
611 277530 : pair->SetComponents(*getter, *setter);
612 : }
613 :
614 288680 : TransitionToAccessorPair(pair, attributes);
615 :
616 : #if VERIFY_HEAP
617 : if (FLAG_verify_heap) {
618 : receiver->JSObjectVerify();
619 : }
620 : #endif
621 : }
622 :
623 :
624 508346 : void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
625 508384 : PropertyAttributes attributes) {
626 508346 : Handle<JSObject> receiver = GetStoreTarget();
627 508346 : holder_ = receiver;
628 :
629 : PropertyDetails details(kAccessor, attributes, PropertyCellType::kMutable);
630 :
631 508346 : if (IsElement()) {
632 : // TODO(verwaest): Move code into the element accessor.
633 : Handle<SeededNumberDictionary> dictionary =
634 26173 : JSObject::NormalizeElements(receiver);
635 :
636 : dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, receiver,
637 26173 : details);
638 26173 : receiver->RequireSlowElements(*dictionary);
639 :
640 26173 : if (receiver->HasSlowArgumentsElements()) {
641 : FixedArray* parameter_map = FixedArray::cast(receiver->elements());
642 67 : uint32_t length = parameter_map->length() - 2;
643 67 : if (number_ < length) {
644 38 : parameter_map->set(number_ + 2, heap()->the_hole_value());
645 : }
646 67 : FixedArray::cast(receiver->elements())->set(1, *dictionary);
647 : } else {
648 26106 : receiver->set_elements(*dictionary);
649 : }
650 :
651 26173 : ReloadPropertyInformation<true>();
652 : } else {
653 : PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
654 : ? KEEP_INOBJECT_PROPERTIES
655 482173 : : CLEAR_INOBJECT_PROPERTIES;
656 : // Normalize object to make this operation simple.
657 : JSObject::NormalizeProperties(receiver, mode, 0,
658 482173 : "TransitionToAccessorPair");
659 :
660 482173 : JSObject::SetNormalizedProperty(receiver, name_, pair, details);
661 482173 : JSObject::ReoptimizeIfPrototype(receiver);
662 :
663 482173 : ReloadPropertyInformation<false>();
664 : }
665 508346 : }
666 :
667 72 : bool LookupIterator::HolderIsReceiver() const {
668 : DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
669 : // Optimization that only works if configuration_ is not mutable.
670 72 : if (!check_prototype_chain()) return true;
671 72 : return *receiver_ == *holder_;
672 : }
673 :
674 9169850 : bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
675 : DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
676 : // Optimization that only works if configuration_ is not mutable.
677 9162908 : if (!check_prototype_chain()) return true;
678 : DisallowHeapAllocation no_gc;
679 9150496 : if (*receiver_ == *holder_) return true;
680 349435 : if (!receiver_->IsJSReceiver()) return false;
681 : JSReceiver* current = JSReceiver::cast(*receiver_);
682 : JSReceiver* object = *holder_;
683 347384 : if (!current->map()->has_hidden_prototype()) return false;
684 : // JSProxy do not occur as hidden prototypes.
685 6942 : if (object->IsJSProxy()) return false;
686 : PrototypeIterator iter(isolate(), current, kStartAtPrototype,
687 : PrototypeIterator::END_AT_NON_HIDDEN);
688 7353 : while (!iter.IsAtEnd()) {
689 6942 : if (iter.GetCurrent<JSReceiver>() == object) return true;
690 411 : iter.Advance();
691 : }
692 : return false;
693 : }
694 :
695 :
696 117868199 : Handle<Object> LookupIterator::FetchValue() const {
697 : Object* result = nullptr;
698 117868199 : if (IsElement()) {
699 : Handle<JSObject> holder = GetHolder<JSObject>();
700 47469071 : ElementsAccessor* accessor = holder->GetElementsAccessor();
701 47469071 : return accessor->Get(holder, number_);
702 70399133 : } else if (holder_->IsJSGlobalObject()) {
703 : Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
704 5313895 : result = holder->global_dictionary()->ValueAt(number_);
705 65085238 : } else if (!holder_->HasFastProperties()) {
706 2322490 : result = holder_->property_dictionary()->ValueAt(number_);
707 63923995 : } else if (property_details_.location() == kField) {
708 : DCHECK_EQ(kData, property_details_.kind());
709 : Handle<JSObject> holder = GetHolder<JSObject>();
710 66958670 : FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
711 : return JSObject::FastPropertyAt(holder, property_details_.representation(),
712 33479335 : field_index);
713 : } else {
714 30444660 : result = holder_->map()->instance_descriptors()->GetValue(number_);
715 : }
716 36919800 : return handle(result, isolate_);
717 : }
718 :
719 0 : bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const {
720 : DCHECK(!IsElement());
721 : DCHECK(holder_->HasFastProperties());
722 : DCHECK_EQ(kField, property_details_.location());
723 : DCHECK_EQ(kConst, property_details_.constness());
724 : Handle<JSObject> holder = GetHolder<JSObject>();
725 0 : FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
726 0 : if (property_details_.representation().IsDouble()) {
727 0 : if (!value->IsNumber()) return false;
728 : uint64_t bits;
729 0 : if (holder->IsUnboxedDoubleField(field_index)) {
730 : bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
731 : } else {
732 0 : Object* current_value = holder->RawFastPropertyAt(field_index);
733 : DCHECK(current_value->IsMutableHeapNumber());
734 : bits = HeapNumber::cast(current_value)->value_as_bits();
735 : }
736 : // Use bit representation of double to to check for hole double, since
737 : // manipulating the signaling NaN used for the hole in C++, e.g. with
738 : // bit_cast or value(), will change its value on ia32 (the x87 stack is
739 : // used to return values and stores to the stack silently clear the
740 : // signalling bit).
741 0 : if (bits == kHoleNanInt64) {
742 : // Uninitialized double field.
743 : return true;
744 : }
745 0 : return bit_cast<double>(bits) == value->Number();
746 : } else {
747 0 : Object* current_value = holder->RawFastPropertyAt(field_index);
748 0 : return current_value->IsUninitialized(isolate()) || current_value == value;
749 : }
750 : }
751 :
752 166208 : int LookupIterator::GetFieldDescriptorIndex() const {
753 : DCHECK(has_property_);
754 : DCHECK(holder_->HasFastProperties());
755 : DCHECK_EQ(kField, property_details_.location());
756 : DCHECK_EQ(kData, property_details_.kind());
757 166208 : return descriptor_number();
758 : }
759 :
760 216210 : int LookupIterator::GetAccessorIndex() const {
761 : DCHECK(has_property_);
762 : DCHECK(holder_->HasFastProperties());
763 : DCHECK_EQ(kDescriptor, property_details_.location());
764 : DCHECK_EQ(kAccessor, property_details_.kind());
765 216210 : return descriptor_number();
766 : }
767 :
768 :
769 472419 : int LookupIterator::GetConstantIndex() const {
770 : DCHECK(has_property_);
771 : DCHECK(holder_->HasFastProperties());
772 : DCHECK_EQ(kDescriptor, property_details_.location());
773 : DCHECK_EQ(kData, property_details_.kind());
774 : DCHECK(!FLAG_track_constant_fields);
775 : DCHECK(!IsElement());
776 472419 : return descriptor_number();
777 : }
778 :
779 0 : Handle<Map> LookupIterator::GetFieldOwnerMap() const {
780 : DCHECK(has_property_);
781 : DCHECK(holder_->HasFastProperties());
782 : DCHECK_EQ(kField, property_details_.location());
783 : DCHECK(!IsElement());
784 : Map* holder_map = holder_->map();
785 0 : return handle(holder_map->FindFieldOwner(descriptor_number()), isolate_);
786 : }
787 :
788 1692640 : FieldIndex LookupIterator::GetFieldIndex() const {
789 : DCHECK(has_property_);
790 : DCHECK(holder_->HasFastProperties());
791 : DCHECK_EQ(kField, property_details_.location());
792 : DCHECK(!IsElement());
793 : Map* holder_map = holder_->map();
794 : int index =
795 : holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
796 : bool is_double = representation().IsDouble();
797 846322 : return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
798 : }
799 :
800 0 : Handle<FieldType> LookupIterator::GetFieldType() const {
801 : DCHECK(has_property_);
802 : DCHECK(holder_->HasFastProperties());
803 : DCHECK_EQ(kField, property_details_.location());
804 : return handle(
805 : holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
806 0 : isolate_);
807 : }
808 :
809 :
810 7956260 : Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
811 : DCHECK(!IsElement());
812 : Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
813 : return handle(holder->global_dictionary()->CellAt(dictionary_entry()),
814 7956262 : isolate_);
815 : }
816 :
817 :
818 8705729 : Handle<Object> LookupIterator::GetAccessors() const {
819 : DCHECK_EQ(ACCESSOR, state_);
820 21257130 : return FetchValue();
821 : }
822 :
823 :
824 96611071 : Handle<Object> LookupIterator::GetDataValue() const {
825 : DCHECK_EQ(DATA, state_);
826 96611071 : Handle<Object> value = FetchValue();
827 96611069 : return value;
828 : }
829 :
830 45075441 : void LookupIterator::WriteDataValue(Handle<Object> value,
831 80862159 : bool initializing_store) {
832 : DCHECK_EQ(DATA, state_);
833 : Handle<JSReceiver> holder = GetHolder<JSReceiver>();
834 45075441 : if (IsElement()) {
835 : Handle<JSObject> object = Handle<JSObject>::cast(holder);
836 901495 : ElementsAccessor* accessor = object->GetElementsAccessor();
837 1802990 : accessor->Set(object, number_, *value);
838 44173946 : } else if (holder->HasFastProperties()) {
839 26179418 : if (property_details_.location() == kField) {
840 : // Check that in case of kConst field the existing value is equal to
841 : // |value|.
842 : DCHECK_IMPLIES(
843 : !initializing_store && property_details_.constness() == kConst,
844 : IsConstFieldValueEqualTo(*value));
845 : JSObject::cast(*holder)->WriteToField(descriptor_number(),
846 17792187 : property_details_, *value);
847 : } else {
848 : DCHECK_EQ(kDescriptor, property_details_.location());
849 : DCHECK_EQ(kConst, property_details_.constness());
850 : }
851 17994531 : } else if (holder->IsJSGlobalObject()) {
852 : GlobalDictionary* dictionary =
853 : JSGlobalObject::cast(*holder)->global_dictionary();
854 9410325 : dictionary->CellAt(dictionary_entry())->set_value(*value);
855 : } else {
856 : NameDictionary* dictionary = holder->property_dictionary();
857 : dictionary->ValueAtPut(dictionary_entry(), *value);
858 : }
859 45075443 : }
860 :
861 : template <bool is_element>
862 960396 : bool LookupIterator::SkipInterceptor(JSObject* holder) {
863 : auto info = GetInterceptor<is_element>(holder);
864 651063 : if (!is_element && name_->IsSymbol() && !info->can_intercept_symbols()) {
865 : return true;
866 : }
867 960384 : if (info->non_masking()) {
868 402 : switch (interceptor_state_) {
869 : case InterceptorState::kUninitialized:
870 252 : interceptor_state_ = InterceptorState::kSkipNonMasking;
871 : // Fall through.
872 : case InterceptorState::kSkipNonMasking:
873 : return true;
874 : case InterceptorState::kProcessNonMasking:
875 : return false;
876 : }
877 : }
878 959982 : return interceptor_state_ == InterceptorState::kProcessNonMasking;
879 : }
880 :
881 633749307 : JSReceiver* LookupIterator::NextHolder(Map* map) {
882 : DisallowHeapAllocation no_gc;
883 370409570 : if (map->prototype() == heap()->null_value()) return nullptr;
884 327328459 : if (!check_prototype_chain() && !map->has_hidden_prototype()) return nullptr;
885 : return JSReceiver::cast(map->prototype());
886 : }
887 :
888 130208022 : LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
889 : DCHECK(!IsElement());
890 130336082 : if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
891 :
892 : Handle<String> name_string = Handle<String>::cast(name_);
893 109873 : if (name_string->length() == 0) return NOT_FOUND;
894 :
895 109873 : return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
896 : ? INTEGER_INDEXED_EXOTIC
897 109873 : : NOT_FOUND;
898 : }
899 :
900 : namespace {
901 :
902 : template <bool is_element>
903 : bool HasInterceptor(Map* map) {
904 : return is_element ? map->has_indexed_interceptor()
905 : : map->has_named_interceptor();
906 : }
907 :
908 : } // namespace
909 :
910 : template <bool is_element>
911 42389203 : LookupIterator::State LookupIterator::LookupInSpecialHolder(
912 : Map* const map, JSReceiver* const holder) {
913 : STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
914 42389203 : switch (state_) {
915 : case NOT_FOUND:
916 35612591 : if (map->IsJSProxyMap()) {
917 227468 : if (is_element || !name_->IsPrivate()) return JSPROXY;
918 : }
919 35375284 : if (map->is_access_check_needed()) {
920 6849665 : if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
921 : }
922 : // Fall through.
923 : case ACCESS_CHECK:
924 56766969 : if (check_interceptor() && HasInterceptor<is_element>(map) &&
925 960396 : !SkipInterceptor<is_element>(JSObject::cast(holder))) {
926 650741 : if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
927 : }
928 : // Fall through.
929 : case INTERCEPTOR:
930 35048593 : if (!is_element && map->IsJSGlobalObjectMap()) {
931 : GlobalDictionary* dict =
932 : JSGlobalObject::cast(holder)->global_dictionary();
933 : int number = dict->FindEntry(name_);
934 27294107 : if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
935 12169842 : number_ = static_cast<uint32_t>(number);
936 : PropertyCell* cell = dict->CellAt(number_);
937 24339684 : if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
938 12135374 : property_details_ = cell->property_details();
939 12135374 : has_property_ = true;
940 12135374 : switch (property_details_.kind()) {
941 : case v8::internal::kData:
942 : return DATA;
943 : case v8::internal::kAccessor:
944 50689 : return ACCESSOR;
945 : }
946 : }
947 8075389 : return LookupInRegularHolder<is_element>(map, holder);
948 : case ACCESSOR:
949 : case DATA:
950 : return NOT_FOUND;
951 : case INTEGER_INDEXED_EXOTIC:
952 : case JSPROXY:
953 : case TRANSITION:
954 0 : UNREACHABLE();
955 : }
956 0 : UNREACHABLE();
957 : }
958 :
959 : template <bool is_element>
960 499657435 : LookupIterator::State LookupIterator::LookupInRegularHolder(
961 : Map* const map, JSReceiver* const holder) {
962 : DisallowHeapAllocation no_gc;
963 499657435 : if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
964 : return NOT_FOUND;
965 : }
966 :
967 : if (is_element) {
968 : JSObject* js_object = JSObject::cast(holder);
969 274380962 : ElementsAccessor* accessor = js_object->GetElementsAccessor();
970 : FixedArrayBase* backing_store = js_object->elements();
971 274380961 : number_ =
972 274380962 : accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
973 274380961 : if (number_ == kMaxUInt32) {
974 225196019 : return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
975 : }
976 49184942 : property_details_ = accessor->GetDetails(js_object, number_);
977 225276373 : } else if (!map->is_dictionary_map()) {
978 : DescriptorArray* descriptors = map->instance_descriptors();
979 212491978 : int number = descriptors->SearchWithCache(isolate_, *name_, map);
980 212492004 : if (number == DescriptorArray::kNotFound) return NotFound(holder);
981 92866074 : number_ = static_cast<uint32_t>(number);
982 92866074 : property_details_ = descriptors->GetDetails(number_);
983 : } else {
984 : NameDictionary* dict = holder->property_dictionary();
985 : int number = dict->FindEntry(name_);
986 12784395 : if (number == NameDictionary::kNotFound) return NotFound(holder);
987 2202300 : number_ = static_cast<uint32_t>(number);
988 2202300 : property_details_ = dict->DetailsAt(number_);
989 : }
990 144253320 : has_property_ = true;
991 144253320 : switch (property_details_.kind()) {
992 : case v8::internal::kData:
993 : return DATA;
994 : case v8::internal::kAccessor:
995 8920945 : return ACCESSOR;
996 : }
997 :
998 0 : UNREACHABLE();
999 : }
1000 :
1001 1968 : Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
1002 1126 : const {
1003 : DCHECK_EQ(ACCESS_CHECK, state_);
1004 : DisallowHeapAllocation no_gc;
1005 : AccessCheckInfo* access_check_info =
1006 1968 : AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
1007 1968 : if (access_check_info) {
1008 : Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
1009 1126 : : access_check_info->named_interceptor();
1010 1126 : if (interceptor) {
1011 174 : return handle(InterceptorInfo::cast(interceptor), isolate_);
1012 : }
1013 : }
1014 1794 : return Handle<InterceptorInfo>();
1015 : }
1016 :
1017 8678271 : bool LookupIterator::TryLookupCachedProperty() {
1018 6264813 : return state() == LookupIterator::ACCESSOR &&
1019 14942404 : GetAccessors()->IsAccessorPair() && LookupCachedProperty();
1020 : }
1021 :
1022 18792495 : bool LookupIterator::LookupCachedProperty() {
1023 : DCHECK_EQ(state(), LookupIterator::ACCESSOR);
1024 : DCHECK(GetAccessors()->IsAccessorPair());
1025 :
1026 : AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
1027 : Handle<Object> getter(accessor_pair->getter(), isolate());
1028 : MaybeHandle<Name> maybe_name =
1029 6264133 : FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
1030 6264133 : if (maybe_name.is_null()) return false;
1031 :
1032 : // We have found a cached property! Modify the iterator accordingly.
1033 96 : name_ = maybe_name.ToHandleChecked();
1034 96 : Restart();
1035 96 : CHECK_EQ(state(), LookupIterator::DATA);
1036 : return true;
1037 : }
1038 :
1039 : } // namespace internal
1040 : } // namespace v8
|