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
12namespace v8 {
13namespace internal {
14
15
16void 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
45Handle<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
54Handle<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
60Handle<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
70bool LookupIterator::IsBootstrapping() const {
71  return isolate_->bootstrapper()->IsActive();
72}
73
74
75bool 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
81void LookupIterator::ReloadPropertyInformation() {
82  state_ = BEFORE_PROPERTY;
83  state_ = LookupInHolder(*holder_map_, *holder_);
84  DCHECK(IsFound() || holder_map_->is_dictionary_map());
85}
86
87
88void 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
99void 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, NORMAL, 0);
106    JSObject::SetNormalizedProperty(holder, name(), value, details);
107  } else {
108    holder_map_ = Map::ReconfigureDataProperty(holder_map_, descriptor_number(),
109                                               attributes);
110    JSObject::MigrateToMap(holder, holder_map_);
111  }
112
113  ReloadPropertyInformation();
114}
115
116
117void LookupIterator::PrepareTransitionToDataProperty(
118    Handle<Object> value, PropertyAttributes attributes,
119    Object::StoreFromKeyed store_mode) {
120  if (state_ == TRANSITION) return;
121  DCHECK(state_ != LookupIterator::ACCESSOR ||
122         GetAccessors()->IsDeclaredAccessorInfo());
123  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
124
125  // Can only be called when the receiver is a JSObject. JSProxy has to be
126  // handled via a trap. Adding properties to primitive values is not
127  // observable.
128  Handle<JSObject> receiver = GetStoreTarget();
129
130  if (!name().is_identical_to(isolate()->factory()->hidden_string()) &&
131      !receiver->map()->is_extensible()) {
132    return;
133  }
134
135  transition_map_ = Map::TransitionToDataProperty(
136      handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
137  state_ = TRANSITION;
138}
139
140
141void LookupIterator::ApplyTransitionToDataProperty() {
142  DCHECK_EQ(TRANSITION, state_);
143
144  Handle<JSObject> receiver = GetStoreTarget();
145  holder_ = receiver;
146  holder_map_ = transition_map_;
147  JSObject::MigrateToMap(receiver, holder_map_);
148  ReloadPropertyInformation();
149}
150
151
152void LookupIterator::TransitionToAccessorProperty(
153    AccessorComponent component, Handle<Object> accessor,
154    PropertyAttributes attributes) {
155  DCHECK(!accessor->IsNull());
156  // Can only be called when the receiver is a JSObject. JSProxy has to be
157  // handled via a trap. Adding properties to primitive values is not
158  // observable.
159  Handle<JSObject> receiver = GetStoreTarget();
160  holder_ = receiver;
161  holder_map_ =
162      Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
163                                        name_, component, accessor, attributes);
164  JSObject::MigrateToMap(receiver, holder_map_);
165
166  ReloadPropertyInformation();
167
168  if (!holder_map_->is_dictionary_map()) return;
169
170  // We have to deoptimize since accesses to data properties may have been
171  // inlined without a corresponding map-check.
172  if (holder_map_->IsGlobalObjectMap()) {
173    Deoptimizer::DeoptimizeGlobalObject(*receiver);
174  }
175
176  // Install the accessor into the dictionary-mode object.
177  PropertyDetails details(attributes, CALLBACKS, 0);
178  Handle<AccessorPair> pair;
179  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
180    pair = Handle<AccessorPair>::cast(GetAccessors());
181    // If the component and attributes are identical, nothing has to be done.
182    if (pair->get(component) == *accessor) {
183      if (property_details().attributes() == attributes) return;
184    } else {
185      pair = AccessorPair::Copy(pair);
186      pair->set(component, *accessor);
187    }
188  } else {
189    pair = isolate()->factory()->NewAccessorPair();
190    pair->set(component, *accessor);
191  }
192  JSObject::SetNormalizedProperty(receiver, name_, pair, details);
193
194  JSObject::ReoptimizeIfPrototype(receiver);
195  holder_map_ = handle(receiver->map(), isolate_);
196  ReloadPropertyInformation();
197}
198
199
200bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
201  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
202  // Optimization that only works if configuration_ is not mutable.
203  if (!check_prototype_chain()) return true;
204  DisallowHeapAllocation no_gc;
205  if (!receiver_->IsJSReceiver()) return false;
206  Object* current = *receiver_;
207  JSReceiver* holder = *holder_;
208  // JSProxy do not occur as hidden prototypes.
209  if (current->IsJSProxy()) {
210    return JSReceiver::cast(current) == holder;
211  }
212  PrototypeIterator iter(isolate(), current,
213                         PrototypeIterator::START_AT_RECEIVER);
214  do {
215    if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
216    DCHECK(!current->IsJSProxy());
217    iter.Advance();
218  } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
219  return false;
220}
221
222
223Handle<Object> LookupIterator::FetchValue() const {
224  Object* result = NULL;
225  Handle<JSObject> holder = GetHolder<JSObject>();
226  if (holder_map_->is_dictionary_map()) {
227    result = holder->property_dictionary()->ValueAt(number_);
228    if (holder_map_->IsGlobalObjectMap()) {
229      result = PropertyCell::cast(result)->value();
230    }
231  } else if (property_details_.type() == v8::internal::FIELD) {
232    FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
233    return JSObject::FastPropertyAt(holder, property_details_.representation(),
234                                    field_index);
235  } else {
236    result = holder_map_->instance_descriptors()->GetValue(number_);
237  }
238  return handle(result, isolate_);
239}
240
241
242int LookupIterator::GetConstantIndex() const {
243  DCHECK(has_property_);
244  DCHECK(!holder_map_->is_dictionary_map());
245  DCHECK_EQ(v8::internal::CONSTANT, property_details_.type());
246  return descriptor_number();
247}
248
249
250FieldIndex LookupIterator::GetFieldIndex() const {
251  DCHECK(has_property_);
252  DCHECK(!holder_map_->is_dictionary_map());
253  DCHECK_EQ(v8::internal::FIELD, property_details_.type());
254  int index =
255      holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
256  bool is_double = representation().IsDouble();
257  return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
258}
259
260
261Handle<HeapType> LookupIterator::GetFieldType() const {
262  DCHECK(has_property_);
263  DCHECK(!holder_map_->is_dictionary_map());
264  DCHECK_EQ(v8::internal::FIELD, property_details_.type());
265  return handle(
266      holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
267      isolate_);
268}
269
270
271Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
272  Handle<JSObject> holder = GetHolder<JSObject>();
273  Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
274  Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
275  return Handle<PropertyCell>(PropertyCell::cast(value));
276}
277
278
279Handle<Object> LookupIterator::GetAccessors() const {
280  DCHECK_EQ(ACCESSOR, state_);
281  return FetchValue();
282}
283
284
285Handle<Object> LookupIterator::GetDataValue() const {
286  DCHECK_EQ(DATA, state_);
287  Handle<Object> value = FetchValue();
288  return value;
289}
290
291
292void LookupIterator::WriteDataValue(Handle<Object> value) {
293  DCHECK_EQ(DATA, state_);
294  Handle<JSObject> holder = GetHolder<JSObject>();
295  if (holder_map_->is_dictionary_map()) {
296    NameDictionary* property_dictionary = holder->property_dictionary();
297    if (holder->IsGlobalObject()) {
298      Handle<PropertyCell> cell(
299          PropertyCell::cast(property_dictionary->ValueAt(dictionary_entry())));
300      PropertyCell::SetValueInferType(cell, value);
301    } else {
302      property_dictionary->ValueAtPut(dictionary_entry(), *value);
303    }
304  } else if (property_details_.type() == v8::internal::FIELD) {
305    holder->WriteToField(descriptor_number(), *value);
306  } else {
307    DCHECK_EQ(v8::internal::CONSTANT, property_details_.type());
308  }
309}
310
311
312void LookupIterator::InternalizeName() {
313  if (name_->IsUniqueName()) return;
314  name_ = factory()->InternalizeString(Handle<String>::cast(name_));
315}
316} }  // namespace v8::internal
317