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