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
33 namespace v8 {
34 namespace 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
44 class Descriptor BASE_EMBEDDED {
45 public:
IndexFromValue(Object * value)46 static int IndexFromValue(Object* value) {
47 return Smi::cast(value)->value();
48 }
49
KeyToSymbol()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
GetKey()58 String* GetKey() { return key_; }
GetValue()59 Object* GetValue() { return value_; }
GetDetails()60 PropertyDetails GetDetails() { return details_; }
61
62 #ifdef OBJECT_PRINT
63 void Print(FILE* out);
64 #endif
65
SetEnumerationIndex(int index)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:
Descriptor()79 Descriptor() : details_(Smi::FromInt(0)) {}
80
Init(String * key,Object * value,PropertyDetails details)81 void Init(String* key, Object* value, PropertyDetails details) {
82 key_ = key;
83 value_ = value;
84 details_ = details;
85 }
86
Descriptor(String * key,Object * value,PropertyDetails details)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)
key_(key)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.
108 class MapTransitionDescriptor: public Descriptor {
109 public:
MapTransitionDescriptor(String * key,Map * map,PropertyAttributes attributes)110 MapTransitionDescriptor(String* key, Map* map, PropertyAttributes attributes)
111 : Descriptor(key, map, attributes, MAP_TRANSITION) { }
112 };
113
114 class ElementsTransitionDescriptor: public Descriptor {
115 public:
ElementsTransitionDescriptor(String * key,Object * map_or_array)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.
127 class ConstTransitionDescriptor: public Descriptor {
128 public:
ConstTransitionDescriptor(String * key,Map * map)129 explicit ConstTransitionDescriptor(String* key, Map* map)
130 : Descriptor(key, map, NONE, CONSTANT_TRANSITION) { }
131 };
132
133
134 class FieldDescriptor: public Descriptor {
135 public:
136 FieldDescriptor(String* key,
137 int field_index,
138 PropertyAttributes attributes,
139 int index = 0)
Descriptor(key,Smi::FromInt (field_index),attributes,FIELD,index)140 : Descriptor(key, Smi::FromInt(field_index), attributes, FIELD, index) {}
141 };
142
143
144 class ConstantFunctionDescriptor: public Descriptor {
145 public:
146 ConstantFunctionDescriptor(String* key,
147 JSFunction* function,
148 PropertyAttributes attributes,
149 int index = 0)
Descriptor(key,function,attributes,CONSTANT_FUNCTION,index)150 : Descriptor(key, function, attributes, CONSTANT_FUNCTION, index) {}
151 };
152
153
154 class CallbacksDescriptor: public Descriptor {
155 public:
156 CallbacksDescriptor(String* key,
157 Object* foreign,
158 PropertyAttributes attributes,
159 int index = 0)
Descriptor(key,foreign,attributes,CALLBACKS,index)160 : Descriptor(key, foreign, attributes, CALLBACKS, index) {}
161 };
162
163
164 template <class T>
IsPropertyDescriptor(T * desc)165 bool 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
193 class LookupResult BASE_EMBEDDED {
194 public:
LookupResult(Isolate * isolate)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
~LookupResult()205 ~LookupResult() {
206 ASSERT(isolate_->top_lookup_result() == this);
207 isolate_->SetTopLookupResult(next_);
208 }
209
DescriptorResult(JSObject * holder,PropertyDetails details,int number)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
DescriptorResult(JSObject * holder,Smi * details,int number)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
ConstantResult(JSObject * holder)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
DictionaryResult(JSObject * holder,int entry)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
HandlerResult(JSProxy * proxy)241 void HandlerResult(JSProxy* proxy) {
242 lookup_type_ = HANDLER_TYPE;
243 holder_ = proxy;
244 details_ = PropertyDetails(NONE, HANDLER);
245 cacheable_ = false;
246 }
247
InterceptorResult(JSObject * holder)248 void InterceptorResult(JSObject* holder) {
249 lookup_type_ = INTERCEPTOR_TYPE;
250 holder_ = holder;
251 details_ = PropertyDetails(NONE, INTERCEPTOR);
252 }
253
NotFound()254 void NotFound() {
255 lookup_type_ = NOT_FOUND;
256 holder_ = NULL;
257 }
258
holder()259 JSObject* holder() {
260 ASSERT(IsFound());
261 return JSObject::cast(holder_);
262 }
263
proxy()264 JSProxy* proxy() {
265 ASSERT(IsFound());
266 return JSProxy::cast(holder_);
267 }
268
type()269 PropertyType type() {
270 ASSERT(IsFound());
271 return details_.type();
272 }
273
GetAttributes()274 PropertyAttributes GetAttributes() {
275 ASSERT(IsFound());
276 return details_.attributes();
277 }
278
GetPropertyDetails()279 PropertyDetails GetPropertyDetails() {
280 return details_;
281 }
282
IsReadOnly()283 bool IsReadOnly() { return details_.IsReadOnly(); }
IsDontDelete()284 bool IsDontDelete() { return details_.IsDontDelete(); }
IsDontEnum()285 bool IsDontEnum() { return details_.IsDontEnum(); }
IsDeleted()286 bool IsDeleted() { return details_.IsDeleted(); }
IsFound()287 bool IsFound() { return lookup_type_ != NOT_FOUND; }
IsHandler()288 bool IsHandler() { return lookup_type_ == HANDLER_TYPE; }
289
290 // Is the result is a property excluding transitions and the null descriptor?
IsProperty()291 bool IsProperty() {
292 return IsFound() && IsPropertyDescriptor(this);
293 }
294
IsCacheable()295 bool IsCacheable() { return cacheable_; }
DisallowCaching()296 void DisallowCaching() { cacheable_ = false; }
297
GetLazyValue()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
GetTransitionMap()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
GetTransitionMapFromMap(Map * map)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
GetFieldIndex()332 int GetFieldIndex() {
333 ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
334 ASSERT(type() == FIELD);
335 return Descriptor::IndexFromValue(GetValue());
336 }
337
GetLocalFieldIndexFromMap(Map * map)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
GetDictionaryEntry()346 int GetDictionaryEntry() {
347 ASSERT(lookup_type_ == DICTIONARY_TYPE);
348 return number_;
349 }
350
GetConstantFunction()351 JSFunction* GetConstantFunction() {
352 ASSERT(type() == CONSTANT_FUNCTION);
353 return JSFunction::cast(GetValue());
354 }
355
GetConstantFunctionFromMap(Map * map)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
GetCallbackObject()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
GetValue()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