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#include "transitions.h"
33
34namespace v8 {
35namespace internal {
36
37
38// Abstraction for elements in instance-descriptor arrays.
39//
40// Each descriptor has a key, property attributes, property type,
41// property index (in the actual instance-descriptor array) and
42// optionally a piece of data.
43//
44
45class Descriptor BASE_EMBEDDED {
46 public:
47  MUST_USE_RESULT MaybeObject* KeyToUniqueName() {
48    if (!key_->IsUniqueName()) {
49      MaybeObject* maybe_result =
50          key_->GetIsolate()->heap()->InternalizeString(String::cast(key_));
51      if (!maybe_result->To(&key_)) return maybe_result;
52    }
53    return key_;
54  }
55
56  Name* GetKey() { return key_; }
57  Object* GetValue() { return value_; }
58  PropertyDetails GetDetails() { return details_; }
59
60#ifdef OBJECT_PRINT
61  void Print(FILE* out);
62#endif
63
64  void SetSortedKeyIndex(int index) { details_ = details_.set_pointer(index); }
65
66 private:
67  Name* key_;
68  Object* value_;
69  PropertyDetails details_;
70
71 protected:
72  Descriptor() : details_(Smi::FromInt(0)) {}
73
74  void Init(Name* key, Object* value, PropertyDetails details) {
75    key_ = key;
76    value_ = value;
77    details_ = details;
78  }
79
80  Descriptor(Name* key, Object* value, PropertyDetails details)
81      : key_(key),
82        value_(value),
83        details_(details) { }
84
85  Descriptor(Name* key,
86             Object* value,
87             PropertyAttributes attributes,
88             PropertyType type,
89             Representation representation,
90             int field_index = 0)
91      : key_(key),
92        value_(value),
93        details_(attributes, type, representation, field_index) { }
94
95  friend class DescriptorArray;
96};
97
98
99class FieldDescriptor: public Descriptor {
100 public:
101  FieldDescriptor(Name* key,
102                  int field_index,
103                  PropertyAttributes attributes,
104                  Representation representation)
105      : Descriptor(key, Smi::FromInt(0), attributes,
106                   FIELD, representation, field_index) {}
107};
108
109
110class ConstantDescriptor: public Descriptor {
111 public:
112  ConstantDescriptor(Name* key,
113                     Object* value,
114                     PropertyAttributes attributes)
115      : Descriptor(key, value, attributes, CONSTANT,
116                   value->OptimalRepresentation()) {}
117};
118
119
120class CallbacksDescriptor:  public Descriptor {
121 public:
122  CallbacksDescriptor(Name* key,
123                      Object* foreign,
124                      PropertyAttributes attributes)
125      : Descriptor(key, foreign, attributes, CALLBACKS,
126                   Representation::Tagged()) {}
127};
128
129
130// Holds a property index value distinguishing if it is a field index or an
131// index inside the object header.
132class PropertyIndex {
133 public:
134  static PropertyIndex NewFieldIndex(int index) {
135    return PropertyIndex(index, false);
136  }
137  static PropertyIndex NewHeaderIndex(int index) {
138    return PropertyIndex(index, true);
139  }
140
141  bool is_field_index() { return (index_ & kHeaderIndexBit) == 0; }
142  bool is_header_index() { return (index_ & kHeaderIndexBit) != 0; }
143
144  int field_index() {
145    ASSERT(is_field_index());
146    return value();
147  }
148  int header_index() {
149    ASSERT(is_header_index());
150    return value();
151  }
152
153  bool is_inobject(Handle<JSObject> holder) {
154    if (is_header_index()) return true;
155    return field_index() < holder->map()->inobject_properties();
156  }
157
158  int translate(Handle<JSObject> holder) {
159    if (is_header_index()) return header_index();
160    int index = field_index() - holder->map()->inobject_properties();
161    if (index >= 0) return index;
162    return index + holder->map()->instance_size() / kPointerSize;
163  }
164
165 private:
166  static const int kHeaderIndexBit = 1 << 31;
167  static const int kIndexMask = ~kHeaderIndexBit;
168
169  int value() { return index_ & kIndexMask; }
170
171  PropertyIndex(int index, bool is_header_based)
172      : index_(index | (is_header_based ? kHeaderIndexBit : 0)) {
173    ASSERT(index <= kIndexMask);
174  }
175
176  int index_;
177};
178
179
180class LookupResult BASE_EMBEDDED {
181 public:
182  explicit LookupResult(Isolate* isolate)
183      : isolate_(isolate),
184        next_(isolate->top_lookup_result()),
185        lookup_type_(NOT_FOUND),
186        holder_(NULL),
187        transition_(NULL),
188        cacheable_(true),
189        details_(NONE, NONEXISTENT, Representation::None()) {
190    isolate->SetTopLookupResult(this);
191  }
192
193  ~LookupResult() {
194    ASSERT(isolate()->top_lookup_result() == this);
195    isolate()->SetTopLookupResult(next_);
196  }
197
198  Isolate* isolate() const { return isolate_; }
199
200  void DescriptorResult(JSObject* holder, PropertyDetails details, int number) {
201    lookup_type_ = DESCRIPTOR_TYPE;
202    holder_ = holder;
203    details_ = details;
204    number_ = number;
205    transition_ = NULL;
206  }
207
208  bool CanHoldValue(Handle<Object> value) {
209    if (IsNormal()) return true;
210    ASSERT(!IsTransition());
211    return value->FitsRepresentation(details_.representation());
212  }
213
214  void TransitionResult(JSObject* holder, Map* target) {
215    lookup_type_ = TRANSITION_TYPE;
216    details_ = PropertyDetails(NONE, TRANSITION, Representation::None());
217    holder_ = holder;
218    transition_ = target;
219    number_ = 0xAAAA;
220  }
221
222  void DictionaryResult(JSObject* holder, int entry) {
223    lookup_type_ = DICTIONARY_TYPE;
224    holder_ = holder;
225    transition_ = NULL;
226    details_ = holder->property_dictionary()->DetailsAt(entry);
227    number_ = entry;
228  }
229
230  void HandlerResult(JSProxy* proxy) {
231    lookup_type_ = HANDLER_TYPE;
232    holder_ = proxy;
233    transition_ = NULL;
234    details_ = PropertyDetails(NONE, HANDLER, Representation::Tagged());
235    cacheable_ = false;
236  }
237
238  void InterceptorResult(JSObject* holder) {
239    lookup_type_ = INTERCEPTOR_TYPE;
240    holder_ = holder;
241    transition_ = NULL;
242    details_ = PropertyDetails(NONE, INTERCEPTOR, Representation::Tagged());
243  }
244
245  void NotFound() {
246    lookup_type_ = NOT_FOUND;
247    details_ = PropertyDetails(NONE, NONEXISTENT, Representation::None());
248    holder_ = NULL;
249  }
250
251  JSObject* holder() {
252    ASSERT(IsFound());
253    return JSObject::cast(holder_);
254  }
255
256  JSProxy* proxy() {
257    ASSERT(IsHandler());
258    return JSProxy::cast(holder_);
259  }
260
261  PropertyType type() {
262    ASSERT(IsFound());
263    return details_.type();
264  }
265
266  Representation representation() {
267    ASSERT(IsFound());
268    ASSERT(!IsTransition());
269    ASSERT(details_.type() != NONEXISTENT);
270    return details_.representation();
271  }
272
273  PropertyAttributes GetAttributes() {
274    ASSERT(!IsTransition());
275    ASSERT(IsFound());
276    ASSERT(details_.type() != NONEXISTENT);
277    return details_.attributes();
278  }
279
280  PropertyDetails GetPropertyDetails() {
281    ASSERT(!IsTransition());
282    return details_;
283  }
284
285  bool IsFastPropertyType() {
286    ASSERT(IsFound());
287    return IsTransition() || type() != NORMAL;
288  }
289
290  // Property callbacks does not include transitions to callbacks.
291  bool IsPropertyCallbacks() {
292    ASSERT(!(details_.type() == CALLBACKS && !IsFound()));
293    return details_.type() == CALLBACKS;
294  }
295
296  bool IsReadOnly() {
297    ASSERT(IsFound());
298    ASSERT(!IsTransition());
299    ASSERT(details_.type() != NONEXISTENT);
300    return details_.IsReadOnly();
301  }
302
303  bool IsField() {
304    ASSERT(!(details_.type() == FIELD && !IsFound()));
305    return details_.type() == FIELD;
306  }
307
308  bool IsNormal() {
309    ASSERT(!(details_.type() == NORMAL && !IsFound()));
310    return details_.type() == NORMAL;
311  }
312
313  bool IsConstant() {
314    ASSERT(!(details_.type() == CONSTANT && !IsFound()));
315    return details_.type() == CONSTANT;
316  }
317
318  bool IsConstantFunction() {
319    return IsConstant() && GetValue()->IsJSFunction();
320  }
321
322  bool IsDontDelete() { return details_.IsDontDelete(); }
323  bool IsDontEnum() { return details_.IsDontEnum(); }
324  bool IsFound() { return lookup_type_ != NOT_FOUND; }
325  bool IsTransition() { return lookup_type_ == TRANSITION_TYPE; }
326  bool IsHandler() { return lookup_type_ == HANDLER_TYPE; }
327  bool IsInterceptor() { return lookup_type_ == INTERCEPTOR_TYPE; }
328
329  // Is the result is a property excluding transitions and the null descriptor?
330  bool IsProperty() {
331    return IsFound() && !IsTransition();
332  }
333
334  bool IsDataProperty() {
335    switch (type()) {
336      case FIELD:
337      case NORMAL:
338      case CONSTANT:
339        return true;
340      case CALLBACKS: {
341        Object* callback = GetCallbackObject();
342        return callback->IsAccessorInfo() || callback->IsForeign();
343      }
344      case HANDLER:
345      case INTERCEPTOR:
346      case TRANSITION:
347      case NONEXISTENT:
348        return false;
349    }
350    UNREACHABLE();
351    return false;
352  }
353
354  bool IsCacheable() { return cacheable_; }
355  void DisallowCaching() { cacheable_ = false; }
356
357  Object* GetLazyValue() {
358    switch (type()) {
359      case FIELD:
360        return holder()->RawFastPropertyAt(GetFieldIndex().field_index());
361      case NORMAL: {
362        Object* value;
363        value = holder()->property_dictionary()->ValueAt(GetDictionaryEntry());
364        if (holder()->IsGlobalObject()) {
365          value = PropertyCell::cast(value)->value();
366        }
367        return value;
368      }
369      case CONSTANT:
370        return GetConstant();
371      case CALLBACKS:
372      case HANDLER:
373      case INTERCEPTOR:
374      case TRANSITION:
375      case NONEXISTENT:
376        return isolate()->heap()->the_hole_value();
377    }
378    UNREACHABLE();
379    return NULL;
380  }
381
382  Map* GetTransitionTarget() {
383    return transition_;
384  }
385
386  PropertyDetails GetTransitionDetails() {
387    return transition_->GetLastDescriptorDetails();
388  }
389
390  bool IsTransitionToField() {
391    return IsTransition() && GetTransitionDetails().type() == FIELD;
392  }
393
394  bool IsTransitionToConstant() {
395    return IsTransition() && GetTransitionDetails().type() == CONSTANT;
396  }
397
398  int GetTransitionIndex() {
399    ASSERT(IsTransition());
400    return number_;
401  }
402
403  int GetDescriptorIndex() {
404    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
405    return number_;
406  }
407
408  PropertyIndex GetFieldIndex() {
409    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
410    return PropertyIndex::NewFieldIndex(GetFieldIndexFromMap(holder()->map()));
411  }
412
413  int GetLocalFieldIndexFromMap(Map* map) {
414    return GetFieldIndexFromMap(map) - map->inobject_properties();
415  }
416
417  int GetDictionaryEntry() {
418    ASSERT(lookup_type_ == DICTIONARY_TYPE);
419    return number_;
420  }
421
422  JSFunction* GetConstantFunction() {
423    ASSERT(type() == CONSTANT);
424    return JSFunction::cast(GetValue());
425  }
426
427  Object* GetConstantFromMap(Map* map) {
428    ASSERT(type() == CONSTANT);
429    return GetValueFromMap(map);
430  }
431
432  JSFunction* GetConstantFunctionFromMap(Map* map) {
433    return JSFunction::cast(GetConstantFromMap(map));
434  }
435
436  Object* GetConstant() {
437    ASSERT(type() == CONSTANT);
438    return GetValue();
439  }
440
441  Object* GetCallbackObject() {
442    ASSERT(type() == CALLBACKS && !IsTransition());
443    return GetValue();
444  }
445
446#ifdef OBJECT_PRINT
447  void Print(FILE* out);
448#endif
449
450  Object* GetValue() {
451    if (lookup_type_ == DESCRIPTOR_TYPE) {
452      return GetValueFromMap(holder()->map());
453    }
454    // In the dictionary case, the data is held in the value field.
455    ASSERT(lookup_type_ == DICTIONARY_TYPE);
456    return holder()->GetNormalizedProperty(this);
457  }
458
459  Object* GetValueFromMap(Map* map) const {
460    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
461    ASSERT(number_ < map->NumberOfOwnDescriptors());
462    return map->instance_descriptors()->GetValue(number_);
463  }
464
465  int GetFieldIndexFromMap(Map* map) const {
466    ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
467    ASSERT(number_ < map->NumberOfOwnDescriptors());
468    return map->instance_descriptors()->GetFieldIndex(number_);
469  }
470
471  void Iterate(ObjectVisitor* visitor);
472
473 private:
474  Isolate* isolate_;
475  LookupResult* next_;
476
477  // Where did we find the result;
478  enum {
479    NOT_FOUND,
480    DESCRIPTOR_TYPE,
481    TRANSITION_TYPE,
482    DICTIONARY_TYPE,
483    HANDLER_TYPE,
484    INTERCEPTOR_TYPE
485  } lookup_type_;
486
487  JSReceiver* holder_;
488  Map* transition_;
489  int number_;
490  bool cacheable_;
491  PropertyDetails details_;
492};
493
494
495} }  // namespace v8::internal
496
497#endif  // V8_PROPERTY_H_
498