deps: update v8 to 4.3.61.21
[platform/upstream/nodejs.git] / deps / v8 / src / lookup.cc
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/v8.h"
6
7 #include "src/bootstrapper.h"
8 #include "src/deoptimizer.h"
9 #include "src/lookup.h"
10 #include "src/lookup-inl.h"
11
12 namespace v8 {
13 namespace internal {
14
15
16 void LookupIterator::Next() {
17   DCHECK_NE(JSPROXY, state_);
18   DCHECK_NE(TRANSITION, state_);
19   DisallowHeapAllocation no_gc;
20   has_property_ = false;
21
22   JSReceiver* holder = *holder_;
23   Map* map = *holder_map_;
24
25   // Perform lookup on current holder.
26   state_ = LookupInHolder(map, holder);
27   if (IsFound()) return;
28
29   // Continue lookup if lookup on current holder failed.
30   do {
31     JSReceiver* maybe_holder = NextHolder(map);
32     if (maybe_holder == nullptr) {
33       if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
34         RestartLookupForNonMaskingInterceptors();
35         return;
36       }
37       break;
38     }
39     holder = maybe_holder;
40     map = holder->map();
41     state_ = LookupInHolder(map, holder);
42   } while (!IsFound());
43
44   if (holder != *holder_) {
45     holder_ = handle(holder, isolate_);
46     holder_map_ = handle(map, isolate_);
47   }
48 }
49
50
51 void LookupIterator::RestartLookupForNonMaskingInterceptors() {
52   interceptor_state_ = InterceptorState::kProcessNonMasking;
53   state_ = NOT_FOUND;
54   property_details_ = PropertyDetails::Empty();
55   number_ = DescriptorArray::kNotFound;
56   holder_ = initial_holder_;
57   holder_map_ = handle(holder_->map(), isolate_);
58   Next();
59 }
60
61
62 Handle<JSReceiver> LookupIterator::GetRoot(Handle<Object> receiver,
63                                            Isolate* isolate) {
64   if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
65   auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
66   if (root->IsNull()) {
67     unsigned int magic = 0xbbbbbbbb;
68     isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
69   }
70   return Handle<JSReceiver>::cast(root);
71 }
72
73
74 Handle<Map> LookupIterator::GetReceiverMap() const {
75   if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
76   return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
77 }
78
79
80 Handle<JSObject> LookupIterator::GetStoreTarget() const {
81   if (receiver_->IsJSGlobalProxy()) {
82     PrototypeIterator iter(isolate(), receiver_);
83     if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
84     return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
85   }
86   return Handle<JSObject>::cast(receiver_);
87 }
88
89
90 bool LookupIterator::IsBootstrapping() const {
91   return isolate_->bootstrapper()->IsActive();
92 }
93
94
95 bool LookupIterator::HasAccess() const {
96   DCHECK_EQ(ACCESS_CHECK, state_);
97   return isolate_->MayAccess(GetHolder<JSObject>());
98 }
99
100
101 void LookupIterator::ReloadPropertyInformation() {
102   state_ = BEFORE_PROPERTY;
103   interceptor_state_ = InterceptorState::kUninitialized;
104   state_ = LookupInHolder(*holder_map_, *holder_);
105   DCHECK(IsFound() || holder_map_->is_dictionary_map());
106 }
107
108
109 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
110   DCHECK(state_ == DATA || state_ == ACCESSOR);
111   DCHECK(HolderIsReceiverOrHiddenPrototype());
112   if (holder_map_->is_dictionary_map()) return;
113   holder_map_ =
114       Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
115   JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
116   ReloadPropertyInformation();
117 }
118
119
120 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
121                                              PropertyAttributes attributes) {
122   DCHECK(state_ == DATA || state_ == ACCESSOR);
123   DCHECK(HolderIsReceiverOrHiddenPrototype());
124   Handle<JSObject> holder = GetHolder<JSObject>();
125   if (holder_map_->is_dictionary_map()) {
126     PropertyDetails details(attributes, v8::internal::DATA, 0,
127                             PropertyCellType::kMutable);
128     JSObject::SetNormalizedProperty(holder, name(), value, details);
129   } else {
130     holder_map_ = Map::ReconfigureExistingProperty(
131         holder_map_, descriptor_number(), i::kData, attributes);
132     holder_map_ =
133         Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
134     JSObject::MigrateToMap(holder, holder_map_);
135   }
136
137   ReloadPropertyInformation();
138 }
139
140
141 void LookupIterator::PrepareTransitionToDataProperty(
142     Handle<Object> value, PropertyAttributes attributes,
143     Object::StoreFromKeyed store_mode) {
144   if (state_ == TRANSITION) return;
145   DCHECK_NE(LookupIterator::ACCESSOR, state_);
146   DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, state_);
147   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
148   // Can only be called when the receiver is a JSObject. JSProxy has to be
149   // handled via a trap. Adding properties to primitive values is not
150   // observable.
151   Handle<JSObject> receiver = GetStoreTarget();
152
153   if (!isolate()->IsInternallyUsedPropertyName(name()) &&
154       !receiver->map()->is_extensible()) {
155     return;
156   }
157
158   auto transition = Map::TransitionToDataProperty(
159       handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
160   state_ = TRANSITION;
161   transition_ = transition;
162
163   if (receiver->IsGlobalObject()) {
164     // Install a property cell.
165     InternalizeName();
166     auto cell = GlobalObject::EnsurePropertyCell(
167         Handle<GlobalObject>::cast(receiver), name());
168     DCHECK(cell->value()->IsTheHole());
169     transition_ = cell;
170   } else if (transition->GetBackPointer()->IsMap()) {
171     property_details_ = transition->GetLastDescriptorDetails();
172     has_property_ = true;
173   }
174 }
175
176
177 void LookupIterator::ApplyTransitionToDataProperty() {
178   DCHECK_EQ(TRANSITION, state_);
179
180   Handle<JSObject> receiver = GetStoreTarget();
181   if (receiver->IsGlobalObject()) return;
182   holder_ = receiver;
183   holder_map_ = transition_map();
184   JSObject::MigrateToMap(receiver, holder_map_);
185   ReloadPropertyInformation();
186 }
187
188
189 void LookupIterator::TransitionToAccessorProperty(
190     AccessorComponent component, Handle<Object> accessor,
191     PropertyAttributes attributes) {
192   DCHECK(!accessor->IsNull());
193   // Can only be called when the receiver is a JSObject. JSProxy has to be
194   // handled via a trap. Adding properties to primitive values is not
195   // observable.
196   Handle<JSObject> receiver = GetStoreTarget();
197   holder_ = receiver;
198   holder_map_ =
199       Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
200                                         name_, component, accessor, attributes);
201   JSObject::MigrateToMap(receiver, holder_map_);
202
203   ReloadPropertyInformation();
204
205   if (!holder_map_->is_dictionary_map()) return;
206
207
208   // Install the accessor into the dictionary-mode object.
209   PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
210                           PropertyCellType::kMutable);
211   Handle<AccessorPair> pair;
212   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
213     pair = Handle<AccessorPair>::cast(GetAccessors());
214     // If the component and attributes are identical, nothing has to be done.
215     if (pair->get(component) == *accessor) {
216       if (property_details().attributes() == attributes) return;
217     } else {
218       pair = AccessorPair::Copy(pair);
219       pair->set(component, *accessor);
220     }
221   } else {
222     pair = isolate()->factory()->NewAccessorPair();
223     pair->set(component, *accessor);
224   }
225   JSObject::SetNormalizedProperty(receiver, name_, pair, details);
226
227   JSObject::ReoptimizeIfPrototype(receiver);
228   holder_map_ = handle(receiver->map(), isolate_);
229   ReloadPropertyInformation();
230 }
231
232
233 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
234   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
235   // Optimization that only works if configuration_ is not mutable.
236   if (!check_prototype_chain()) return true;
237   DisallowHeapAllocation no_gc;
238   if (!receiver_->IsJSReceiver()) return false;
239   Object* current = *receiver_;
240   JSReceiver* holder = *holder_;
241   // JSProxy do not occur as hidden prototypes.
242   if (current->IsJSProxy()) {
243     return JSReceiver::cast(current) == holder;
244   }
245   PrototypeIterator iter(isolate(), current,
246                          PrototypeIterator::START_AT_RECEIVER);
247   do {
248     if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
249     DCHECK(!current->IsJSProxy());
250     iter.Advance();
251   } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
252   return false;
253 }
254
255
256 Handle<Object> LookupIterator::FetchValue() const {
257   Object* result = NULL;
258   Handle<JSObject> holder = GetHolder<JSObject>();
259   if (holder_map_->is_dictionary_map()) {
260     result = holder->property_dictionary()->ValueAt(number_);
261     if (holder_map_->IsGlobalObjectMap()) {
262       DCHECK(result->IsPropertyCell());
263       result = PropertyCell::cast(result)->value();
264     }
265   } else if (property_details_.type() == v8::internal::DATA) {
266     FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
267     return JSObject::FastPropertyAt(holder, property_details_.representation(),
268                                     field_index);
269   } else {
270     result = holder_map_->instance_descriptors()->GetValue(number_);
271   }
272   return handle(result, isolate_);
273 }
274
275
276 int LookupIterator::GetAccessorIndex() const {
277   DCHECK(has_property_);
278   DCHECK(!holder_map_->is_dictionary_map());
279   DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
280   return descriptor_number();
281 }
282
283
284 int LookupIterator::GetConstantIndex() const {
285   DCHECK(has_property_);
286   DCHECK(!holder_map_->is_dictionary_map());
287   DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
288   return descriptor_number();
289 }
290
291
292 FieldIndex LookupIterator::GetFieldIndex() const {
293   DCHECK(has_property_);
294   DCHECK(!holder_map_->is_dictionary_map());
295   DCHECK_EQ(v8::internal::DATA, property_details_.type());
296   int index =
297       holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
298   bool is_double = representation().IsDouble();
299   return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
300 }
301
302
303 Handle<HeapType> LookupIterator::GetFieldType() const {
304   DCHECK(has_property_);
305   DCHECK(!holder_map_->is_dictionary_map());
306   DCHECK_EQ(v8::internal::DATA, property_details_.type());
307   return handle(
308       holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
309       isolate_);
310 }
311
312
313 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
314   Handle<JSObject> holder = GetHolder<JSObject>();
315   Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
316   Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
317   DCHECK(value->IsPropertyCell());
318   return handle(PropertyCell::cast(value));
319 }
320
321
322 Handle<Object> LookupIterator::GetAccessors() const {
323   DCHECK_EQ(ACCESSOR, state_);
324   return FetchValue();
325 }
326
327
328 Handle<Object> LookupIterator::GetDataValue() const {
329   DCHECK_EQ(DATA, state_);
330   Handle<Object> value = FetchValue();
331   return value;
332 }
333
334
335 Handle<Object> LookupIterator::WriteDataValue(Handle<Object> value) {
336   DCHECK_EQ(DATA, state_);
337   Handle<JSObject> holder = GetHolder<JSObject>();
338   if (holder_map_->is_dictionary_map()) {
339     Handle<NameDictionary> property_dictionary =
340         handle(holder->property_dictionary());
341     if (holder->IsGlobalObject()) {
342       value = PropertyCell::UpdateCell(property_dictionary, dictionary_entry(),
343                                        value, property_details_);
344     } else {
345       property_dictionary->ValueAtPut(dictionary_entry(), *value);
346     }
347   } else if (property_details_.type() == v8::internal::DATA) {
348     holder->WriteToField(descriptor_number(), *value);
349   } else {
350     DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
351   }
352   return value;
353 }
354
355
356 bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
357   DCHECK(exotic_index_state_ != ExoticIndexState::kNoIndex);
358   // Currently typed arrays are the only such objects.
359   if (!holder->IsJSTypedArray()) return false;
360   if (exotic_index_state_ == ExoticIndexState::kIndex) return true;
361   DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
362   bool result = false;
363   // Compute and cache result.
364   if (name()->IsString()) {
365     Handle<String> name_string = Handle<String>::cast(name());
366     if (name_string->length() != 0) {
367       result = IsNonArrayIndexInteger(*name_string);
368     }
369   }
370   exotic_index_state_ =
371       result ? ExoticIndexState::kIndex : ExoticIndexState::kNoIndex;
372   return result;
373 }
374
375
376 void LookupIterator::InternalizeName() {
377   if (name_->IsUniqueName()) return;
378   name_ = factory()->InternalizeString(Handle<String>::cast(name_));
379 }
380
381
382 bool LookupIterator::SkipInterceptor(JSObject* holder) {
383   auto info = holder->GetNamedInterceptor();
384   // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
385   if (info->non_masking()) {
386     switch (interceptor_state_) {
387       case InterceptorState::kUninitialized:
388         interceptor_state_ = InterceptorState::kSkipNonMasking;
389       // Fall through.
390       case InterceptorState::kSkipNonMasking:
391         return true;
392       case InterceptorState::kProcessNonMasking:
393         return false;
394     }
395   }
396   return interceptor_state_ == InterceptorState::kProcessNonMasking;
397 }
398 } }  // namespace v8::internal