blob: 3df8752c01a7108ce145875a55d51bdab4185293 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00005#include "src/lookup.h"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00006
7#include "src/bootstrapper.h"
8#include "src/deoptimizer.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00009#include "src/elements.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +010010#include "src/field-type.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000011#include "src/isolate-inl.h"
Ben Murdochb8a8cc12014-11-26 15:28:44 +000012
13namespace v8 {
14namespace internal {
15
16
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000017// static
18LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
19 Handle<Object> receiver,
20 Handle<Object> key,
21 bool* success,
22 Configuration configuration) {
23 uint32_t index = 0;
24 if (key->ToArrayIndex(&index)) {
25 *success = true;
26 return LookupIterator(isolate, receiver, index, configuration);
27 }
28
29 Handle<Name> name;
30 *success = Object::ToName(isolate, key).ToHandle(&name);
31 if (!*success) {
32 DCHECK(isolate->has_pending_exception());
33 // Return an unusable dummy.
34 return LookupIterator(receiver, isolate->factory()->empty_string());
35 }
36
37 if (name->AsArrayIndex(&index)) {
38 LookupIterator it(isolate, receiver, index, configuration);
39 // Here we try to avoid having to rebuild the string later
40 // by storing it on the indexed LookupIterator.
41 it.name_ = name;
42 return it;
43 }
44
45 return LookupIterator(receiver, name, configuration);
46}
47
Ben Murdochda12d292016-06-02 14:46:10 +010048template <bool is_element>
49void LookupIterator::Start() {
50 DisallowHeapAllocation no_gc;
51
52 has_property_ = false;
53 state_ = NOT_FOUND;
54 holder_ = initial_holder_;
55
56 JSReceiver* holder = *holder_;
57 Map* map = holder->map();
58
59 state_ = LookupInHolder<is_element>(map, holder);
60 if (IsFound()) return;
61
62 NextInternal<is_element>(map, holder);
63}
64
65template void LookupIterator::Start<true>();
66template void LookupIterator::Start<false>();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000067
Ben Murdochb8a8cc12014-11-26 15:28:44 +000068void LookupIterator::Next() {
69 DCHECK_NE(JSPROXY, state_);
70 DCHECK_NE(TRANSITION, state_);
71 DisallowHeapAllocation no_gc;
72 has_property_ = false;
73
74 JSReceiver* holder = *holder_;
Ben Murdoch097c5b22016-05-18 11:27:45 +010075 Map* map = holder->map();
Ben Murdochb8a8cc12014-11-26 15:28:44 +000076
Ben Murdochda12d292016-06-02 14:46:10 +010077 if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
78 state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
79 : LookupInSpecialHolder<false>(map, holder);
80 if (IsFound()) return;
81 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +000082
Ben Murdochda12d292016-06-02 14:46:10 +010083 IsElement() ? NextInternal<true>(map, holder)
84 : NextInternal<false>(map, holder);
85}
86
87template <bool is_element>
88void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000089 do {
90 JSReceiver* maybe_holder = NextHolder(map);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000091 if (maybe_holder == nullptr) {
92 if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
Ben Murdochda12d292016-06-02 14:46:10 +010093 RestartLookupForNonMaskingInterceptors<is_element>();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000094 return;
95 }
Ben Murdochda12d292016-06-02 14:46:10 +010096 state_ = NOT_FOUND;
97 if (holder != *holder_) holder_ = handle(holder, isolate_);
98 return;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000099 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000100 holder = maybe_holder;
101 map = holder->map();
Ben Murdochda12d292016-06-02 14:46:10 +0100102 state_ = LookupInHolder<is_element>(map, holder);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000103 } while (!IsFound());
104
Ben Murdochda12d292016-06-02 14:46:10 +0100105 holder_ = handle(holder, isolate_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000106}
107
Ben Murdochda12d292016-06-02 14:46:10 +0100108template <bool is_element>
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000110 interceptor_state_ = interceptor_state;
111 property_details_ = PropertyDetails::Empty();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000112 number_ = DescriptorArray::kNotFound;
Ben Murdochda12d292016-06-02 14:46:10 +0100113 Start<is_element>();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000114}
115
Ben Murdochda12d292016-06-02 14:46:10 +0100116template void LookupIterator::RestartInternal<true>(InterceptorState);
117template void LookupIterator::RestartInternal<false>(InterceptorState);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000118
119// static
120Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
121 Isolate* isolate, Handle<Object> receiver, uint32_t index) {
122 // Strings are the only objects with properties (only elements) directly on
123 // the wrapper. Hence we can skip generating the wrapper for all other cases.
124 if (index != kMaxUInt32 && receiver->IsString() &&
125 index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
126 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
127 // context, ensuring that we don't leak it into JS?
128 Handle<JSFunction> constructor = isolate->string_function();
129 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
130 Handle<JSValue>::cast(result)->set_value(*receiver);
131 return result;
132 }
133 auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
134 if (root->IsNull()) {
135 unsigned int magic = 0xbbbbbbbb;
136 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
137 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000138 return Handle<JSReceiver>::cast(root);
139}
140
141
142Handle<Map> LookupIterator::GetReceiverMap() const {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000143 if (receiver_->IsNumber()) return factory()->heap_number_map();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000144 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
145}
146
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000147bool LookupIterator::HasAccess() const {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000148 DCHECK_EQ(ACCESS_CHECK, state_);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000149 return isolate_->MayAccess(handle(isolate_->context()),
150 GetHolder<JSObject>());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000151}
152
Ben Murdochda12d292016-06-02 14:46:10 +0100153template <bool is_element>
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000154void LookupIterator::ReloadPropertyInformation() {
155 state_ = BEFORE_PROPERTY;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000156 interceptor_state_ = InterceptorState::kUninitialized;
Ben Murdochda12d292016-06-02 14:46:10 +0100157 state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100158 DCHECK(IsFound() || !holder_->HasFastProperties());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000159}
160
Ben Murdoch097c5b22016-05-18 11:27:45 +0100161bool LookupIterator::HolderIsInContextIndex(uint32_t index) const {
162 DisallowHeapAllocation no_gc;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000163
Ben Murdoch097c5b22016-05-18 11:27:45 +0100164 Object* context = heap()->native_contexts_list();
165 while (!context->IsUndefined()) {
166 Context* current_context = Context::cast(context);
167 if (current_context->get(index) == *holder_) {
168 return true;
169 }
170 context = current_context->get(Context::NEXT_CONTEXT_LINK);
171 }
172 return false;
173}
174
Ben Murdochda12d292016-06-02 14:46:10 +0100175void LookupIterator::InternalUpdateProtector() {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100176 if (isolate_->bootstrapper()->IsActive()) return;
177 if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
178
Ben Murdochda12d292016-06-02 14:46:10 +0100179 if (*name_ == heap()->constructor_string()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100180 // Setting the constructor property could change an instance's @@species
181 if (holder_->IsJSArray()) {
182 isolate_->CountUsage(
183 v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
184 isolate_->InvalidateArraySpeciesProtector();
185 } else if (holder_->map()->is_prototype_map()) {
186 // Setting the constructor of Array.prototype of any realm also needs
187 // to invalidate the species protector
188 if (HolderIsInContextIndex(Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
189 isolate_->CountUsage(v8::Isolate::UseCounterFeature::
190 kArrayPrototypeConstructorModified);
191 isolate_->InvalidateArraySpeciesProtector();
192 }
193 }
Ben Murdochda12d292016-06-02 14:46:10 +0100194 } else if (*name_ == heap()->species_symbol()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100195 // Setting the Symbol.species property of any Array constructor invalidates
196 // the species protector
197 if (HolderIsInContextIndex(Context::ARRAY_FUNCTION_INDEX)) {
198 isolate_->CountUsage(
199 v8::Isolate::UseCounterFeature::kArraySpeciesModified);
200 isolate_->InvalidateArraySpeciesProtector();
201 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000202 }
203}
204
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000205void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
206 DCHECK(state_ == DATA || state_ == ACCESSOR);
207 DCHECK(HolderIsReceiverOrHiddenPrototype());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000208
209 Handle<JSObject> holder = GetHolder<JSObject>();
210
211 if (IsElement()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100212 ElementsKind kind = holder->GetElementsKind();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000213 ElementsKind to = value->OptimalElementsKind();
214 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
215 to = GetMoreGeneralElementsKind(kind, to);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100216
217 if (kind != to) {
218 JSObject::TransitionElementsKind(holder, to);
219 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000220
221 // Copy the backing store if it is copy-on-write.
222 if (IsFastSmiOrObjectElementsKind(to)) {
223 JSObject::EnsureWritableFastElements(holder);
224 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100225 return;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000226 }
227
Ben Murdoch097c5b22016-05-18 11:27:45 +0100228 if (!holder->HasFastProperties()) return;
229
230 Handle<Map> old_map(holder->map(), isolate_);
231 Handle<Map> new_map =
232 Map::PrepareForDataProperty(old_map, descriptor_number(), value);
233
234 if (old_map.is_identical_to(new_map)) {
235 // Update the property details if the representation was None.
236 if (representation().IsNone()) {
237 property_details_ =
238 new_map->instance_descriptors()->GetDetails(descriptor_number());
239 }
240 return;
241 }
242
243 JSObject::MigrateToMap(holder, new_map);
Ben Murdochda12d292016-06-02 14:46:10 +0100244 ReloadPropertyInformation<false>();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000245}
246
247
248void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
249 PropertyAttributes attributes) {
250 DCHECK(state_ == DATA || state_ == ACCESSOR);
251 DCHECK(HolderIsReceiverOrHiddenPrototype());
252 Handle<JSObject> holder = GetHolder<JSObject>();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000253 if (IsElement()) {
254 DCHECK(!holder->HasFixedTypedArrayElements());
255 DCHECK(attributes != NONE || !holder->HasFastElements());
256 Handle<FixedArrayBase> elements(holder->elements());
257 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
258 attributes);
Ben Murdochda12d292016-06-02 14:46:10 +0100259 ReloadPropertyInformation<true>();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000260 } else {
Ben Murdochda12d292016-06-02 14:46:10 +0100261 if (!holder->HasFastProperties()) {
262 PropertyDetails details(attributes, v8::internal::DATA, 0,
263 PropertyCellType::kMutable);
264 JSObject::SetNormalizedProperty(holder, name(), value, details);
265 } else {
266 Handle<Map> old_map(holder->map(), isolate_);
267 Handle<Map> new_map = Map::ReconfigureExistingProperty(
268 old_map, descriptor_number(), i::kData, attributes);
269 new_map =
270 Map::PrepareForDataProperty(new_map, descriptor_number(), value);
271 JSObject::MigrateToMap(holder, new_map);
272 }
273 ReloadPropertyInformation<false>();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000274 }
275
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000276 WriteDataValue(value);
277
278#if VERIFY_HEAP
279 if (FLAG_verify_heap) {
280 holder->JSObjectVerify();
281 }
282#endif
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000283}
284
Ben Murdoch097c5b22016-05-18 11:27:45 +0100285// Can only be called when the receiver is a JSObject. JSProxy has to be handled
286// via a trap. Adding properties to primitive values is not observable.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000287void LookupIterator::PrepareTransitionToDataProperty(
Ben Murdoch097c5b22016-05-18 11:27:45 +0100288 Handle<JSObject> receiver, Handle<Object> value,
289 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
290 DCHECK(receiver.is_identical_to(GetStoreTarget()));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000291 if (state_ == TRANSITION) return;
292 DCHECK(state_ != LookupIterator::ACCESSOR ||
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000293 (GetAccessors()->IsAccessorInfo() &&
294 AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
295 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000296 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000297
Ben Murdoch097c5b22016-05-18 11:27:45 +0100298 Handle<Map> map(receiver->map(), isolate_);
299
300 // Dictionary maps can always have additional data properties.
301 if (map->is_dictionary_map()) {
302 state_ = TRANSITION;
303 if (map->IsJSGlobalObjectMap()) {
304 // Install a property cell.
305 auto cell = JSGlobalObject::EnsurePropertyCell(
306 Handle<JSGlobalObject>::cast(receiver), name());
307 DCHECK(cell->value()->IsTheHole());
308 transition_ = cell;
309 } else {
310 transition_ = map;
311 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000312 return;
313 }
314
Ben Murdoch097c5b22016-05-18 11:27:45 +0100315 Handle<Map> transition =
316 Map::TransitionToDataProperty(map, name_, value, attributes, store_mode);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000317 state_ = TRANSITION;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000318 transition_ = transition;
319
Ben Murdoch097c5b22016-05-18 11:27:45 +0100320 if (!transition->is_dictionary_map()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000321 property_details_ = transition->GetLastDescriptorDetails();
322 has_property_ = true;
323 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000324}
325
Ben Murdoch097c5b22016-05-18 11:27:45 +0100326void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000327 DCHECK_EQ(TRANSITION, state_);
328
Ben Murdoch097c5b22016-05-18 11:27:45 +0100329 DCHECK(receiver.is_identical_to(GetStoreTarget()));
330
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000331 if (receiver->IsJSGlobalObject()) return;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000332 holder_ = receiver;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100333 Handle<Map> transition = transition_map();
334 bool simple_transition = transition->GetBackPointer() == receiver->map();
335 JSObject::MigrateToMap(receiver, transition);
336
337 if (simple_transition) {
338 int number = transition->LastAdded();
339 number_ = static_cast<uint32_t>(number);
340 property_details_ = transition->GetLastDescriptorDetails();
341 state_ = DATA;
342 } else {
Ben Murdochda12d292016-06-02 14:46:10 +0100343 ReloadPropertyInformation<false>();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100344 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000345}
346
347
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000348void LookupIterator::Delete() {
349 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
350 if (IsElement()) {
351 Handle<JSObject> object = Handle<JSObject>::cast(holder);
352 ElementsAccessor* accessor = object->GetElementsAccessor();
353 accessor->Delete(object, number_);
354 } else {
355 PropertyNormalizationMode mode = holder->map()->is_prototype_map()
356 ? KEEP_INOBJECT_PROPERTIES
357 : CLEAR_INOBJECT_PROPERTIES;
358
359 if (holder->HasFastProperties()) {
360 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
361 "DeletingProperty");
Ben Murdochda12d292016-06-02 14:46:10 +0100362 ReloadPropertyInformation<false>();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000363 }
364 // TODO(verwaest): Get rid of the name_ argument.
365 JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
366 if (holder->IsJSObject()) {
367 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
368 }
369 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100370 state_ = NOT_FOUND;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000371}
372
373
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000374void LookupIterator::TransitionToAccessorProperty(
375 AccessorComponent component, Handle<Object> accessor,
376 PropertyAttributes attributes) {
377 DCHECK(!accessor->IsNull());
378 // Can only be called when the receiver is a JSObject. JSProxy has to be
379 // handled via a trap. Adding properties to primitive values is not
380 // observable.
381 Handle<JSObject> receiver = GetStoreTarget();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000382
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000383 if (!IsElement() && !receiver->map()->is_dictionary_map()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100384 Handle<Map> old_map(receiver->map(), isolate_);
Ben Murdochda12d292016-06-02 14:46:10 +0100385
386 if (!holder_.is_identical_to(receiver)) {
387 holder_ = receiver;
388 state_ = NOT_FOUND;
389 } else if (state_ == INTERCEPTOR) {
390 LookupInRegularHolder<false>(*old_map, *holder_);
391 }
392 int descriptor =
393 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
394
Ben Murdoch097c5b22016-05-18 11:27:45 +0100395 Handle<Map> new_map = Map::TransitionToAccessorProperty(
Ben Murdochda12d292016-06-02 14:46:10 +0100396 old_map, name_, descriptor, component, accessor, attributes);
397 bool simple_transition = new_map->GetBackPointer() == receiver->map();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100398 JSObject::MigrateToMap(receiver, new_map);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000399
Ben Murdochda12d292016-06-02 14:46:10 +0100400 if (simple_transition) {
401 int number = new_map->LastAdded();
402 number_ = static_cast<uint32_t>(number);
403 property_details_ = new_map->GetLastDescriptorDetails();
404 state_ = ACCESSOR;
405 return;
406 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000407
Ben Murdochda12d292016-06-02 14:46:10 +0100408 ReloadPropertyInformation<false>();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100409 if (!new_map->is_dictionary_map()) return;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000410 }
411
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000412 Handle<AccessorPair> pair;
413 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
414 pair = Handle<AccessorPair>::cast(GetAccessors());
415 // If the component and attributes are identical, nothing has to be done.
416 if (pair->get(component) == *accessor) {
417 if (property_details().attributes() == attributes) return;
418 } else {
419 pair = AccessorPair::Copy(pair);
420 pair->set(component, *accessor);
421 }
422 } else {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000423 pair = factory()->NewAccessorPair();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000424 pair->set(component, *accessor);
425 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000426
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000427 TransitionToAccessorPair(pair, attributes);
428
429#if VERIFY_HEAP
430 if (FLAG_verify_heap) {
431 receiver->JSObjectVerify();
432 }
433#endif
434}
435
436
437void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
438 PropertyAttributes attributes) {
439 Handle<JSObject> receiver = GetStoreTarget();
440 holder_ = receiver;
441
442 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
443 PropertyCellType::kMutable);
444
445 if (IsElement()) {
446 // TODO(verwaest): Move code into the element accessor.
447 Handle<SeededNumberDictionary> dictionary =
448 JSObject::NormalizeElements(receiver);
449
450 // We unconditionally pass used_as_prototype=false here because the call
451 // to RequireSlowElements takes care of the required IC clearing and
452 // we don't want to walk the heap twice.
453 dictionary =
454 SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
455 receiver->RequireSlowElements(*dictionary);
456
457 if (receiver->HasSlowArgumentsElements()) {
458 FixedArray* parameter_map = FixedArray::cast(receiver->elements());
459 uint32_t length = parameter_map->length() - 2;
460 if (number_ < length) {
461 parameter_map->set(number_ + 2, heap()->the_hole_value());
462 }
463 FixedArray::cast(receiver->elements())->set(1, *dictionary);
464 } else {
465 receiver->set_elements(*dictionary);
466 }
Ben Murdochda12d292016-06-02 14:46:10 +0100467
468 ReloadPropertyInformation<true>();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000469 } else {
470 PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
471 ? KEEP_INOBJECT_PROPERTIES
472 : CLEAR_INOBJECT_PROPERTIES;
473 // Normalize object to make this operation simple.
474 JSObject::NormalizeProperties(receiver, mode, 0,
475 "TransitionToAccessorPair");
476
477 JSObject::SetNormalizedProperty(receiver, name_, pair, details);
478 JSObject::ReoptimizeIfPrototype(receiver);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000479
Ben Murdochda12d292016-06-02 14:46:10 +0100480 ReloadPropertyInformation<false>();
481 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000482}
483
484
485bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
486 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
487 // Optimization that only works if configuration_ is not mutable.
488 if (!check_prototype_chain()) return true;
489 DisallowHeapAllocation no_gc;
Ben Murdochda12d292016-06-02 14:46:10 +0100490 if (*receiver_ == *holder_) return true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000491 if (!receiver_->IsJSReceiver()) return false;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100492 JSReceiver* current = JSReceiver::cast(*receiver_);
493 JSReceiver* object = *holder_;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100494 if (!current->map()->has_hidden_prototype()) return false;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000495 // JSProxy do not occur as hidden prototypes.
Ben Murdochda12d292016-06-02 14:46:10 +0100496 if (object->IsJSProxy()) return false;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000497 PrototypeIterator iter(isolate(), current,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100498 PrototypeIterator::START_AT_PROTOTYPE,
499 PrototypeIterator::END_AT_NON_HIDDEN);
500 while (!iter.IsAtEnd()) {
501 if (iter.GetCurrent<JSReceiver>() == object) return true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000502 iter.Advance();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100503 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000504 return false;
505}
506
507
508Handle<Object> LookupIterator::FetchValue() const {
509 Object* result = NULL;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000510 if (IsElement()) {
511 Handle<JSObject> holder = GetHolder<JSObject>();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000512 ElementsAccessor* accessor = holder->GetElementsAccessor();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100513 return accessor->Get(holder, number_);
514 } else if (holder_->IsJSGlobalObject()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000515 Handle<JSObject> holder = GetHolder<JSObject>();
516 result = holder->global_dictionary()->ValueAt(number_);
517 DCHECK(result->IsPropertyCell());
518 result = PropertyCell::cast(result)->value();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100519 } else if (!holder_->HasFastProperties()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000520 result = holder_->property_dictionary()->ValueAt(number_);
521 } else if (property_details_.type() == v8::internal::DATA) {
522 Handle<JSObject> holder = GetHolder<JSObject>();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100523 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000524 return JSObject::FastPropertyAt(holder, property_details_.representation(),
525 field_index);
526 } else {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100527 result = holder_->map()->instance_descriptors()->GetValue(number_);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000528 }
529 return handle(result, isolate_);
530}
531
532
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000533int LookupIterator::GetAccessorIndex() const {
534 DCHECK(has_property_);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100535 DCHECK(holder_->HasFastProperties());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000536 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
537 return descriptor_number();
538}
539
540
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000541int LookupIterator::GetConstantIndex() const {
542 DCHECK(has_property_);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100543 DCHECK(holder_->HasFastProperties());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000544 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
545 DCHECK(!IsElement());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000546 return descriptor_number();
547}
548
549
550FieldIndex LookupIterator::GetFieldIndex() const {
551 DCHECK(has_property_);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100552 DCHECK(holder_->HasFastProperties());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000553 DCHECK_EQ(v8::internal::DATA, property_details_.type());
554 DCHECK(!IsElement());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100555 Map* holder_map = holder_->map();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000556 int index =
Ben Murdoch097c5b22016-05-18 11:27:45 +0100557 holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000558 bool is_double = representation().IsDouble();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100559 return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000560}
561
Ben Murdoch097c5b22016-05-18 11:27:45 +0100562Handle<FieldType> LookupIterator::GetFieldType() const {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000563 DCHECK(has_property_);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100564 DCHECK(holder_->HasFastProperties());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000565 DCHECK_EQ(v8::internal::DATA, property_details_.type());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000566 return handle(
Ben Murdoch097c5b22016-05-18 11:27:45 +0100567 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000568 isolate_);
569}
570
571
572Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000573 DCHECK(!IsElement());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000574 Handle<JSObject> holder = GetHolder<JSObject>();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000575 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
576 Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
577 DCHECK(value->IsPropertyCell());
578 return handle(PropertyCell::cast(value));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000579}
580
581
582Handle<Object> LookupIterator::GetAccessors() const {
583 DCHECK_EQ(ACCESSOR, state_);
584 return FetchValue();
585}
586
587
588Handle<Object> LookupIterator::GetDataValue() const {
589 DCHECK_EQ(DATA, state_);
590 Handle<Object> value = FetchValue();
591 return value;
592}
593
594
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000595void LookupIterator::WriteDataValue(Handle<Object> value) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000596 DCHECK_EQ(DATA, state_);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000597 Handle<JSReceiver> holder = GetHolder<JSReceiver>();
598 if (IsElement()) {
599 Handle<JSObject> object = Handle<JSObject>::cast(holder);
600 ElementsAccessor* accessor = object->GetElementsAccessor();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100601 accessor->Set(object, number_, *value);
602 } else if (holder->HasFastProperties()) {
603 if (property_details_.type() == v8::internal::DATA) {
604 JSObject::cast(*holder)->WriteToField(descriptor_number(),
605 property_details_, *value);
606 } else {
607 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
608 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000609 } else if (holder->IsJSGlobalObject()) {
610 Handle<GlobalDictionary> property_dictionary =
611 handle(JSObject::cast(*holder)->global_dictionary());
612 PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
613 property_details_);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100614 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000615 NameDictionary* property_dictionary = holder->property_dictionary();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000616 property_dictionary->ValueAtPut(dictionary_entry(), *value);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000617 }
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400618}
619
Ben Murdochda12d292016-06-02 14:46:10 +0100620template <bool is_element>
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000621bool LookupIterator::SkipInterceptor(JSObject* holder) {
Ben Murdochda12d292016-06-02 14:46:10 +0100622 auto info = GetInterceptor<is_element>(holder);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000623 // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
624 if (info->non_masking()) {
625 switch (interceptor_state_) {
626 case InterceptorState::kUninitialized:
627 interceptor_state_ = InterceptorState::kSkipNonMasking;
628 // Fall through.
629 case InterceptorState::kSkipNonMasking:
630 return true;
631 case InterceptorState::kProcessNonMasking:
632 return false;
633 }
634 }
635 return interceptor_state_ == InterceptorState::kProcessNonMasking;
636}
637
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000638JSReceiver* LookupIterator::NextHolder(Map* map) {
639 DisallowHeapAllocation no_gc;
Ben Murdochda12d292016-06-02 14:46:10 +0100640 if (map->prototype() == heap()->null_value()) return NULL;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000641
Ben Murdoch097c5b22016-05-18 11:27:45 +0100642 DCHECK(!map->IsJSGlobalProxyMap() || map->has_hidden_prototype());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000643
644 if (!check_prototype_chain() &&
Ben Murdoch097c5b22016-05-18 11:27:45 +0100645 !(check_hidden() && map->has_hidden_prototype()) &&
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000646 // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even
647 // when not checking other hidden prototypes.
648 !map->IsJSGlobalProxyMap()) {
649 return NULL;
650 }
651
Ben Murdoch097c5b22016-05-18 11:27:45 +0100652 return JSReceiver::cast(map->prototype());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000653}
654
Ben Murdoch097c5b22016-05-18 11:27:45 +0100655LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
656 DCHECK(!IsElement());
657 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
658
659 Handle<String> name_string = Handle<String>::cast(name_);
660 if (name_string->length() == 0) return NOT_FOUND;
661
662 return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
663 ? INTEGER_INDEXED_EXOTIC
664 : NOT_FOUND;
665}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000666
Ben Murdochda12d292016-06-02 14:46:10 +0100667namespace {
668
669template <bool is_element>
670bool HasInterceptor(Map* map) {
671 return is_element ? map->has_indexed_interceptor()
672 : map->has_named_interceptor();
673}
674
675} // namespace
676
677template <bool is_element>
678LookupIterator::State LookupIterator::LookupInSpecialHolder(
679 Map* const map, JSReceiver* const holder) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000680 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000681 switch (state_) {
682 case NOT_FOUND:
683 if (map->IsJSProxyMap()) {
Ben Murdochda12d292016-06-02 14:46:10 +0100684 if (is_element || !name_->IsPrivate()) return JSPROXY;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000685 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100686 if (map->is_access_check_needed()) {
Ben Murdochda12d292016-06-02 14:46:10 +0100687 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000688 }
689 // Fall through.
690 case ACCESS_CHECK:
Ben Murdochda12d292016-06-02 14:46:10 +0100691 if (check_interceptor() && HasInterceptor<is_element>(map) &&
692 !SkipInterceptor<is_element>(JSObject::cast(holder))) {
693 if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000694 }
695 // Fall through.
696 case INTERCEPTOR:
Ben Murdochda12d292016-06-02 14:46:10 +0100697 if (!is_element && map->IsJSGlobalObjectMap()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000698 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
699 int number = dict->FindEntry(name_);
700 if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
701 number_ = static_cast<uint32_t>(number);
702 DCHECK(dict->ValueAt(number_)->IsPropertyCell());
703 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
704 if (cell->value()->IsTheHole()) return NOT_FOUND;
705 property_details_ = cell->property_details();
Ben Murdochda12d292016-06-02 14:46:10 +0100706 has_property_ = true;
707 switch (property_details_.kind()) {
708 case v8::internal::kData:
709 return DATA;
710 case v8::internal::kAccessor:
711 return ACCESSOR;
712 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000713 }
Ben Murdochda12d292016-06-02 14:46:10 +0100714 return LookupInRegularHolder<is_element>(map, holder);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000715 case ACCESSOR:
716 case DATA:
717 return NOT_FOUND;
718 case INTEGER_INDEXED_EXOTIC:
719 case JSPROXY:
720 case TRANSITION:
721 UNREACHABLE();
722 }
723 UNREACHABLE();
Ben Murdochda12d292016-06-02 14:46:10 +0100724 return NOT_FOUND;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000725}
726
Ben Murdochda12d292016-06-02 14:46:10 +0100727template <bool is_element>
728LookupIterator::State LookupIterator::LookupInRegularHolder(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000729 Map* const map, JSReceiver* const holder) {
Ben Murdochda12d292016-06-02 14:46:10 +0100730 DisallowHeapAllocation no_gc;
731 if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
732 return NOT_FOUND;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000733 }
Ben Murdochda12d292016-06-02 14:46:10 +0100734
735 if (is_element) {
736 JSObject* js_object = JSObject::cast(holder);
737 ElementsAccessor* accessor = js_object->GetElementsAccessor();
738 FixedArrayBase* backing_store = js_object->elements();
739 number_ = accessor->GetEntryForIndex(js_object, backing_store, index_);
740 if (number_ == kMaxUInt32) {
741 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
742 }
743 property_details_ = accessor->GetDetails(js_object, number_);
744 } else if (!map->is_dictionary_map()) {
745 DescriptorArray* descriptors = map->instance_descriptors();
746 int number = descriptors->SearchWithCache(isolate_, *name_, map);
747 if (number == DescriptorArray::kNotFound) return NotFound(holder);
748 number_ = static_cast<uint32_t>(number);
749 property_details_ = descriptors->GetDetails(number_);
750 } else {
751 NameDictionary* dict = holder->property_dictionary();
752 int number = dict->FindEntry(name_);
753 if (number == NameDictionary::kNotFound) return NotFound(holder);
754 number_ = static_cast<uint32_t>(number);
755 property_details_ = dict->DetailsAt(number_);
756 }
757 has_property_ = true;
758 switch (property_details_.kind()) {
759 case v8::internal::kData:
760 return DATA;
761 case v8::internal::kAccessor:
762 return ACCESSOR;
763 }
764
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000765 UNREACHABLE();
766 return state_;
767}
768
769} // namespace internal
770} // namespace v8