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: 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. 108 class MapTransitionDescriptor: public Descriptor { 109 public: 110 MapTransitionDescriptor(String* key, Map* map, PropertyAttributes attributes) 111 : Descriptor(key, map, attributes, MAP_TRANSITION) { } 112 }; 113 114 class 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. 127 class ConstTransitionDescriptor: public Descriptor { 128 public: 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) 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) 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) 160 : Descriptor(key, foreign, attributes, CALLBACKS, index) {} 161 }; 162 163 164 template <class T> 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: 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