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 34 namespace v8 { 35 namespace 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 45 class 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 99 class 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 110 class 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 120 class 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. 132 class 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 180 class 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