1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#ifndef V8_PROPERTY_H_
29#define V8_PROPERTY_H_
30
31#include "allocation.h"
32
33namespace v8 {
34namespace internal {
35
36
37// Abstraction for elements in instance-descriptor arrays.
38//
39// Each descriptor has a key, property attributes, property type,
40// property index (in the actual instance-descriptor array) and
41// optionally a piece of data.
42//
43
44class Descriptor BASE_EMBEDDED {
45 public:
46  static int IndexFromValue(Object* value) {
47    return Smi::cast(value)->value();
48  }
49
50  MUST_USE_RESULT MaybeObject* KeyToSymbol() {
51    if (!StringShape(key_).IsSymbol()) {
52      MaybeObject* maybe_result = HEAP->LookupSymbol(key_);
53      if (!maybe_result->To(&key_)) return maybe_result;
54    }
55    return key_;
56  }
57
58  String* GetKey() { return key_; }
59  Object* GetValue() { return value_; }
60  PropertyDetails GetDetails() { return details_; }
61
62#ifdef OBJECT_PRINT
63  void Print(FILE* out);
64#endif
65
66  void SetEnumerationIndex(int index) {
67    ASSERT(PropertyDetails::IsValidIndex(index));
68    details_ = PropertyDetails(details_.attributes(), details_.type(), index);
69  }
70
71  bool ContainsTransition();
72
73 private:
74  String* key_;
75  Object* value_;
76  PropertyDetails details_;
77
78 protected:
79  Descriptor() : details_(Smi::FromInt(0)) {}
80
81  void Init(String* key, Object* value, PropertyDetails details) {
82    key_ = key;
83    value_ = value;
84    details_ = details;
85  }
86
87  Descriptor(String* key, Object* value, PropertyDetails details)
88      : key_(key),
89        value_(value),
90        details_(details) { }
91
92  Descriptor(String* key,
93             Object* value,
94             PropertyAttributes attributes,
95             PropertyType type,
96             int index = 0)
97      : key_(key),
98        value_(value),
99        details_(attributes, type, index) { }
100
101  friend class DescriptorArray;
102};
103
104// A pointer from a map to the new map that is created by adding
105// a named property.  These are key to the speed and functioning of V8.
106// The two maps should always have the same prototype, since
107// MapSpace::CreateBackPointers depends on this.
108class MapTransitionDescriptor: public Descriptor {
109 public:
110  MapTransitionDescriptor(String* key, Map* map, PropertyAttributes attributes)
111      : Descriptor(key, map, attributes, MAP_TRANSITION) { }
112};
113
114class ElementsTransitionDescriptor: public Descriptor {
115 public:
116  ElementsTransitionDescriptor(String* key,
117                               Object* map_or_array)
118      : Descriptor(key, map_or_array, PropertyDetails(NONE,
119                                                      ELEMENTS_TRANSITION)) { }
120};
121
122// Marks a field name in a map so that adding the field is guaranteed
123// to create a FIELD descriptor in the new map.  Used after adding
124// a constant function the first time, creating a CONSTANT_FUNCTION
125// descriptor in the new map.  This avoids creating multiple maps with
126// the same CONSTANT_FUNCTION field.
127class ConstTransitionDescriptor: public Descriptor {
128 public:
129  explicit ConstTransitionDescriptor(String* key, Map* map)
130      : Descriptor(key, map, NONE, CONSTANT_TRANSITION) { }
131};
132
133
134class FieldDescriptor: public Descriptor {
135 public:
136  FieldDescriptor(String* key,
137                  int field_index,
138                  PropertyAttributes attributes,
139                  int index = 0)
140      : Descriptor(key, Smi::FromInt(field_index), attributes, FIELD, index) {}
141};
142
143
144class ConstantFunctionDescriptor: public Descriptor {
145 public:
146  ConstantFunctionDescriptor(String* key,
147                             JSFunction* function,
148                             PropertyAttributes attributes,
149                             int index = 0)
150      : Descriptor(key, function, attributes, CONSTANT_FUNCTION, index) {}
151};
152
153
154class CallbacksDescriptor:  public Descriptor {
155 public:
156  CallbacksDescriptor(String* key,
157                      Object* foreign,
158                      PropertyAttributes attributes,
159                      int index = 0)
160      : Descriptor(key, foreign, attributes, CALLBACKS, index) {}
161};
162
163
164template <class T>
165bool IsPropertyDescriptor(T* desc) {
166  switch (desc->type()) {
167    case NORMAL:
168    case FIELD:
169    case CONSTANT_FUNCTION:
170    case HANDLER:
171    case INTERCEPTOR:
172      return true;
173    case CALLBACKS: {
174      Object* callback_object = desc->GetCallbackObject();
175      // Non-JavaScript (i.e. native) accessors are always a property, otherwise
176      // either the getter or the setter must be an accessor. Put another way:
177      // If we only see map transitions and holes in a pair, this is not a
178      // property.
179      return (!callback_object->IsAccessorPair() ||
180              AccessorPair::cast(callback_object)->ContainsAccessor());
181    }
182    case MAP_TRANSITION:
183    case ELEMENTS_TRANSITION:
184    case CONSTANT_TRANSITION:
185    case NULL_DESCRIPTOR:
186      return false;
187  }
188  UNREACHABLE();  // keep the compiler happy
189  return false;
190}
191
192
193class LookupResult BASE_EMBEDDED {
194 public:
195  explicit LookupResult(Isolate* isolate)
196      : isolate_(isolate),
197        next_(isolate->top_lookup_result()),
198        lookup_type_(NOT_FOUND),
199        holder_(NULL),
200        cacheable_(true),
201        details_(NONE, NORMAL) {
202    isolate->SetTopLookupResult(this);
203  }
204
205  ~LookupResult() {
206    ASSERT(isolate_->top_lookup_result() == this);
207    isolate_->SetTopLookupResult(next_);
208  }
209
210  void DescriptorResult(JSObject* holder, PropertyDetails details, int number) {
211    lookup_type_ = DESCRIPTOR_TYPE;
212    holder_ = holder;
213    details_ = details;
214    number_ = number;
215  }
216
217  void DescriptorResult(JSObject* holder, Smi* details, int number) {
218    lookup_type_ = DESCRIPTOR_TYPE;
219    holder_ = holder;
220    details_ = PropertyDetails(details);
221    number_ = number;
222  }
223
224  void ConstantResult(JSObject* holder) {
225    lookup_type_ = CONSTANT_TYPE;
226    holder_ = holder;
227    details_ =
228        PropertyDetails(static_cast<PropertyAttributes>(DONT_ENUM |
229                                                        DONT_DELETE),
230                        CALLBACKS);
231    number_ = -1;
232  }
233
234  void DictionaryResult(JSObject* holder, int entry) {
235    lookup_type_ = DICTIONARY_TYPE;
236    holder_ = holder;
237    details_ = holder->property_dictionary()->DetailsAt(entry);
238    number_ = entry;
239  }
240
241  void HandlerResult(JSProxy* proxy) {
242    lookup_type_ = HANDLER_TYPE;
243    holder_ = proxy;
244    details_ = PropertyDetails(NONE, HANDLER);
245    cacheable_ = false;
246  }
247
248  void InterceptorResult(JSObject* holder) {
249    lookup_type_ = INTERCEPTOR_TYPE;
250    holder_ = holder;
251    details_ = PropertyDetails(NONE, INTERCEPTOR);
252  }
253
254  void NotFound() {
255    lookup_type_ = NOT_FOUND;
256    holder_ = NULL;
257  }
258
259  JSObject* holder() {
260    ASSERT(IsFound());
261    return JSObject::cast(holder_);
262  }
263
264  JSProxy* proxy() {
265    ASSERT(IsFound());
266    return JSProxy::cast(holder_);
267  }
268
269  PropertyType type() {
270    ASSERT(IsFound());
271    return details_.type();
272  }
273
274  PropertyAttributes GetAttributes() {
275    ASSERT(IsFound());
276    return details_.attributes();
277  }
278
279  PropertyDetails GetPropertyDetails() {
280    return details_;
281  }
282
283  bool IsReadOnly() { return details_.IsReadOnly(); }
284  bool IsDontDelete() { return details_.IsDontDelete(); }
285  bool IsDontEnum() { return details_.IsDontEnum(); }
286  bool IsDeleted() { return details_.IsDeleted(); }
287  bool IsFound() { return lookup_type_ != NOT_FOUND; }
288  bool IsHandler() { return lookup_type_ == HANDLER_TYPE; }
289
290  // Is the result is a property excluding transitions and the null descriptor?
291  bool IsProperty() {
292    return IsFound() && IsPropertyDescriptor(this);
293  }
294
295  bool IsCacheable() { return cacheable_; }
296  void DisallowCaching() { cacheable_ = false; }
297
298  Object* GetLazyValue() {
299    switch (type()) {
300      case FIELD:
301        return holder()->FastPropertyAt(GetFieldIndex());
302      case NORMAL: {
303        Object* value;
304        value = holder()->property_dictionary()->ValueAt(GetDictionaryEntry());
305        if (holder()->IsGlobalObject()) {
306          value = JSGlobalPropertyCell::cast(value)->value();
307        }
308        return value;
309      }
310      case CONSTANT_FUNCTION:
311        return GetConstantFunction();
312      default:
313        return Smi::FromInt(0);
314    }
315  }
316
317
318  Map* GetTransitionMap() {
319    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
320    ASSERT(type() == MAP_TRANSITION ||
321           type() == ELEMENTS_TRANSITION ||
322           type() == CONSTANT_TRANSITION);
323    return Map::cast(GetValue());
324  }
325
326  Map* GetTransitionMapFromMap(Map* map) {
327    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
328    ASSERT(type() == MAP_TRANSITION);
329    return Map::cast(map->instance_descriptors()->GetValue(number_));
330  }
331
332  int GetFieldIndex() {
333    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
334    ASSERT(type() == FIELD);
335    return Descriptor::IndexFromValue(GetValue());
336  }
337
338  int GetLocalFieldIndexFromMap(Map* map) {
339    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
340    ASSERT(type() == FIELD);
341    return Descriptor::IndexFromValue(
342        map->instance_descriptors()->GetValue(number_)) -
343        map->inobject_properties();
344  }
345
346  int GetDictionaryEntry() {
347    ASSERT(lookup_type_ == DICTIONARY_TYPE);
348    return number_;
349  }
350
351  JSFunction* GetConstantFunction() {
352    ASSERT(type() == CONSTANT_FUNCTION);
353    return JSFunction::cast(GetValue());
354  }
355
356  JSFunction* GetConstantFunctionFromMap(Map* map) {
357    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
358    ASSERT(type() == CONSTANT_FUNCTION);
359    return JSFunction::cast(map->instance_descriptors()->GetValue(number_));
360  }
361
362  Object* GetCallbackObject() {
363    if (lookup_type_ == CONSTANT_TYPE) {
364      // For now we only have the __proto__ as constant type.
365      return HEAP->prototype_accessors();
366    }
367    return GetValue();
368  }
369
370#ifdef OBJECT_PRINT
371  void Print(FILE* out);
372#endif
373
374  Object* GetValue() {
375    if (lookup_type_ == DESCRIPTOR_TYPE) {
376      DescriptorArray* descriptors = holder()->map()->instance_descriptors();
377      return descriptors->GetValue(number_);
378    }
379    // In the dictionary case, the data is held in the value field.
380    ASSERT(lookup_type_ == DICTIONARY_TYPE);
381    return holder()->GetNormalizedProperty(this);
382  }
383
384  void Iterate(ObjectVisitor* visitor);
385
386 private:
387  Isolate* isolate_;
388  LookupResult* next_;
389
390  // Where did we find the result;
391  enum {
392    NOT_FOUND,
393    DESCRIPTOR_TYPE,
394    DICTIONARY_TYPE,
395    HANDLER_TYPE,
396    INTERCEPTOR_TYPE,
397    CONSTANT_TYPE
398  } lookup_type_;
399
400  JSReceiver* holder_;
401  int number_;
402  bool cacheable_;
403  PropertyDetails details_;
404};
405
406
407} }  // namespace v8::internal
408
409#endif  // V8_PROPERTY_H_
410