672c026c3c7fdc3c30abe9674b940a630d8852e0
[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 == NULL) break;
33     holder = maybe_holder;
34     map = holder->map();
35     state_ = LookupInHolder(map, holder);
36   } while (!IsFound());
37
38   if (holder != *holder_) {
39     holder_ = handle(holder, isolate_);
40     holder_map_ = handle(map, isolate_);
41   }
42 }
43
44
45 Handle<JSReceiver> LookupIterator::GetRoot() const {
46   if (receiver_->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver_);
47   Handle<Object> root =
48       handle(receiver_->GetRootMap(isolate_)->prototype(), isolate_);
49   CHECK(!root->IsNull());
50   return Handle<JSReceiver>::cast(root);
51 }
52
53
54 Handle<Map> LookupIterator::GetReceiverMap() const {
55   if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
56   return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
57 }
58
59
60 Handle<JSObject> LookupIterator::GetStoreTarget() const {
61   if (receiver_->IsJSGlobalProxy()) {
62     PrototypeIterator iter(isolate(), receiver_);
63     if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
64     return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
65   }
66   return Handle<JSObject>::cast(receiver_);
67 }
68
69
70 bool LookupIterator::IsBootstrapping() const {
71   return isolate_->bootstrapper()->IsActive();
72 }
73
74
75 bool LookupIterator::HasAccess(v8::AccessType access_type) const {
76   DCHECK_EQ(ACCESS_CHECK, state_);
77   return isolate_->MayNamedAccess(GetHolder<JSObject>(), name_, access_type);
78 }
79
80
81 void LookupIterator::ReloadPropertyInformation() {
82   state_ = BEFORE_PROPERTY;
83   state_ = LookupInHolder(*holder_map_, *holder_);
84   DCHECK(IsFound() || holder_map_->is_dictionary_map());
85 }
86
87
88 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
89   DCHECK(state_ == DATA || state_ == ACCESSOR);
90   DCHECK(HolderIsReceiverOrHiddenPrototype());
91   if (holder_map_->is_dictionary_map()) return;
92   holder_map_ =
93       Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
94   JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
95   ReloadPropertyInformation();
96 }
97
98
99 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
100                                              PropertyAttributes attributes) {
101   DCHECK(state_ == DATA || state_ == ACCESSOR);
102   DCHECK(HolderIsReceiverOrHiddenPrototype());
103   Handle<JSObject> holder = GetHolder<JSObject>();
104   if (holder_map_->is_dictionary_map()) {
105     PropertyDetails details(attributes, v8::internal::DATA, 0);
106     JSObject::SetNormalizedProperty(holder, name(), value, details);
107   } else {
108     holder_map_ = Map::ReconfigureExistingProperty(
109         holder_map_, descriptor_number(), i::kData, attributes);
110     holder_map_ =
111         Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
112     JSObject::MigrateToMap(holder, holder_map_);
113   }
114
115   ReloadPropertyInformation();
116 }
117
118
119 void LookupIterator::PrepareTransitionToDataProperty(
120     Handle<Object> value, PropertyAttributes attributes,
121     Object::StoreFromKeyed store_mode) {
122   if (state_ == TRANSITION) return;
123   DCHECK(state_ != LookupIterator::ACCESSOR);
124   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
125   DCHECK(!IsSpecialNumericIndex());
126   // Can only be called when the receiver is a JSObject. JSProxy has to be
127   // handled via a trap. Adding properties to primitive values is not
128   // observable.
129   Handle<JSObject> receiver = GetStoreTarget();
130
131   if (!isolate()->IsInternallyUsedPropertyName(name()) &&
132       !receiver->map()->is_extensible()) {
133     return;
134   }
135
136   auto transition = Map::TransitionToDataProperty(
137       handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
138   state_ = TRANSITION;
139   transition_ = transition;
140
141   if (receiver->IsGlobalObject()) {
142     // Install a property cell.
143     InternalizeName();
144     auto cell = GlobalObject::EnsurePropertyCell(
145         Handle<GlobalObject>::cast(receiver), name());
146     DCHECK(cell->value()->IsTheHole());
147     transition_ = cell;
148   } else if (transition->GetBackPointer()->IsMap()) {
149     property_details_ = transition->GetLastDescriptorDetails();
150     has_property_ = true;
151   }
152 }
153
154
155 void LookupIterator::ApplyTransitionToDataProperty() {
156   DCHECK_EQ(TRANSITION, state_);
157
158   Handle<JSObject> receiver = GetStoreTarget();
159   if (receiver->IsGlobalObject()) return;
160   holder_ = receiver;
161   holder_map_ = transition_map();
162   JSObject::MigrateToMap(receiver, holder_map_);
163   ReloadPropertyInformation();
164 }
165
166
167 void LookupIterator::TransitionToAccessorProperty(
168     AccessorComponent component, Handle<Object> accessor,
169     PropertyAttributes attributes) {
170   DCHECK(!accessor->IsNull());
171   // Can only be called when the receiver is a JSObject. JSProxy has to be
172   // handled via a trap. Adding properties to primitive values is not
173   // observable.
174   Handle<JSObject> receiver = GetStoreTarget();
175   holder_ = receiver;
176   holder_map_ =
177       Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
178                                         name_, component, accessor, attributes);
179   JSObject::MigrateToMap(receiver, holder_map_);
180
181   ReloadPropertyInformation();
182
183   if (!holder_map_->is_dictionary_map()) return;
184
185   // We have to deoptimize since accesses to data properties may have been
186   // inlined without a corresponding map-check.
187   if (holder_map_->IsGlobalObjectMap()) {
188     Deoptimizer::DeoptimizeGlobalObject(*receiver);
189   }
190
191   // Install the accessor into the dictionary-mode object.
192   PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0);
193   Handle<AccessorPair> pair;
194   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
195     pair = Handle<AccessorPair>::cast(GetAccessors());
196     // If the component and attributes are identical, nothing has to be done.
197     if (pair->get(component) == *accessor) {
198       if (property_details().attributes() == attributes) return;
199     } else {
200       pair = AccessorPair::Copy(pair);
201       pair->set(component, *accessor);
202     }
203   } else {
204     pair = isolate()->factory()->NewAccessorPair();
205     pair->set(component, *accessor);
206   }
207   JSObject::SetNormalizedProperty(receiver, name_, pair, details);
208
209   JSObject::ReoptimizeIfPrototype(receiver);
210   holder_map_ = handle(receiver->map(), isolate_);
211   ReloadPropertyInformation();
212 }
213
214
215 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
216   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
217   // Optimization that only works if configuration_ is not mutable.
218   if (!check_prototype_chain()) return true;
219   DisallowHeapAllocation no_gc;
220   if (!receiver_->IsJSReceiver()) return false;
221   Object* current = *receiver_;
222   JSReceiver* holder = *holder_;
223   // JSProxy do not occur as hidden prototypes.
224   if (current->IsJSProxy()) {
225     return JSReceiver::cast(current) == holder;
226   }
227   PrototypeIterator iter(isolate(), current,
228                          PrototypeIterator::START_AT_RECEIVER);
229   do {
230     if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
231     DCHECK(!current->IsJSProxy());
232     iter.Advance();
233   } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
234   return false;
235 }
236
237
238 Handle<Object> LookupIterator::FetchValue() const {
239   Object* result = NULL;
240   Handle<JSObject> holder = GetHolder<JSObject>();
241   if (holder_map_->is_dictionary_map()) {
242     result = holder->property_dictionary()->ValueAt(number_);
243     if (holder_map_->IsGlobalObjectMap()) {
244       result = PropertyCell::cast(result)->value();
245     }
246   } else if (property_details_.type() == v8::internal::DATA) {
247     FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
248     return JSObject::FastPropertyAt(holder, property_details_.representation(),
249                                     field_index);
250   } else {
251     result = holder_map_->instance_descriptors()->GetValue(number_);
252   }
253   return handle(result, isolate_);
254 }
255
256
257 int LookupIterator::GetAccessorIndex() const {
258   DCHECK(has_property_);
259   DCHECK(!holder_map_->is_dictionary_map());
260   DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
261   return descriptor_number();
262 }
263
264
265 int LookupIterator::GetConstantIndex() const {
266   DCHECK(has_property_);
267   DCHECK(!holder_map_->is_dictionary_map());
268   DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
269   return descriptor_number();
270 }
271
272
273 FieldIndex LookupIterator::GetFieldIndex() const {
274   DCHECK(has_property_);
275   DCHECK(!holder_map_->is_dictionary_map());
276   DCHECK_EQ(v8::internal::DATA, property_details_.type());
277   int index =
278       holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
279   bool is_double = representation().IsDouble();
280   return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
281 }
282
283
284 Handle<HeapType> LookupIterator::GetFieldType() const {
285   DCHECK(has_property_);
286   DCHECK(!holder_map_->is_dictionary_map());
287   DCHECK_EQ(v8::internal::DATA, property_details_.type());
288   return handle(
289       holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
290       isolate_);
291 }
292
293
294 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
295   Handle<JSObject> holder = GetHolder<JSObject>();
296   Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
297   Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
298   return Handle<PropertyCell>(PropertyCell::cast(value));
299 }
300
301
302 Handle<Object> LookupIterator::GetAccessors() const {
303   DCHECK_EQ(ACCESSOR, state_);
304   return FetchValue();
305 }
306
307
308 Handle<Object> LookupIterator::GetDataValue() const {
309   DCHECK_EQ(DATA, state_);
310   Handle<Object> value = FetchValue();
311   return value;
312 }
313
314
315 Handle<Object> LookupIterator::WriteDataValue(Handle<Object> value) {
316   DCHECK_EQ(DATA, state_);
317   Handle<JSObject> holder = GetHolder<JSObject>();
318   if (holder_map_->is_dictionary_map()) {
319     NameDictionary* property_dictionary = holder->property_dictionary();
320     if (holder->IsGlobalObject()) {
321       Handle<PropertyCell> cell(
322           PropertyCell::cast(property_dictionary->ValueAt(dictionary_entry())));
323       value = PropertyCell::SetValueInferType(cell, value);
324     } else {
325       property_dictionary->ValueAtPut(dictionary_entry(), *value);
326     }
327   } else if (property_details_.type() == v8::internal::DATA) {
328     holder->WriteToField(descriptor_number(), *value);
329   } else {
330     DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
331   }
332   return value;
333 }
334
335
336 bool LookupIterator::IsSpecialNumericIndex() const {
337   if (GetStoreTarget()->IsJSTypedArray() && name()->IsString()) {
338     Handle<String> name_string = Handle<String>::cast(name());
339     if (name_string->length() > 0) {
340       double d =
341           StringToDouble(isolate()->unicode_cache(), name_string, NO_FLAGS);
342       if (!std::isnan(d)) {
343         if (String::Equals(isolate()->factory()->minus_zero_string(),
344                            name_string))
345           return true;
346
347         Factory* factory = isolate()->factory();
348         Handle<Object> num = factory->NewNumber(d);
349         Handle<String> roundtrip_string = factory->NumberToString(num);
350         if (String::Equals(name_string, roundtrip_string)) return true;
351       }
352     }
353   }
354   return false;
355 }
356
357
358 void LookupIterator::InternalizeName() {
359   if (name_->IsUniqueName()) return;
360   name_ = factory()->InternalizeString(Handle<String>::cast(name_));
361 }
362 } }  // namespace v8::internal