1 // Copyright 2013 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 #include "v8.h" 29 30 #include "accessors.h" 31 #include "api.h" 32 #include "arguments.h" 33 #include "bootstrapper.h" 34 #include "codegen.h" 35 #include "debug.h" 36 #include "deoptimizer.h" 37 #include "date.h" 38 #include "elements.h" 39 #include "execution.h" 40 #include "full-codegen.h" 41 #include "hydrogen.h" 42 #include "isolate-inl.h" 43 #include "objects-inl.h" 44 #include "objects-visiting.h" 45 #include "objects-visiting-inl.h" 46 #include "macro-assembler.h" 47 #include "mark-compact.h" 48 #include "safepoint-table.h" 49 #include "string-stream.h" 50 #include "utils.h" 51 52 #ifdef ENABLE_DISASSEMBLER 53 #include "disasm.h" 54 #include "disassembler.h" 55 #endif 56 57 namespace v8 { 58 namespace internal { 59 60 61 MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor, 62 Object* value) { 63 Object* result; 64 { MaybeObject* maybe_result = 65 constructor->GetHeap()->AllocateJSObject(constructor); 66 if (!maybe_result->ToObject(&result)) return maybe_result; 67 } 68 JSValue::cast(result)->set_value(value); 69 return result; 70 } 71 72 73 MaybeObject* Object::ToObject(Context* native_context) { 74 if (IsNumber()) { 75 return CreateJSValue(native_context->number_function(), this); 76 } else if (IsBoolean()) { 77 return CreateJSValue(native_context->boolean_function(), this); 78 } else if (IsString()) { 79 return CreateJSValue(native_context->string_function(), this); 80 } 81 ASSERT(IsJSObject()); 82 return this; 83 } 84 85 86 MaybeObject* Object::ToObject() { 87 if (IsJSReceiver()) { 88 return this; 89 } else if (IsNumber()) { 90 Isolate* isolate = Isolate::Current(); 91 Context* native_context = isolate->context()->native_context(); 92 return CreateJSValue(native_context->number_function(), this); 93 } else if (IsBoolean()) { 94 Isolate* isolate = HeapObject::cast(this)->GetIsolate(); 95 Context* native_context = isolate->context()->native_context(); 96 return CreateJSValue(native_context->boolean_function(), this); 97 } else if (IsString()) { 98 Isolate* isolate = HeapObject::cast(this)->GetIsolate(); 99 Context* native_context = isolate->context()->native_context(); 100 return CreateJSValue(native_context->string_function(), this); 101 } else if (IsSymbol()) { 102 Isolate* isolate = HeapObject::cast(this)->GetIsolate(); 103 Context* native_context = isolate->context()->native_context(); 104 return CreateJSValue(native_context->symbol_function(), this); 105 } 106 107 // Throw a type error. 108 return Failure::InternalError(); 109 } 110 111 112 bool Object::BooleanValue() { 113 if (IsBoolean()) return IsTrue(); 114 if (IsSmi()) return Smi::cast(this)->value() != 0; 115 if (IsUndefined() || IsNull()) return false; 116 if (IsUndetectableObject()) return false; // Undetectable object is false. 117 if (IsString()) return String::cast(this)->length() != 0; 118 if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue(); 119 return true; 120 } 121 122 123 void Object::Lookup(Name* name, LookupResult* result) { 124 Object* holder = NULL; 125 if (IsJSReceiver()) { 126 holder = this; 127 } else { 128 Context* native_context = result->isolate()->context()->native_context(); 129 if (IsNumber()) { 130 holder = native_context->number_function()->instance_prototype(); 131 } else if (IsString()) { 132 holder = native_context->string_function()->instance_prototype(); 133 } else if (IsSymbol()) { 134 holder = native_context->symbol_function()->instance_prototype(); 135 } else if (IsBoolean()) { 136 holder = native_context->boolean_function()->instance_prototype(); 137 } else { 138 Isolate::Current()->PushStackTraceAndDie( 139 0xDEAD0000, this, JSReceiver::cast(this)->map(), 0xDEAD0001); 140 } 141 } 142 ASSERT(holder != NULL); // Cannot handle null or undefined. 143 JSReceiver::cast(holder)->Lookup(name, result); 144 } 145 146 147 MaybeObject* Object::GetPropertyWithReceiver(Object* receiver, 148 Name* name, 149 PropertyAttributes* attributes) { 150 LookupResult result(name->GetIsolate()); 151 Lookup(name, &result); 152 MaybeObject* value = GetProperty(receiver, &result, name, attributes); 153 ASSERT(*attributes <= ABSENT); 154 return value; 155 } 156 157 158 bool Object::ToInt32(int32_t* value) { 159 if (IsSmi()) { 160 *value = Smi::cast(this)->value(); 161 return true; 162 } 163 if (IsHeapNumber()) { 164 double num = HeapNumber::cast(this)->value(); 165 if (FastI2D(FastD2I(num)) == num) { 166 *value = FastD2I(num); 167 return true; 168 } 169 } 170 return false; 171 } 172 173 174 bool Object::ToUint32(uint32_t* value) { 175 if (IsSmi()) { 176 int num = Smi::cast(this)->value(); 177 if (num >= 0) { 178 *value = static_cast<uint32_t>(num); 179 return true; 180 } 181 } 182 if (IsHeapNumber()) { 183 double num = HeapNumber::cast(this)->value(); 184 if (num >= 0 && FastUI2D(FastD2UI(num)) == num) { 185 *value = FastD2UI(num); 186 return true; 187 } 188 } 189 return false; 190 } 191 192 193 template<typename To> 194 static inline To* CheckedCast(void *from) { 195 uintptr_t temp = reinterpret_cast<uintptr_t>(from); 196 ASSERT(temp % sizeof(To) == 0); 197 return reinterpret_cast<To*>(temp); 198 } 199 200 201 static MaybeObject* PerformCompare(const BitmaskCompareDescriptor& descriptor, 202 char* ptr, 203 Heap* heap) { 204 uint32_t bitmask = descriptor.bitmask; 205 uint32_t compare_value = descriptor.compare_value; 206 uint32_t value; 207 switch (descriptor.size) { 208 case 1: 209 value = static_cast<uint32_t>(*CheckedCast<uint8_t>(ptr)); 210 compare_value &= 0xff; 211 bitmask &= 0xff; 212 break; 213 case 2: 214 value = static_cast<uint32_t>(*CheckedCast<uint16_t>(ptr)); 215 compare_value &= 0xffff; 216 bitmask &= 0xffff; 217 break; 218 case 4: 219 value = *CheckedCast<uint32_t>(ptr); 220 break; 221 default: 222 UNREACHABLE(); 223 return NULL; 224 } 225 return heap->ToBoolean((bitmask & value) == (bitmask & compare_value)); 226 } 227 228 229 static MaybeObject* PerformCompare(const PointerCompareDescriptor& descriptor, 230 char* ptr, 231 Heap* heap) { 232 uintptr_t compare_value = 233 reinterpret_cast<uintptr_t>(descriptor.compare_value); 234 uintptr_t value = *CheckedCast<uintptr_t>(ptr); 235 return heap->ToBoolean(compare_value == value); 236 } 237 238 239 static MaybeObject* GetPrimitiveValue( 240 const PrimitiveValueDescriptor& descriptor, 241 char* ptr, 242 Heap* heap) { 243 int32_t int32_value = 0; 244 switch (descriptor.data_type) { 245 case kDescriptorInt8Type: 246 int32_value = *CheckedCast<int8_t>(ptr); 247 break; 248 case kDescriptorUint8Type: 249 int32_value = *CheckedCast<uint8_t>(ptr); 250 break; 251 case kDescriptorInt16Type: 252 int32_value = *CheckedCast<int16_t>(ptr); 253 break; 254 case kDescriptorUint16Type: 255 int32_value = *CheckedCast<uint16_t>(ptr); 256 break; 257 case kDescriptorInt32Type: 258 int32_value = *CheckedCast<int32_t>(ptr); 259 break; 260 case kDescriptorUint32Type: { 261 uint32_t value = *CheckedCast<uint32_t>(ptr); 262 return heap->NumberFromUint32(value); 263 } 264 case kDescriptorBoolType: { 265 uint8_t byte = *CheckedCast<uint8_t>(ptr); 266 return heap->ToBoolean(byte & (0x1 << descriptor.bool_offset)); 267 } 268 case kDescriptorFloatType: { 269 float value = *CheckedCast<float>(ptr); 270 return heap->NumberFromDouble(value); 271 } 272 case kDescriptorDoubleType: { 273 double value = *CheckedCast<double>(ptr); 274 return heap->NumberFromDouble(value); 275 } 276 } 277 return heap->NumberFromInt32(int32_value); 278 } 279 280 281 static MaybeObject* GetDeclaredAccessorProperty(Object* receiver, 282 DeclaredAccessorInfo* info, 283 Isolate* isolate) { 284 char* current = reinterpret_cast<char*>(receiver); 285 DeclaredAccessorDescriptorIterator iterator(info->descriptor()); 286 while (true) { 287 const DeclaredAccessorDescriptorData* data = iterator.Next(); 288 switch (data->type) { 289 case kDescriptorReturnObject: { 290 ASSERT(iterator.Complete()); 291 current = *CheckedCast<char*>(current); 292 return *CheckedCast<Object*>(current); 293 } 294 case kDescriptorPointerDereference: 295 ASSERT(!iterator.Complete()); 296 current = *reinterpret_cast<char**>(current); 297 break; 298 case kDescriptorPointerShift: 299 ASSERT(!iterator.Complete()); 300 current += data->pointer_shift_descriptor.byte_offset; 301 break; 302 case kDescriptorObjectDereference: { 303 ASSERT(!iterator.Complete()); 304 Object* object = CheckedCast<Object>(current); 305 int field = data->object_dereference_descriptor.internal_field; 306 Object* smi = JSObject::cast(object)->GetInternalField(field); 307 ASSERT(smi->IsSmi()); 308 current = reinterpret_cast<char*>(smi); 309 break; 310 } 311 case kDescriptorBitmaskCompare: 312 ASSERT(iterator.Complete()); 313 return PerformCompare(data->bitmask_compare_descriptor, 314 current, 315 isolate->heap()); 316 case kDescriptorPointerCompare: 317 ASSERT(iterator.Complete()); 318 return PerformCompare(data->pointer_compare_descriptor, 319 current, 320 isolate->heap()); 321 case kDescriptorPrimitiveValue: 322 ASSERT(iterator.Complete()); 323 return GetPrimitiveValue(data->primitive_value_descriptor, 324 current, 325 isolate->heap()); 326 } 327 } 328 UNREACHABLE(); 329 return NULL; 330 } 331 332 333 MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, 334 Object* structure, 335 Name* name) { 336 Isolate* isolate = name->GetIsolate(); 337 // To accommodate both the old and the new api we switch on the 338 // data structure used to store the callbacks. Eventually foreign 339 // callbacks should be phased out. 340 if (structure->IsForeign()) { 341 AccessorDescriptor* callback = 342 reinterpret_cast<AccessorDescriptor*>( 343 Foreign::cast(structure)->foreign_address()); 344 MaybeObject* value = (callback->getter)(receiver, callback->data); 345 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 346 return value; 347 } 348 349 // api style callbacks. 350 if (structure->IsAccessorInfo()) { 351 if (!AccessorInfo::cast(structure)->IsCompatibleReceiver(receiver)) { 352 Handle<Object> name_handle(name, isolate); 353 Handle<Object> receiver_handle(receiver, isolate); 354 Handle<Object> args[2] = { name_handle, receiver_handle }; 355 Handle<Object> error = 356 isolate->factory()->NewTypeError("incompatible_method_receiver", 357 HandleVector(args, 358 ARRAY_SIZE(args))); 359 return isolate->Throw(*error); 360 } 361 // TODO(rossberg): Handling symbols in the API requires changing the API, 362 // so we do not support it for now. 363 if (name->IsSymbol()) return isolate->heap()->undefined_value(); 364 if (structure->IsDeclaredAccessorInfo()) { 365 return GetDeclaredAccessorProperty(receiver, 366 DeclaredAccessorInfo::cast(structure), 367 isolate); 368 } 369 ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(structure); 370 Object* fun_obj = data->getter(); 371 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); 372 if (call_fun == NULL) return isolate->heap()->undefined_value(); 373 HandleScope scope(isolate); 374 JSObject* self = JSObject::cast(receiver); 375 Handle<String> key(String::cast(name)); 376 LOG(isolate, ApiNamedPropertyAccess("load", self, name)); 377 PropertyCallbackArguments args(isolate, data->data(), self, this); 378 v8::Handle<v8::Value> result = 379 args.Call(call_fun, v8::Utils::ToLocal(key)); 380 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 381 if (result.IsEmpty()) { 382 return isolate->heap()->undefined_value(); 383 } 384 Object* return_value = *v8::Utils::OpenHandle(*result); 385 return_value->VerifyApiCallResultType(); 386 return return_value; 387 } 388 389 // __defineGetter__ callback 390 if (structure->IsAccessorPair()) { 391 Object* getter = AccessorPair::cast(structure)->getter(); 392 if (getter->IsSpecFunction()) { 393 // TODO(rossberg): nicer would be to cast to some JSCallable here... 394 return GetPropertyWithDefinedGetter(receiver, JSReceiver::cast(getter)); 395 } 396 // Getter is not a function. 397 return isolate->heap()->undefined_value(); 398 } 399 400 UNREACHABLE(); 401 return NULL; 402 } 403 404 405 MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw, 406 Name* name_raw) { 407 Isolate* isolate = GetIsolate(); 408 HandleScope scope(isolate); 409 Handle<Object> receiver(receiver_raw, isolate); 410 Handle<Object> name(name_raw, isolate); 411 412 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 413 if (name->IsSymbol()) return isolate->heap()->undefined_value(); 414 415 Handle<Object> args[] = { receiver, name }; 416 Handle<Object> result = CallTrap( 417 "get", isolate->derived_get_trap(), ARRAY_SIZE(args), args); 418 if (isolate->has_pending_exception()) return Failure::Exception(); 419 420 return *result; 421 } 422 423 424 Handle<Object> Object::GetProperty(Handle<Object> object, Handle<Name> name) { 425 // TODO(rossberg): The index test should not be here but in the GetProperty 426 // method (or somewhere else entirely). Needs more global clean-up. 427 uint32_t index; 428 if (name->AsArrayIndex(&index)) 429 return GetElement(object, index); 430 Isolate* isolate = object->IsHeapObject() 431 ? Handle<HeapObject>::cast(object)->GetIsolate() 432 : Isolate::Current(); 433 CALL_HEAP_FUNCTION(isolate, object->GetProperty(*name), Object); 434 } 435 436 437 Handle<Object> Object::GetElement(Handle<Object> object, uint32_t index) { 438 Isolate* isolate = object->IsHeapObject() 439 ? Handle<HeapObject>::cast(object)->GetIsolate() 440 : Isolate::Current(); 441 CALL_HEAP_FUNCTION(isolate, object->GetElement(index), Object); 442 } 443 444 445 MaybeObject* JSProxy::GetElementWithHandler(Object* receiver, 446 uint32_t index) { 447 String* name; 448 MaybeObject* maybe = GetHeap()->Uint32ToString(index); 449 if (!maybe->To<String>(&name)) return maybe; 450 return GetPropertyWithHandler(receiver, name); 451 } 452 453 454 MaybeObject* JSProxy::SetElementWithHandler(JSReceiver* receiver, 455 uint32_t index, 456 Object* value, 457 StrictModeFlag strict_mode) { 458 String* name; 459 MaybeObject* maybe = GetHeap()->Uint32ToString(index); 460 if (!maybe->To<String>(&name)) return maybe; 461 return SetPropertyWithHandler(receiver, name, value, NONE, strict_mode); 462 } 463 464 465 bool JSProxy::HasElementWithHandler(uint32_t index) { 466 String* name; 467 MaybeObject* maybe = GetHeap()->Uint32ToString(index); 468 if (!maybe->To<String>(&name)) return maybe; 469 return HasPropertyWithHandler(name); 470 } 471 472 473 MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver, 474 JSReceiver* getter) { 475 Isolate* isolate = getter->GetIsolate(); 476 HandleScope scope(isolate); 477 Handle<JSReceiver> fun(getter); 478 Handle<Object> self(receiver, isolate); 479 #ifdef ENABLE_DEBUGGER_SUPPORT 480 Debug* debug = isolate->debug(); 481 // Handle stepping into a getter if step into is active. 482 // TODO(rossberg): should this apply to getters that are function proxies? 483 if (debug->StepInActive() && fun->IsJSFunction()) { 484 debug->HandleStepIn( 485 Handle<JSFunction>::cast(fun), Handle<Object>::null(), 0, false); 486 } 487 #endif 488 489 bool has_pending_exception; 490 Handle<Object> result = 491 Execution::Call(fun, self, 0, NULL, &has_pending_exception, true); 492 // Check for pending exception and return the result. 493 if (has_pending_exception) return Failure::Exception(); 494 return *result; 495 } 496 497 498 // Only deal with CALLBACKS and INTERCEPTOR 499 MaybeObject* JSObject::GetPropertyWithFailedAccessCheck( 500 Object* receiver, 501 LookupResult* result, 502 Name* name, 503 PropertyAttributes* attributes) { 504 if (result->IsProperty()) { 505 switch (result->type()) { 506 case CALLBACKS: { 507 // Only allow API accessors. 508 Object* obj = result->GetCallbackObject(); 509 if (obj->IsAccessorInfo()) { 510 AccessorInfo* info = AccessorInfo::cast(obj); 511 if (info->all_can_read()) { 512 *attributes = result->GetAttributes(); 513 return result->holder()->GetPropertyWithCallback( 514 receiver, result->GetCallbackObject(), name); 515 } 516 } 517 break; 518 } 519 case NORMAL: 520 case FIELD: 521 case CONSTANT: { 522 // Search ALL_CAN_READ accessors in prototype chain. 523 LookupResult r(GetIsolate()); 524 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); 525 if (r.IsProperty()) { 526 return GetPropertyWithFailedAccessCheck(receiver, 527 &r, 528 name, 529 attributes); 530 } 531 break; 532 } 533 case INTERCEPTOR: { 534 // If the object has an interceptor, try real named properties. 535 // No access check in GetPropertyAttributeWithInterceptor. 536 LookupResult r(GetIsolate()); 537 result->holder()->LookupRealNamedProperty(name, &r); 538 if (r.IsProperty()) { 539 return GetPropertyWithFailedAccessCheck(receiver, 540 &r, 541 name, 542 attributes); 543 } 544 break; 545 } 546 default: 547 UNREACHABLE(); 548 } 549 } 550 551 // No accessible property found. 552 *attributes = ABSENT; 553 Heap* heap = name->GetHeap(); 554 Isolate* isolate = heap->isolate(); 555 isolate->ReportFailedAccessCheck(this, v8::ACCESS_GET); 556 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 557 return heap->undefined_value(); 558 } 559 560 561 PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( 562 Object* receiver, 563 LookupResult* result, 564 Name* name, 565 bool continue_search) { 566 if (result->IsProperty()) { 567 switch (result->type()) { 568 case CALLBACKS: { 569 // Only allow API accessors. 570 Object* obj = result->GetCallbackObject(); 571 if (obj->IsAccessorInfo()) { 572 AccessorInfo* info = AccessorInfo::cast(obj); 573 if (info->all_can_read()) { 574 return result->GetAttributes(); 575 } 576 } 577 break; 578 } 579 580 case NORMAL: 581 case FIELD: 582 case CONSTANT: { 583 if (!continue_search) break; 584 // Search ALL_CAN_READ accessors in prototype chain. 585 LookupResult r(GetIsolate()); 586 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); 587 if (r.IsProperty()) { 588 return GetPropertyAttributeWithFailedAccessCheck(receiver, 589 &r, 590 name, 591 continue_search); 592 } 593 break; 594 } 595 596 case INTERCEPTOR: { 597 // If the object has an interceptor, try real named properties. 598 // No access check in GetPropertyAttributeWithInterceptor. 599 LookupResult r(GetIsolate()); 600 if (continue_search) { 601 result->holder()->LookupRealNamedProperty(name, &r); 602 } else { 603 result->holder()->LocalLookupRealNamedProperty(name, &r); 604 } 605 if (!r.IsFound()) break; 606 return GetPropertyAttributeWithFailedAccessCheck(receiver, 607 &r, 608 name, 609 continue_search); 610 } 611 612 case HANDLER: 613 case TRANSITION: 614 case NONEXISTENT: 615 UNREACHABLE(); 616 } 617 } 618 619 GetIsolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 620 return ABSENT; 621 } 622 623 624 Object* JSObject::GetNormalizedProperty(LookupResult* result) { 625 ASSERT(!HasFastProperties()); 626 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry()); 627 if (IsGlobalObject()) { 628 value = PropertyCell::cast(value)->value(); 629 } 630 ASSERT(!value->IsPropertyCell() && !value->IsCell()); 631 return value; 632 } 633 634 635 Handle<Object> JSObject::SetNormalizedProperty(Handle<JSObject> object, 636 LookupResult* result, 637 Handle<Object> value) { 638 CALL_HEAP_FUNCTION(object->GetIsolate(), 639 object->SetNormalizedProperty(result, *value), 640 Object); 641 } 642 643 644 MaybeObject* JSObject::SetNormalizedProperty(LookupResult* result, 645 Object* value) { 646 ASSERT(!HasFastProperties()); 647 if (IsGlobalObject()) { 648 PropertyCell* cell = PropertyCell::cast( 649 property_dictionary()->ValueAt(result->GetDictionaryEntry())); 650 MaybeObject* maybe_type = cell->SetValueInferType(value); 651 if (maybe_type->IsFailure()) return maybe_type; 652 } else { 653 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value); 654 } 655 return value; 656 } 657 658 659 Handle<Object> JSObject::SetNormalizedProperty(Handle<JSObject> object, 660 Handle<Name> key, 661 Handle<Object> value, 662 PropertyDetails details) { 663 CALL_HEAP_FUNCTION(object->GetIsolate(), 664 object->SetNormalizedProperty(*key, *value, details), 665 Object); 666 } 667 668 669 MaybeObject* JSObject::SetNormalizedProperty(Name* name, 670 Object* value, 671 PropertyDetails details) { 672 ASSERT(!HasFastProperties()); 673 int entry = property_dictionary()->FindEntry(name); 674 if (entry == NameDictionary::kNotFound) { 675 Object* store_value = value; 676 if (IsGlobalObject()) { 677 Heap* heap = name->GetHeap(); 678 MaybeObject* maybe_store_value = heap->AllocatePropertyCell(value); 679 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value; 680 } 681 Object* dict; 682 { MaybeObject* maybe_dict = 683 property_dictionary()->Add(name, store_value, details); 684 if (!maybe_dict->ToObject(&dict)) return maybe_dict; 685 } 686 set_properties(NameDictionary::cast(dict)); 687 return value; 688 } 689 690 PropertyDetails original_details = property_dictionary()->DetailsAt(entry); 691 int enumeration_index; 692 // Preserve the enumeration index unless the property was deleted. 693 if (original_details.IsDeleted()) { 694 enumeration_index = property_dictionary()->NextEnumerationIndex(); 695 property_dictionary()->SetNextEnumerationIndex(enumeration_index + 1); 696 } else { 697 enumeration_index = original_details.dictionary_index(); 698 ASSERT(enumeration_index > 0); 699 } 700 701 details = PropertyDetails( 702 details.attributes(), details.type(), enumeration_index); 703 704 if (IsGlobalObject()) { 705 PropertyCell* cell = 706 PropertyCell::cast(property_dictionary()->ValueAt(entry)); 707 MaybeObject* maybe_type = cell->SetValueInferType(value); 708 if (maybe_type->IsFailure()) return maybe_type; 709 // Please note we have to update the property details. 710 property_dictionary()->DetailsAtPut(entry, details); 711 } else { 712 property_dictionary()->SetEntry(entry, name, value, details); 713 } 714 return value; 715 } 716 717 718 // TODO(mstarzinger): Temporary wrapper until target is handlified. 719 Handle<NameDictionary> NameDictionaryShrink(Handle<NameDictionary> dict, 720 Handle<Name> name) { 721 CALL_HEAP_FUNCTION(dict->GetIsolate(), dict->Shrink(*name), NameDictionary); 722 } 723 724 725 static void CellSetValueInferType(Handle<PropertyCell> cell, 726 Handle<Object> value) { 727 CALL_HEAP_FUNCTION_VOID(cell->GetIsolate(), cell->SetValueInferType(*value)); 728 } 729 730 731 Handle<Object> JSObject::DeleteNormalizedProperty(Handle<JSObject> object, 732 Handle<Name> name, 733 DeleteMode mode) { 734 ASSERT(!object->HasFastProperties()); 735 Isolate* isolate = object->GetIsolate(); 736 Handle<NameDictionary> dictionary(object->property_dictionary()); 737 int entry = dictionary->FindEntry(*name); 738 if (entry != NameDictionary::kNotFound) { 739 // If we have a global object set the cell to the hole. 740 if (object->IsGlobalObject()) { 741 PropertyDetails details = dictionary->DetailsAt(entry); 742 if (details.IsDontDelete()) { 743 if (mode != FORCE_DELETION) return isolate->factory()->false_value(); 744 // When forced to delete global properties, we have to make a 745 // map change to invalidate any ICs that think they can load 746 // from the DontDelete cell without checking if it contains 747 // the hole value. 748 Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); 749 ASSERT(new_map->is_dictionary_map()); 750 object->set_map(*new_map); 751 } 752 Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); 753 CellSetValueInferType(cell, isolate->factory()->the_hole_value()); 754 dictionary->DetailsAtPut(entry, details.AsDeleted()); 755 } else { 756 Handle<Object> deleted(dictionary->DeleteProperty(entry, mode), isolate); 757 if (*deleted == isolate->heap()->true_value()) { 758 Handle<NameDictionary> new_properties = 759 NameDictionaryShrink(dictionary, name); 760 object->set_properties(*new_properties); 761 } 762 return deleted; 763 } 764 } 765 return isolate->factory()->true_value(); 766 } 767 768 769 bool JSObject::IsDirty() { 770 Object* cons_obj = map()->constructor(); 771 if (!cons_obj->IsJSFunction()) 772 return true; 773 JSFunction* fun = JSFunction::cast(cons_obj); 774 if (!fun->shared()->IsApiFunction()) 775 return true; 776 // If the object is fully fast case and has the same map it was 777 // created with then no changes can have been made to it. 778 return map() != fun->initial_map() 779 || !HasFastObjectElements() 780 || !HasFastProperties(); 781 } 782 783 784 Handle<Object> Object::GetProperty(Handle<Object> object, 785 Handle<Object> receiver, 786 LookupResult* result, 787 Handle<Name> key, 788 PropertyAttributes* attributes) { 789 Isolate* isolate = object->IsHeapObject() 790 ? Handle<HeapObject>::cast(object)->GetIsolate() 791 : Isolate::Current(); 792 CALL_HEAP_FUNCTION( 793 isolate, 794 object->GetProperty(*receiver, result, *key, attributes), 795 Object); 796 } 797 798 799 MaybeObject* Object::GetPropertyOrFail(Handle<Object> object, 800 Handle<Object> receiver, 801 LookupResult* result, 802 Handle<Name> key, 803 PropertyAttributes* attributes) { 804 Isolate* isolate = object->IsHeapObject() 805 ? Handle<HeapObject>::cast(object)->GetIsolate() 806 : Isolate::Current(); 807 CALL_HEAP_FUNCTION_PASS_EXCEPTION( 808 isolate, 809 object->GetProperty(*receiver, result, *key, attributes)); 810 } 811 812 813 MaybeObject* Object::GetProperty(Object* receiver, 814 LookupResult* result, 815 Name* name, 816 PropertyAttributes* attributes) { 817 // Make sure that the top context does not change when doing 818 // callbacks or interceptor calls. 819 AssertNoContextChange ncc; 820 Isolate* isolate = name->GetIsolate(); 821 Heap* heap = isolate->heap(); 822 823 // Traverse the prototype chain from the current object (this) to 824 // the holder and check for access rights. This avoids traversing the 825 // objects more than once in case of interceptors, because the 826 // holder will always be the interceptor holder and the search may 827 // only continue with a current object just after the interceptor 828 // holder in the prototype chain. 829 // Proxy handlers do not use the proxy's prototype, so we can skip this. 830 if (!result->IsHandler()) { 831 Object* last = result->IsProperty() 832 ? result->holder() 833 : Object::cast(heap->null_value()); 834 ASSERT(this != this->GetPrototype(isolate)); 835 for (Object* current = this; 836 true; 837 current = current->GetPrototype(isolate)) { 838 if (current->IsAccessCheckNeeded()) { 839 // Check if we're allowed to read from the current object. Note 840 // that even though we may not actually end up loading the named 841 // property from the current object, we still check that we have 842 // access to it. 843 JSObject* checked = JSObject::cast(current); 844 if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) { 845 return checked->GetPropertyWithFailedAccessCheck(receiver, 846 result, 847 name, 848 attributes); 849 } 850 } 851 // Stop traversing the chain once we reach the last object in the 852 // chain; either the holder of the result or null in case of an 853 // absent property. 854 if (current == last) break; 855 } 856 } 857 858 if (!result->IsProperty()) { 859 *attributes = ABSENT; 860 return heap->undefined_value(); 861 } 862 *attributes = result->GetAttributes(); 863 Object* value; 864 switch (result->type()) { 865 case NORMAL: 866 value = result->holder()->GetNormalizedProperty(result); 867 ASSERT(!value->IsTheHole() || result->IsReadOnly()); 868 return value->IsTheHole() ? heap->undefined_value() : value; 869 case FIELD: { 870 MaybeObject* maybe_result = result->holder()->FastPropertyAt( 871 result->representation(), 872 result->GetFieldIndex().field_index()); 873 if (!maybe_result->To(&value)) return maybe_result; 874 ASSERT(!value->IsTheHole() || result->IsReadOnly()); 875 return value->IsTheHole() ? heap->undefined_value() : value; 876 } 877 case CONSTANT: 878 return result->GetConstant(); 879 case CALLBACKS: 880 return result->holder()->GetPropertyWithCallback( 881 receiver, result->GetCallbackObject(), name); 882 case HANDLER: 883 return result->proxy()->GetPropertyWithHandler(receiver, name); 884 case INTERCEPTOR: 885 return result->holder()->GetPropertyWithInterceptor( 886 receiver, name, attributes); 887 case TRANSITION: 888 case NONEXISTENT: 889 UNREACHABLE(); 890 break; 891 } 892 UNREACHABLE(); 893 return NULL; 894 } 895 896 897 MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) { 898 Isolate* isolate = IsSmi() 899 ? Isolate::Current() 900 : HeapObject::cast(this)->GetIsolate(); 901 Heap* heap = isolate->heap(); 902 Object* holder = this; 903 904 // Iterate up the prototype chain until an element is found or the null 905 // prototype is encountered. 906 for (holder = this; 907 holder != heap->null_value(); 908 holder = holder->GetPrototype(isolate)) { 909 if (!holder->IsJSObject()) { 910 Context* native_context = isolate->context()->native_context(); 911 if (holder->IsNumber()) { 912 holder = native_context->number_function()->instance_prototype(); 913 } else if (holder->IsString()) { 914 holder = native_context->string_function()->instance_prototype(); 915 } else if (holder->IsSymbol()) { 916 holder = native_context->symbol_function()->instance_prototype(); 917 } else if (holder->IsBoolean()) { 918 holder = native_context->boolean_function()->instance_prototype(); 919 } else if (holder->IsJSProxy()) { 920 return JSProxy::cast(holder)->GetElementWithHandler(receiver, index); 921 } else { 922 // Undefined and null have no indexed properties. 923 ASSERT(holder->IsUndefined() || holder->IsNull()); 924 return heap->undefined_value(); 925 } 926 } 927 928 // Inline the case for JSObjects. Doing so significantly improves the 929 // performance of fetching elements where checking the prototype chain is 930 // necessary. 931 JSObject* js_object = JSObject::cast(holder); 932 933 // Check access rights if needed. 934 if (js_object->IsAccessCheckNeeded()) { 935 Isolate* isolate = heap->isolate(); 936 if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) { 937 isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_GET); 938 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 939 return heap->undefined_value(); 940 } 941 } 942 943 if (js_object->HasIndexedInterceptor()) { 944 return js_object->GetElementWithInterceptor(receiver, index); 945 } 946 947 if (js_object->elements() != heap->empty_fixed_array()) { 948 MaybeObject* result = js_object->GetElementsAccessor()->Get( 949 receiver, js_object, index); 950 if (result != heap->the_hole_value()) return result; 951 } 952 } 953 954 return heap->undefined_value(); 955 } 956 957 958 Object* Object::GetPrototype(Isolate* isolate) { 959 if (IsSmi()) { 960 Context* context = isolate->context()->native_context(); 961 return context->number_function()->instance_prototype(); 962 } 963 964 HeapObject* heap_object = HeapObject::cast(this); 965 966 // The object is either a number, a string, a boolean, 967 // a real JS object, or a Harmony proxy. 968 if (heap_object->IsJSReceiver()) { 969 return heap_object->map()->prototype(); 970 } 971 Context* context = isolate->context()->native_context(); 972 973 if (heap_object->IsHeapNumber()) { 974 return context->number_function()->instance_prototype(); 975 } 976 if (heap_object->IsString()) { 977 return context->string_function()->instance_prototype(); 978 } 979 if (heap_object->IsSymbol()) { 980 return context->symbol_function()->instance_prototype(); 981 } 982 if (heap_object->IsBoolean()) { 983 return context->boolean_function()->instance_prototype(); 984 } else { 985 return isolate->heap()->null_value(); 986 } 987 } 988 989 990 MaybeObject* Object::GetHash(CreationFlag flag) { 991 // The object is either a number, a name, an odd-ball, 992 // a real JS object, or a Harmony proxy. 993 if (IsNumber()) { 994 uint32_t hash = ComputeLongHash(double_to_uint64(Number())); 995 return Smi::FromInt(hash & Smi::kMaxValue); 996 } 997 if (IsName()) { 998 uint32_t hash = Name::cast(this)->Hash(); 999 return Smi::FromInt(hash); 1000 } 1001 if (IsOddball()) { 1002 uint32_t hash = Oddball::cast(this)->to_string()->Hash(); 1003 return Smi::FromInt(hash); 1004 } 1005 if (IsJSReceiver()) { 1006 return JSReceiver::cast(this)->GetIdentityHash(flag); 1007 } 1008 1009 UNREACHABLE(); 1010 return Smi::FromInt(0); 1011 } 1012 1013 1014 bool Object::SameValue(Object* other) { 1015 if (other == this) return true; 1016 1017 // The object is either a number, a name, an odd-ball, 1018 // a real JS object, or a Harmony proxy. 1019 if (IsNumber() && other->IsNumber()) { 1020 double this_value = Number(); 1021 double other_value = other->Number(); 1022 return (this_value == other_value) || 1023 (std::isnan(this_value) && std::isnan(other_value)); 1024 } 1025 if (IsString() && other->IsString()) { 1026 return String::cast(this)->Equals(String::cast(other)); 1027 } 1028 return false; 1029 } 1030 1031 1032 void Object::ShortPrint(FILE* out) { 1033 HeapStringAllocator allocator; 1034 StringStream accumulator(&allocator); 1035 ShortPrint(&accumulator); 1036 accumulator.OutputToFile(out); 1037 } 1038 1039 1040 void Object::ShortPrint(StringStream* accumulator) { 1041 if (IsSmi()) { 1042 Smi::cast(this)->SmiPrint(accumulator); 1043 } else if (IsFailure()) { 1044 Failure::cast(this)->FailurePrint(accumulator); 1045 } else { 1046 HeapObject::cast(this)->HeapObjectShortPrint(accumulator); 1047 } 1048 } 1049 1050 1051 void Smi::SmiPrint(FILE* out) { 1052 PrintF(out, "%d", value()); 1053 } 1054 1055 1056 void Smi::SmiPrint(StringStream* accumulator) { 1057 accumulator->Add("%d", value()); 1058 } 1059 1060 1061 void Failure::FailurePrint(StringStream* accumulator) { 1062 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value())); 1063 } 1064 1065 1066 void Failure::FailurePrint(FILE* out) { 1067 PrintF(out, "Failure(%p)", reinterpret_cast<void*>(value())); 1068 } 1069 1070 1071 // Should a word be prefixed by 'a' or 'an' in order to read naturally in 1072 // English? Returns false for non-ASCII or words that don't start with 1073 // a capital letter. The a/an rule follows pronunciation in English. 1074 // We don't use the BBC's overcorrect "an historic occasion" though if 1075 // you speak a dialect you may well say "an 'istoric occasion". 1076 static bool AnWord(String* str) { 1077 if (str->length() == 0) return false; // A nothing. 1078 int c0 = str->Get(0); 1079 int c1 = str->length() > 1 ? str->Get(1) : 0; 1080 if (c0 == 'U') { 1081 if (c1 > 'Z') { 1082 return true; // An Umpire, but a UTF8String, a U. 1083 } 1084 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') { 1085 return true; // An Ape, an ABCBook. 1086 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) && 1087 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' || 1088 c0 == 'S' || c0 == 'X')) { 1089 return true; // An MP3File, an M. 1090 } 1091 return false; 1092 } 1093 1094 1095 MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) { 1096 #ifdef DEBUG 1097 // Do not attempt to flatten in debug mode when allocation is not 1098 // allowed. This is to avoid an assertion failure when allocating. 1099 // Flattening strings is the only case where we always allow 1100 // allocation because no GC is performed if the allocation fails. 1101 if (!AllowHeapAllocation::IsAllowed()) return this; 1102 #endif 1103 1104 Heap* heap = GetHeap(); 1105 switch (StringShape(this).representation_tag()) { 1106 case kConsStringTag: { 1107 ConsString* cs = ConsString::cast(this); 1108 if (cs->second()->length() == 0) { 1109 return cs->first(); 1110 } 1111 // There's little point in putting the flat string in new space if the 1112 // cons string is in old space. It can never get GCed until there is 1113 // an old space GC. 1114 PretenureFlag tenure = heap->InNewSpace(this) ? pretenure : TENURED; 1115 int len = length(); 1116 Object* object; 1117 String* result; 1118 if (IsOneByteRepresentation()) { 1119 { MaybeObject* maybe_object = 1120 heap->AllocateRawOneByteString(len, tenure); 1121 if (!maybe_object->ToObject(&object)) return maybe_object; 1122 } 1123 result = String::cast(object); 1124 String* first = cs->first(); 1125 int first_length = first->length(); 1126 uint8_t* dest = SeqOneByteString::cast(result)->GetChars(); 1127 WriteToFlat(first, dest, 0, first_length); 1128 String* second = cs->second(); 1129 WriteToFlat(second, 1130 dest + first_length, 1131 0, 1132 len - first_length); 1133 } else { 1134 { MaybeObject* maybe_object = 1135 heap->AllocateRawTwoByteString(len, tenure); 1136 if (!maybe_object->ToObject(&object)) return maybe_object; 1137 } 1138 result = String::cast(object); 1139 uc16* dest = SeqTwoByteString::cast(result)->GetChars(); 1140 String* first = cs->first(); 1141 int first_length = first->length(); 1142 WriteToFlat(first, dest, 0, first_length); 1143 String* second = cs->second(); 1144 WriteToFlat(second, 1145 dest + first_length, 1146 0, 1147 len - first_length); 1148 } 1149 cs->set_first(result); 1150 cs->set_second(heap->empty_string(), SKIP_WRITE_BARRIER); 1151 return result; 1152 } 1153 default: 1154 return this; 1155 } 1156 } 1157 1158 1159 bool String::MakeExternal(v8::String::ExternalStringResource* resource) { 1160 // Externalizing twice leaks the external resource, so it's 1161 // prohibited by the API. 1162 ASSERT(!this->IsExternalString()); 1163 #ifdef DEBUG 1164 if (FLAG_enable_slow_asserts) { 1165 // Assert that the resource and the string are equivalent. 1166 ASSERT(static_cast<size_t>(this->length()) == resource->length()); 1167 ScopedVector<uc16> smart_chars(this->length()); 1168 String::WriteToFlat(this, smart_chars.start(), 0, this->length()); 1169 ASSERT(memcmp(smart_chars.start(), 1170 resource->data(), 1171 resource->length() * sizeof(smart_chars[0])) == 0); 1172 } 1173 #endif // DEBUG 1174 Heap* heap = GetHeap(); 1175 int size = this->Size(); // Byte size of the original string. 1176 if (size < ExternalString::kShortSize) { 1177 return false; 1178 } 1179 bool is_ascii = this->IsOneByteRepresentation(); 1180 bool is_internalized = this->IsInternalizedString(); 1181 1182 // Morph the object to an external string by adjusting the map and 1183 // reinitializing the fields. 1184 if (size >= ExternalString::kSize) { 1185 this->set_map_no_write_barrier( 1186 is_internalized 1187 ? (is_ascii 1188 ? heap->external_internalized_string_with_one_byte_data_map() 1189 : heap->external_internalized_string_map()) 1190 : (is_ascii 1191 ? heap->external_string_with_one_byte_data_map() 1192 : heap->external_string_map())); 1193 } else { 1194 this->set_map_no_write_barrier( 1195 is_internalized 1196 ? (is_ascii 1197 ? heap-> 1198 short_external_internalized_string_with_one_byte_data_map() 1199 : heap->short_external_internalized_string_map()) 1200 : (is_ascii 1201 ? heap->short_external_string_with_one_byte_data_map() 1202 : heap->short_external_string_map())); 1203 } 1204 ExternalTwoByteString* self = ExternalTwoByteString::cast(this); 1205 self->set_resource(resource); 1206 if (is_internalized) self->Hash(); // Force regeneration of the hash value. 1207 1208 // Fill the remainder of the string with dead wood. 1209 int new_size = this->Size(); // Byte size of the external String object. 1210 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); 1211 if (Marking::IsBlack(Marking::MarkBitFrom(this))) { 1212 MemoryChunk::IncrementLiveBytesFromMutator(this->address(), 1213 new_size - size); 1214 } 1215 return true; 1216 } 1217 1218 1219 bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { 1220 #ifdef DEBUG 1221 if (FLAG_enable_slow_asserts) { 1222 // Assert that the resource and the string are equivalent. 1223 ASSERT(static_cast<size_t>(this->length()) == resource->length()); 1224 if (this->IsTwoByteRepresentation()) { 1225 ScopedVector<uint16_t> smart_chars(this->length()); 1226 String::WriteToFlat(this, smart_chars.start(), 0, this->length()); 1227 ASSERT(String::IsOneByte(smart_chars.start(), this->length())); 1228 } 1229 ScopedVector<char> smart_chars(this->length()); 1230 String::WriteToFlat(this, smart_chars.start(), 0, this->length()); 1231 ASSERT(memcmp(smart_chars.start(), 1232 resource->data(), 1233 resource->length() * sizeof(smart_chars[0])) == 0); 1234 } 1235 #endif // DEBUG 1236 Heap* heap = GetHeap(); 1237 int size = this->Size(); // Byte size of the original string. 1238 if (size < ExternalString::kShortSize) { 1239 return false; 1240 } 1241 bool is_internalized = this->IsInternalizedString(); 1242 1243 // Morph the object to an external string by adjusting the map and 1244 // reinitializing the fields. Use short version if space is limited. 1245 if (size >= ExternalString::kSize) { 1246 this->set_map_no_write_barrier( 1247 is_internalized ? heap->external_ascii_internalized_string_map() 1248 : heap->external_ascii_string_map()); 1249 } else { 1250 this->set_map_no_write_barrier( 1251 is_internalized ? heap->short_external_ascii_internalized_string_map() 1252 : heap->short_external_ascii_string_map()); 1253 } 1254 ExternalAsciiString* self = ExternalAsciiString::cast(this); 1255 self->set_resource(resource); 1256 if (is_internalized) self->Hash(); // Force regeneration of the hash value. 1257 1258 // Fill the remainder of the string with dead wood. 1259 int new_size = this->Size(); // Byte size of the external String object. 1260 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); 1261 if (Marking::IsBlack(Marking::MarkBitFrom(this))) { 1262 MemoryChunk::IncrementLiveBytesFromMutator(this->address(), 1263 new_size - size); 1264 } 1265 return true; 1266 } 1267 1268 1269 void String::StringShortPrint(StringStream* accumulator) { 1270 int len = length(); 1271 if (len > kMaxShortPrintLength) { 1272 accumulator->Add("<Very long string[%u]>", len); 1273 return; 1274 } 1275 1276 if (!LooksValid()) { 1277 accumulator->Add("<Invalid String>"); 1278 return; 1279 } 1280 1281 ConsStringIteratorOp op; 1282 StringCharacterStream stream(this, &op); 1283 1284 bool truncated = false; 1285 if (len > kMaxShortPrintLength) { 1286 len = kMaxShortPrintLength; 1287 truncated = true; 1288 } 1289 bool ascii = true; 1290 for (int i = 0; i < len; i++) { 1291 uint16_t c = stream.GetNext(); 1292 1293 if (c < 32 || c >= 127) { 1294 ascii = false; 1295 } 1296 } 1297 stream.Reset(this); 1298 if (ascii) { 1299 accumulator->Add("<String[%u]: ", length()); 1300 for (int i = 0; i < len; i++) { 1301 accumulator->Put(static_cast<char>(stream.GetNext())); 1302 } 1303 accumulator->Put('>'); 1304 } else { 1305 // Backslash indicates that the string contains control 1306 // characters and that backslashes are therefore escaped. 1307 accumulator->Add("<String[%u]\\: ", length()); 1308 for (int i = 0; i < len; i++) { 1309 uint16_t c = stream.GetNext(); 1310 if (c == '\n') { 1311 accumulator->Add("\\n"); 1312 } else if (c == '\r') { 1313 accumulator->Add("\\r"); 1314 } else if (c == '\\') { 1315 accumulator->Add("\\\\"); 1316 } else if (c < 32 || c > 126) { 1317 accumulator->Add("\\x%02x", c); 1318 } else { 1319 accumulator->Put(static_cast<char>(c)); 1320 } 1321 } 1322 if (truncated) { 1323 accumulator->Put('.'); 1324 accumulator->Put('.'); 1325 accumulator->Put('.'); 1326 } 1327 accumulator->Put('>'); 1328 } 1329 return; 1330 } 1331 1332 1333 void JSObject::JSObjectShortPrint(StringStream* accumulator) { 1334 switch (map()->instance_type()) { 1335 case JS_ARRAY_TYPE: { 1336 double length = JSArray::cast(this)->length()->IsUndefined() 1337 ? 0 1338 : JSArray::cast(this)->length()->Number(); 1339 accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length)); 1340 break; 1341 } 1342 case JS_WEAK_MAP_TYPE: { 1343 accumulator->Add("<JS WeakMap>"); 1344 break; 1345 } 1346 case JS_WEAK_SET_TYPE: { 1347 accumulator->Add("<JS WeakSet>"); 1348 break; 1349 } 1350 case JS_REGEXP_TYPE: { 1351 accumulator->Add("<JS RegExp>"); 1352 break; 1353 } 1354 case JS_FUNCTION_TYPE: { 1355 JSFunction* function = JSFunction::cast(this); 1356 Object* fun_name = function->shared()->DebugName(); 1357 bool printed = false; 1358 if (fun_name->IsString()) { 1359 String* str = String::cast(fun_name); 1360 if (str->length() > 0) { 1361 accumulator->Add("<JS Function "); 1362 accumulator->Put(str); 1363 printed = true; 1364 } 1365 } 1366 if (!printed) { 1367 accumulator->Add("<JS Function"); 1368 } 1369 accumulator->Add(" (SharedFunctionInfo %p)", 1370 reinterpret_cast<void*>(function->shared())); 1371 accumulator->Put('>'); 1372 break; 1373 } 1374 case JS_GENERATOR_OBJECT_TYPE: { 1375 accumulator->Add("<JS Generator>"); 1376 break; 1377 } 1378 case JS_MODULE_TYPE: { 1379 accumulator->Add("<JS Module>"); 1380 break; 1381 } 1382 // All other JSObjects are rather similar to each other (JSObject, 1383 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). 1384 default: { 1385 Map* map_of_this = map(); 1386 Heap* heap = GetHeap(); 1387 Object* constructor = map_of_this->constructor(); 1388 bool printed = false; 1389 if (constructor->IsHeapObject() && 1390 !heap->Contains(HeapObject::cast(constructor))) { 1391 accumulator->Add("!!!INVALID CONSTRUCTOR!!!"); 1392 } else { 1393 bool global_object = IsJSGlobalProxy(); 1394 if (constructor->IsJSFunction()) { 1395 if (!heap->Contains(JSFunction::cast(constructor)->shared())) { 1396 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!"); 1397 } else { 1398 Object* constructor_name = 1399 JSFunction::cast(constructor)->shared()->name(); 1400 if (constructor_name->IsString()) { 1401 String* str = String::cast(constructor_name); 1402 if (str->length() > 0) { 1403 bool vowel = AnWord(str); 1404 accumulator->Add("<%sa%s ", 1405 global_object ? "Global Object: " : "", 1406 vowel ? "n" : ""); 1407 accumulator->Put(str); 1408 accumulator->Add(" with %smap %p", 1409 map_of_this->is_deprecated() ? "deprecated " : "", 1410 map_of_this); 1411 printed = true; 1412 } 1413 } 1414 } 1415 } 1416 if (!printed) { 1417 accumulator->Add("<JS %sObject", global_object ? "Global " : ""); 1418 } 1419 } 1420 if (IsJSValue()) { 1421 accumulator->Add(" value = "); 1422 JSValue::cast(this)->value()->ShortPrint(accumulator); 1423 } 1424 accumulator->Put('>'); 1425 break; 1426 } 1427 } 1428 } 1429 1430 1431 void JSObject::PrintElementsTransition( 1432 FILE* file, ElementsKind from_kind, FixedArrayBase* from_elements, 1433 ElementsKind to_kind, FixedArrayBase* to_elements) { 1434 if (from_kind != to_kind) { 1435 PrintF(file, "elements transition ["); 1436 PrintElementsKind(file, from_kind); 1437 PrintF(file, " -> "); 1438 PrintElementsKind(file, to_kind); 1439 PrintF(file, "] in "); 1440 JavaScriptFrame::PrintTop(GetIsolate(), file, false, true); 1441 PrintF(file, " for "); 1442 ShortPrint(file); 1443 PrintF(file, " from "); 1444 from_elements->ShortPrint(file); 1445 PrintF(file, " to "); 1446 to_elements->ShortPrint(file); 1447 PrintF(file, "\n"); 1448 } 1449 } 1450 1451 1452 void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { 1453 Heap* heap = GetHeap(); 1454 if (!heap->Contains(this)) { 1455 accumulator->Add("!!!INVALID POINTER!!!"); 1456 return; 1457 } 1458 if (!heap->Contains(map())) { 1459 accumulator->Add("!!!INVALID MAP!!!"); 1460 return; 1461 } 1462 1463 accumulator->Add("%p ", this); 1464 1465 if (IsString()) { 1466 String::cast(this)->StringShortPrint(accumulator); 1467 return; 1468 } 1469 if (IsJSObject()) { 1470 JSObject::cast(this)->JSObjectShortPrint(accumulator); 1471 return; 1472 } 1473 switch (map()->instance_type()) { 1474 case MAP_TYPE: 1475 accumulator->Add("<Map(elements=%u)>", Map::cast(this)->elements_kind()); 1476 break; 1477 case FIXED_ARRAY_TYPE: 1478 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length()); 1479 break; 1480 case FIXED_DOUBLE_ARRAY_TYPE: 1481 accumulator->Add("<FixedDoubleArray[%u]>", 1482 FixedDoubleArray::cast(this)->length()); 1483 break; 1484 case BYTE_ARRAY_TYPE: 1485 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length()); 1486 break; 1487 case FREE_SPACE_TYPE: 1488 accumulator->Add("<FreeSpace[%u]>", FreeSpace::cast(this)->Size()); 1489 break; 1490 case EXTERNAL_PIXEL_ARRAY_TYPE: 1491 accumulator->Add("<ExternalPixelArray[%u]>", 1492 ExternalPixelArray::cast(this)->length()); 1493 break; 1494 case EXTERNAL_BYTE_ARRAY_TYPE: 1495 accumulator->Add("<ExternalByteArray[%u]>", 1496 ExternalByteArray::cast(this)->length()); 1497 break; 1498 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: 1499 accumulator->Add("<ExternalUnsignedByteArray[%u]>", 1500 ExternalUnsignedByteArray::cast(this)->length()); 1501 break; 1502 case EXTERNAL_SHORT_ARRAY_TYPE: 1503 accumulator->Add("<ExternalShortArray[%u]>", 1504 ExternalShortArray::cast(this)->length()); 1505 break; 1506 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: 1507 accumulator->Add("<ExternalUnsignedShortArray[%u]>", 1508 ExternalUnsignedShortArray::cast(this)->length()); 1509 break; 1510 case EXTERNAL_INT_ARRAY_TYPE: 1511 accumulator->Add("<ExternalIntArray[%u]>", 1512 ExternalIntArray::cast(this)->length()); 1513 break; 1514 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: 1515 accumulator->Add("<ExternalUnsignedIntArray[%u]>", 1516 ExternalUnsignedIntArray::cast(this)->length()); 1517 break; 1518 case EXTERNAL_FLOAT_ARRAY_TYPE: 1519 accumulator->Add("<ExternalFloatArray[%u]>", 1520 ExternalFloatArray::cast(this)->length()); 1521 break; 1522 case EXTERNAL_DOUBLE_ARRAY_TYPE: 1523 accumulator->Add("<ExternalDoubleArray[%u]>", 1524 ExternalDoubleArray::cast(this)->length()); 1525 break; 1526 case SHARED_FUNCTION_INFO_TYPE: { 1527 SharedFunctionInfo* shared = SharedFunctionInfo::cast(this); 1528 SmartArrayPointer<char> debug_name = 1529 shared->DebugName()->ToCString(); 1530 if (debug_name[0] != 0) { 1531 accumulator->Add("<SharedFunctionInfo %s>", *debug_name); 1532 } else { 1533 accumulator->Add("<SharedFunctionInfo>"); 1534 } 1535 break; 1536 } 1537 case JS_MESSAGE_OBJECT_TYPE: 1538 accumulator->Add("<JSMessageObject>"); 1539 break; 1540 #define MAKE_STRUCT_CASE(NAME, Name, name) \ 1541 case NAME##_TYPE: \ 1542 accumulator->Put('<'); \ 1543 accumulator->Add(#Name); \ 1544 accumulator->Put('>'); \ 1545 break; 1546 STRUCT_LIST(MAKE_STRUCT_CASE) 1547 #undef MAKE_STRUCT_CASE 1548 case CODE_TYPE: 1549 accumulator->Add("<Code>"); 1550 break; 1551 case ODDBALL_TYPE: { 1552 if (IsUndefined()) 1553 accumulator->Add("<undefined>"); 1554 else if (IsTheHole()) 1555 accumulator->Add("<the hole>"); 1556 else if (IsNull()) 1557 accumulator->Add("<null>"); 1558 else if (IsTrue()) 1559 accumulator->Add("<true>"); 1560 else if (IsFalse()) 1561 accumulator->Add("<false>"); 1562 else 1563 accumulator->Add("<Odd Oddball>"); 1564 break; 1565 } 1566 case SYMBOL_TYPE: { 1567 Symbol* symbol = Symbol::cast(this); 1568 accumulator->Add("<Symbol: %d", symbol->Hash()); 1569 if (!symbol->name()->IsUndefined()) { 1570 accumulator->Add(" "); 1571 String::cast(symbol->name())->StringShortPrint(accumulator); 1572 } 1573 accumulator->Add(">"); 1574 break; 1575 } 1576 case HEAP_NUMBER_TYPE: 1577 accumulator->Add("<Number: "); 1578 HeapNumber::cast(this)->HeapNumberPrint(accumulator); 1579 accumulator->Put('>'); 1580 break; 1581 case JS_PROXY_TYPE: 1582 accumulator->Add("<JSProxy>"); 1583 break; 1584 case JS_FUNCTION_PROXY_TYPE: 1585 accumulator->Add("<JSFunctionProxy>"); 1586 break; 1587 case FOREIGN_TYPE: 1588 accumulator->Add("<Foreign>"); 1589 break; 1590 case CELL_TYPE: 1591 accumulator->Add("Cell for "); 1592 Cell::cast(this)->value()->ShortPrint(accumulator); 1593 break; 1594 case PROPERTY_CELL_TYPE: 1595 accumulator->Add("PropertyCell for "); 1596 PropertyCell::cast(this)->value()->ShortPrint(accumulator); 1597 break; 1598 default: 1599 accumulator->Add("<Other heap object (%d)>", map()->instance_type()); 1600 break; 1601 } 1602 } 1603 1604 1605 void HeapObject::Iterate(ObjectVisitor* v) { 1606 // Handle header 1607 IteratePointer(v, kMapOffset); 1608 // Handle object body 1609 Map* m = map(); 1610 IterateBody(m->instance_type(), SizeFromMap(m), v); 1611 } 1612 1613 1614 void HeapObject::IterateBody(InstanceType type, int object_size, 1615 ObjectVisitor* v) { 1616 // Avoiding <Type>::cast(this) because it accesses the map pointer field. 1617 // During GC, the map pointer field is encoded. 1618 if (type < FIRST_NONSTRING_TYPE) { 1619 switch (type & kStringRepresentationMask) { 1620 case kSeqStringTag: 1621 break; 1622 case kConsStringTag: 1623 ConsString::BodyDescriptor::IterateBody(this, v); 1624 break; 1625 case kSlicedStringTag: 1626 SlicedString::BodyDescriptor::IterateBody(this, v); 1627 break; 1628 case kExternalStringTag: 1629 if ((type & kStringEncodingMask) == kOneByteStringTag) { 1630 reinterpret_cast<ExternalAsciiString*>(this)-> 1631 ExternalAsciiStringIterateBody(v); 1632 } else { 1633 reinterpret_cast<ExternalTwoByteString*>(this)-> 1634 ExternalTwoByteStringIterateBody(v); 1635 } 1636 break; 1637 } 1638 return; 1639 } 1640 1641 switch (type) { 1642 case FIXED_ARRAY_TYPE: 1643 FixedArray::BodyDescriptor::IterateBody(this, object_size, v); 1644 break; 1645 case FIXED_DOUBLE_ARRAY_TYPE: 1646 break; 1647 case JS_OBJECT_TYPE: 1648 case JS_CONTEXT_EXTENSION_OBJECT_TYPE: 1649 case JS_GENERATOR_OBJECT_TYPE: 1650 case JS_MODULE_TYPE: 1651 case JS_VALUE_TYPE: 1652 case JS_DATE_TYPE: 1653 case JS_ARRAY_TYPE: 1654 case JS_ARRAY_BUFFER_TYPE: 1655 case JS_TYPED_ARRAY_TYPE: 1656 case JS_DATA_VIEW_TYPE: 1657 case JS_SET_TYPE: 1658 case JS_MAP_TYPE: 1659 case JS_WEAK_MAP_TYPE: 1660 case JS_WEAK_SET_TYPE: 1661 case JS_REGEXP_TYPE: 1662 case JS_GLOBAL_PROXY_TYPE: 1663 case JS_GLOBAL_OBJECT_TYPE: 1664 case JS_BUILTINS_OBJECT_TYPE: 1665 case JS_MESSAGE_OBJECT_TYPE: 1666 JSObject::BodyDescriptor::IterateBody(this, object_size, v); 1667 break; 1668 case JS_FUNCTION_TYPE: 1669 reinterpret_cast<JSFunction*>(this) 1670 ->JSFunctionIterateBody(object_size, v); 1671 break; 1672 case ODDBALL_TYPE: 1673 Oddball::BodyDescriptor::IterateBody(this, v); 1674 break; 1675 case JS_PROXY_TYPE: 1676 JSProxy::BodyDescriptor::IterateBody(this, v); 1677 break; 1678 case JS_FUNCTION_PROXY_TYPE: 1679 JSFunctionProxy::BodyDescriptor::IterateBody(this, v); 1680 break; 1681 case FOREIGN_TYPE: 1682 reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v); 1683 break; 1684 case MAP_TYPE: 1685 Map::BodyDescriptor::IterateBody(this, v); 1686 break; 1687 case CODE_TYPE: 1688 reinterpret_cast<Code*>(this)->CodeIterateBody(v); 1689 break; 1690 case CELL_TYPE: 1691 Cell::BodyDescriptor::IterateBody(this, v); 1692 break; 1693 case PROPERTY_CELL_TYPE: 1694 PropertyCell::BodyDescriptor::IterateBody(this, v); 1695 break; 1696 case SYMBOL_TYPE: 1697 Symbol::BodyDescriptor::IterateBody(this, v); 1698 break; 1699 case HEAP_NUMBER_TYPE: 1700 case FILLER_TYPE: 1701 case BYTE_ARRAY_TYPE: 1702 case FREE_SPACE_TYPE: 1703 case EXTERNAL_PIXEL_ARRAY_TYPE: 1704 case EXTERNAL_BYTE_ARRAY_TYPE: 1705 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: 1706 case EXTERNAL_SHORT_ARRAY_TYPE: 1707 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: 1708 case EXTERNAL_INT_ARRAY_TYPE: 1709 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: 1710 case EXTERNAL_FLOAT_ARRAY_TYPE: 1711 case EXTERNAL_DOUBLE_ARRAY_TYPE: 1712 break; 1713 case SHARED_FUNCTION_INFO_TYPE: { 1714 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v); 1715 break; 1716 } 1717 1718 #define MAKE_STRUCT_CASE(NAME, Name, name) \ 1719 case NAME##_TYPE: 1720 STRUCT_LIST(MAKE_STRUCT_CASE) 1721 #undef MAKE_STRUCT_CASE 1722 if (type == ALLOCATION_SITE_TYPE) { 1723 AllocationSite::BodyDescriptor::IterateBody(this, v); 1724 } else { 1725 StructBodyDescriptor::IterateBody(this, object_size, v); 1726 } 1727 break; 1728 default: 1729 PrintF("Unknown type: %d\n", type); 1730 UNREACHABLE(); 1731 } 1732 } 1733 1734 1735 bool HeapNumber::HeapNumberBooleanValue() { 1736 // NaN, +0, and -0 should return the false object 1737 #if __BYTE_ORDER == __LITTLE_ENDIAN 1738 union IeeeDoubleLittleEndianArchType u; 1739 #elif __BYTE_ORDER == __BIG_ENDIAN 1740 union IeeeDoubleBigEndianArchType u; 1741 #endif 1742 u.d = value(); 1743 if (u.bits.exp == 2047) { 1744 // Detect NaN for IEEE double precision floating point. 1745 if ((u.bits.man_low | u.bits.man_high) != 0) return false; 1746 } 1747 if (u.bits.exp == 0) { 1748 // Detect +0, and -0 for IEEE double precision floating point. 1749 if ((u.bits.man_low | u.bits.man_high) == 0) return false; 1750 } 1751 return true; 1752 } 1753 1754 1755 void HeapNumber::HeapNumberPrint(FILE* out) { 1756 PrintF(out, "%.16g", Number()); 1757 } 1758 1759 1760 void HeapNumber::HeapNumberPrint(StringStream* accumulator) { 1761 // The Windows version of vsnprintf can allocate when printing a %g string 1762 // into a buffer that may not be big enough. We don't want random memory 1763 // allocation when producing post-crash stack traces, so we print into a 1764 // buffer that is plenty big enough for any floating point number, then 1765 // print that using vsnprintf (which may truncate but never allocate if 1766 // there is no more space in the buffer). 1767 EmbeddedVector<char, 100> buffer; 1768 OS::SNPrintF(buffer, "%.16g", Number()); 1769 accumulator->Add("%s", buffer.start()); 1770 } 1771 1772 1773 String* JSReceiver::class_name() { 1774 if (IsJSFunction() && IsJSFunctionProxy()) { 1775 return GetHeap()->function_class_string(); 1776 } 1777 if (map()->constructor()->IsJSFunction()) { 1778 JSFunction* constructor = JSFunction::cast(map()->constructor()); 1779 return String::cast(constructor->shared()->instance_class_name()); 1780 } 1781 // If the constructor is not present, return "Object". 1782 return GetHeap()->Object_string(); 1783 } 1784 1785 1786 String* JSReceiver::constructor_name() { 1787 if (map()->constructor()->IsJSFunction()) { 1788 JSFunction* constructor = JSFunction::cast(map()->constructor()); 1789 String* name = String::cast(constructor->shared()->name()); 1790 if (name->length() > 0) return name; 1791 String* inferred_name = constructor->shared()->inferred_name(); 1792 if (inferred_name->length() > 0) return inferred_name; 1793 Object* proto = GetPrototype(); 1794 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name(); 1795 } 1796 // TODO(rossberg): what about proxies? 1797 // If the constructor is not present, return "Object". 1798 return GetHeap()->Object_string(); 1799 } 1800 1801 1802 MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map, 1803 Name* name, 1804 Object* value, 1805 int field_index, 1806 Representation representation) { 1807 // This method is used to transition to a field. If we are transitioning to a 1808 // double field, allocate new storage. 1809 Object* storage; 1810 MaybeObject* maybe_storage = 1811 value->AllocateNewStorageFor(GetHeap(), representation); 1812 if (!maybe_storage->To(&storage)) return maybe_storage; 1813 1814 if (map()->unused_property_fields() == 0) { 1815 int new_unused = new_map->unused_property_fields(); 1816 FixedArray* values; 1817 MaybeObject* maybe_values = 1818 properties()->CopySize(properties()->length() + new_unused + 1); 1819 if (!maybe_values->To(&values)) return maybe_values; 1820 1821 set_properties(values); 1822 } 1823 1824 set_map(new_map); 1825 1826 FastPropertyAtPut(field_index, storage); 1827 return value; 1828 } 1829 1830 1831 static bool IsIdentifier(UnicodeCache* cache, Name* name) { 1832 // Checks whether the buffer contains an identifier (no escape). 1833 if (!name->IsString()) return false; 1834 String* string = String::cast(name); 1835 if (string->length() == 0) return false; 1836 ConsStringIteratorOp op; 1837 StringCharacterStream stream(string, &op); 1838 if (!cache->IsIdentifierStart(stream.GetNext())) { 1839 return false; 1840 } 1841 while (stream.HasMore()) { 1842 if (!cache->IsIdentifierPart(stream.GetNext())) { 1843 return false; 1844 } 1845 } 1846 return true; 1847 } 1848 1849 1850 MaybeObject* JSObject::AddFastProperty(Name* name, 1851 Object* value, 1852 PropertyAttributes attributes, 1853 StoreFromKeyed store_mode, 1854 ValueType value_type) { 1855 ASSERT(!IsJSGlobalProxy()); 1856 ASSERT(DescriptorArray::kNotFound == 1857 map()->instance_descriptors()->Search( 1858 name, map()->NumberOfOwnDescriptors())); 1859 1860 // Normalize the object if the name is an actual name (not the 1861 // hidden strings) and is not a real identifier. 1862 // Normalize the object if it will have too many fast properties. 1863 Isolate* isolate = GetHeap()->isolate(); 1864 if ((!name->IsSymbol() && !IsIdentifier(isolate->unicode_cache(), name) 1865 && name != isolate->heap()->hidden_string()) || 1866 (map()->unused_property_fields() == 0 && 1867 TooManyFastProperties(properties()->length(), store_mode))) { 1868 Object* obj; 1869 MaybeObject* maybe_obj = 1870 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1871 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 1872 1873 return AddSlowProperty(name, value, attributes); 1874 } 1875 1876 // Compute the new index for new field. 1877 int index = map()->NextFreePropertyIndex(); 1878 1879 // Allocate new instance descriptors with (name, index) added 1880 if (IsJSContextExtensionObject()) value_type = FORCE_TAGGED; 1881 Representation representation = value->OptimalRepresentation(value_type); 1882 1883 FieldDescriptor new_field(name, index, attributes, representation); 1884 1885 ASSERT(index < map()->inobject_properties() || 1886 (index - map()->inobject_properties()) < properties()->length() || 1887 map()->unused_property_fields() == 0); 1888 1889 FixedArray* values = NULL; 1890 1891 // TODO(verwaest): Merge with AddFastPropertyUsingMap. 1892 if (map()->unused_property_fields() == 0) { 1893 // Make room for the new value 1894 MaybeObject* maybe_values = 1895 properties()->CopySize(properties()->length() + kFieldsAdded); 1896 if (!maybe_values->To(&values)) return maybe_values; 1897 } 1898 1899 TransitionFlag flag = INSERT_TRANSITION; 1900 1901 Heap* heap = isolate->heap(); 1902 1903 Object* storage; 1904 MaybeObject* maybe_storage = 1905 value->AllocateNewStorageFor(heap, representation); 1906 if (!maybe_storage->To(&storage)) return maybe_storage; 1907 1908 // Note that Map::CopyAddDescriptor has side-effects, the new map is already 1909 // inserted in the transition tree. No more allocations that might fail are 1910 // allowed after this point. 1911 Map* new_map; 1912 MaybeObject* maybe_new_map = map()->CopyAddDescriptor(&new_field, flag); 1913 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 1914 1915 if (map()->unused_property_fields() == 0) { 1916 ASSERT(values != NULL); 1917 set_properties(values); 1918 new_map->set_unused_property_fields(kFieldsAdded - 1); 1919 } else { 1920 new_map->set_unused_property_fields(map()->unused_property_fields() - 1); 1921 } 1922 1923 set_map(new_map); 1924 1925 FastPropertyAtPut(index, storage); 1926 return value; 1927 } 1928 1929 1930 MaybeObject* JSObject::AddConstantProperty( 1931 Name* name, 1932 Object* constant, 1933 PropertyAttributes attributes) { 1934 // Allocate new instance descriptors with (name, constant) added 1935 ConstantDescriptor d(name, constant, attributes); 1936 1937 TransitionFlag flag = 1938 // Do not add transitions to global objects. 1939 (IsGlobalObject() || 1940 // Don't add transitions to special properties with non-trivial 1941 // attributes. 1942 attributes != NONE) 1943 ? OMIT_TRANSITION 1944 : INSERT_TRANSITION; 1945 1946 Map* new_map; 1947 MaybeObject* maybe_new_map = map()->CopyAddDescriptor(&d, flag); 1948 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 1949 1950 set_map(new_map); 1951 return constant; 1952 } 1953 1954 1955 // Add property in slow mode 1956 MaybeObject* JSObject::AddSlowProperty(Name* name, 1957 Object* value, 1958 PropertyAttributes attributes) { 1959 ASSERT(!HasFastProperties()); 1960 NameDictionary* dict = property_dictionary(); 1961 Object* store_value = value; 1962 if (IsGlobalObject()) { 1963 // In case name is an orphaned property reuse the cell. 1964 int entry = dict->FindEntry(name); 1965 if (entry != NameDictionary::kNotFound) { 1966 store_value = dict->ValueAt(entry); 1967 MaybeObject* maybe_type = 1968 PropertyCell::cast(store_value)->SetValueInferType(value); 1969 if (maybe_type->IsFailure()) return maybe_type; 1970 // Assign an enumeration index to the property and update 1971 // SetNextEnumerationIndex. 1972 int index = dict->NextEnumerationIndex(); 1973 PropertyDetails details = PropertyDetails(attributes, NORMAL, index); 1974 dict->SetNextEnumerationIndex(index + 1); 1975 dict->SetEntry(entry, name, store_value, details); 1976 return value; 1977 } 1978 Heap* heap = GetHeap(); 1979 { MaybeObject* maybe_store_value = 1980 heap->AllocatePropertyCell(value); 1981 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value; 1982 } 1983 MaybeObject* maybe_type = 1984 PropertyCell::cast(store_value)->SetValueInferType(value); 1985 if (maybe_type->IsFailure()) return maybe_type; 1986 } 1987 PropertyDetails details = PropertyDetails(attributes, NORMAL, 0); 1988 Object* result; 1989 { MaybeObject* maybe_result = dict->Add(name, store_value, details); 1990 if (!maybe_result->ToObject(&result)) return maybe_result; 1991 } 1992 if (dict != result) set_properties(NameDictionary::cast(result)); 1993 return value; 1994 } 1995 1996 1997 MaybeObject* JSObject::AddProperty(Name* name, 1998 Object* value, 1999 PropertyAttributes attributes, 2000 StrictModeFlag strict_mode, 2001 JSReceiver::StoreFromKeyed store_mode, 2002 ExtensibilityCheck extensibility_check, 2003 ValueType value_type, 2004 StoreMode mode) { 2005 ASSERT(!IsJSGlobalProxy()); 2006 Map* map_of_this = map(); 2007 Heap* heap = GetHeap(); 2008 Isolate* isolate = heap->isolate(); 2009 MaybeObject* result; 2010 if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK && 2011 !map_of_this->is_extensible()) { 2012 if (strict_mode == kNonStrictMode) { 2013 return value; 2014 } else { 2015 Handle<Object> args[1] = {Handle<Name>(name)}; 2016 return isolate->Throw( 2017 *isolate->factory()->NewTypeError("object_not_extensible", 2018 HandleVector(args, 1))); 2019 } 2020 } 2021 2022 if (HasFastProperties()) { 2023 // Ensure the descriptor array does not get too big. 2024 if (map_of_this->NumberOfOwnDescriptors() < 2025 DescriptorArray::kMaxNumberOfDescriptors) { 2026 // TODO(verwaest): Support other constants. 2027 // if (mode == ALLOW_AS_CONSTANT && 2028 // !value->IsTheHole() && 2029 // !value->IsConsString()) { 2030 if (value->IsJSFunction()) { 2031 result = AddConstantProperty(name, value, attributes); 2032 } else { 2033 result = AddFastProperty( 2034 name, value, attributes, store_mode, value_type); 2035 } 2036 } else { 2037 // Normalize the object to prevent very large instance descriptors. 2038 // This eliminates unwanted N^2 allocation and lookup behavior. 2039 Object* obj; 2040 MaybeObject* maybe = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 2041 if (!maybe->To(&obj)) return maybe; 2042 result = AddSlowProperty(name, value, attributes); 2043 } 2044 } else { 2045 result = AddSlowProperty(name, value, attributes); 2046 } 2047 2048 Handle<Object> hresult; 2049 if (!result->ToHandle(&hresult, isolate)) return result; 2050 2051 if (FLAG_harmony_observation && map()->is_observed()) { 2052 EnqueueChangeRecord(handle(this, isolate), 2053 "new", 2054 handle(name, isolate), 2055 handle(heap->the_hole_value(), isolate)); 2056 } 2057 2058 return *hresult; 2059 } 2060 2061 2062 void JSObject::EnqueueChangeRecord(Handle<JSObject> object, 2063 const char* type_str, 2064 Handle<Name> name, 2065 Handle<Object> old_value) { 2066 Isolate* isolate = object->GetIsolate(); 2067 HandleScope scope(isolate); 2068 Handle<String> type = isolate->factory()->InternalizeUtf8String(type_str); 2069 if (object->IsJSGlobalObject()) { 2070 object = handle(JSGlobalObject::cast(*object)->global_receiver(), isolate); 2071 } 2072 Handle<Object> args[] = { type, object, name, old_value }; 2073 bool threw; 2074 Execution::Call(Handle<JSFunction>(isolate->observers_notify_change()), 2075 isolate->factory()->undefined_value(), 2076 old_value->IsTheHole() ? 3 : 4, args, 2077 &threw); 2078 ASSERT(!threw); 2079 } 2080 2081 2082 void JSObject::DeliverChangeRecords(Isolate* isolate) { 2083 ASSERT(isolate->observer_delivery_pending()); 2084 bool threw = false; 2085 Execution::Call( 2086 isolate->observers_deliver_changes(), 2087 isolate->factory()->undefined_value(), 2088 0, 2089 NULL, 2090 &threw); 2091 ASSERT(!threw); 2092 isolate->set_observer_delivery_pending(false); 2093 } 2094 2095 2096 MaybeObject* JSObject::SetPropertyPostInterceptor( 2097 Name* name, 2098 Object* value, 2099 PropertyAttributes attributes, 2100 StrictModeFlag strict_mode, 2101 ExtensibilityCheck extensibility_check, 2102 StoreMode mode) { 2103 // Check local property, ignore interceptor. 2104 LookupResult result(GetIsolate()); 2105 LocalLookupRealNamedProperty(name, &result); 2106 if (!result.IsFound()) map()->LookupTransition(this, name, &result); 2107 if (result.IsFound()) { 2108 // An existing property or a map transition was found. Use set property to 2109 // handle all these cases. 2110 return SetProperty(&result, name, value, attributes, strict_mode); 2111 } 2112 bool done = false; 2113 MaybeObject* result_object; 2114 result_object = 2115 SetPropertyViaPrototypes(name, value, attributes, strict_mode, &done); 2116 if (done) return result_object; 2117 // Add a new real property. 2118 return AddProperty(name, value, attributes, strict_mode, 2119 MAY_BE_STORE_FROM_KEYED, extensibility_check, 2120 OPTIMAL_REPRESENTATION, mode); 2121 } 2122 2123 2124 MaybeObject* JSObject::ReplaceSlowProperty(Name* name, 2125 Object* value, 2126 PropertyAttributes attributes) { 2127 NameDictionary* dictionary = property_dictionary(); 2128 int old_index = dictionary->FindEntry(name); 2129 int new_enumeration_index = 0; // 0 means "Use the next available index." 2130 if (old_index != -1) { 2131 // All calls to ReplaceSlowProperty have had all transitions removed. 2132 new_enumeration_index = dictionary->DetailsAt(old_index).dictionary_index(); 2133 } 2134 2135 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index); 2136 return SetNormalizedProperty(name, value, new_details); 2137 } 2138 2139 2140 MaybeObject* JSObject::ConvertTransitionToMapTransition( 2141 int transition_index, 2142 Name* name, 2143 Object* new_value, 2144 PropertyAttributes attributes) { 2145 Map* old_map = map(); 2146 Map* old_target = old_map->GetTransition(transition_index); 2147 Object* result; 2148 2149 MaybeObject* maybe_result = ConvertDescriptorToField( 2150 name, new_value, attributes, OMIT_TRANSITION_KEEP_REPRESENTATIONS); 2151 if (!maybe_result->To(&result)) return maybe_result; 2152 2153 if (!HasFastProperties()) return result; 2154 2155 // This method should only be used to convert existing transitions. 2156 Map* new_map = map(); 2157 2158 // TODO(verwaest): From here on we lose existing map transitions, causing 2159 // invalid back pointers. This will change once we can store multiple 2160 // transitions with the same key. 2161 bool owned_descriptors = old_map->owns_descriptors(); 2162 if (owned_descriptors || 2163 old_target->instance_descriptors() == old_map->instance_descriptors()) { 2164 // Since the conversion above generated a new fast map with an additional 2165 // property which can be shared as well, install this descriptor pointer 2166 // along the entire chain of smaller maps. 2167 Map* map; 2168 DescriptorArray* new_descriptors = new_map->instance_descriptors(); 2169 DescriptorArray* old_descriptors = old_map->instance_descriptors(); 2170 for (Object* current = old_map; 2171 !current->IsUndefined(); 2172 current = map->GetBackPointer()) { 2173 map = Map::cast(current); 2174 if (map->instance_descriptors() != old_descriptors) break; 2175 map->SetEnumLength(Map::kInvalidEnumCache); 2176 map->set_instance_descriptors(new_descriptors); 2177 } 2178 old_map->set_owns_descriptors(false); 2179 } 2180 2181 old_target->DeprecateTransitionTree(); 2182 2183 old_map->SetTransition(transition_index, new_map); 2184 new_map->SetBackPointer(old_map); 2185 return result; 2186 } 2187 2188 2189 MaybeObject* JSObject::ConvertDescriptorToField(Name* name, 2190 Object* new_value, 2191 PropertyAttributes attributes, 2192 TransitionFlag flag) { 2193 if (map()->unused_property_fields() == 0 && 2194 TooManyFastProperties(properties()->length(), MAY_BE_STORE_FROM_KEYED)) { 2195 Object* obj; 2196 MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 2197 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 2198 return ReplaceSlowProperty(name, new_value, attributes); 2199 } 2200 2201 Representation representation = IsJSContextExtensionObject() 2202 ? Representation::Tagged() : new_value->OptimalRepresentation(); 2203 int index = map()->NextFreePropertyIndex(); 2204 FieldDescriptor new_field(name, index, attributes, representation); 2205 2206 // Make a new map for the object. 2207 Map* new_map; 2208 MaybeObject* maybe_new_map = map()->CopyInsertDescriptor(&new_field, flag); 2209 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 2210 2211 // Make new properties array if necessary. 2212 FixedArray* new_properties = NULL; 2213 int new_unused_property_fields = map()->unused_property_fields() - 1; 2214 if (map()->unused_property_fields() == 0) { 2215 new_unused_property_fields = kFieldsAdded - 1; 2216 MaybeObject* maybe_new_properties = 2217 properties()->CopySize(properties()->length() + kFieldsAdded); 2218 if (!maybe_new_properties->To(&new_properties)) return maybe_new_properties; 2219 } 2220 2221 Heap* heap = GetHeap(); 2222 Object* storage; 2223 MaybeObject* maybe_storage = 2224 new_value->AllocateNewStorageFor(heap, representation); 2225 if (!maybe_storage->To(&storage)) return maybe_storage; 2226 2227 // Update pointers to commit changes. 2228 // Object points to the new map. 2229 new_map->set_unused_property_fields(new_unused_property_fields); 2230 set_map(new_map); 2231 if (new_properties != NULL) { 2232 set_properties(new_properties); 2233 } 2234 FastPropertyAtPut(index, new_value); 2235 return new_value; 2236 } 2237 2238 2239 const char* Representation::Mnemonic() const { 2240 switch (kind_) { 2241 case kNone: return "v"; 2242 case kTagged: return "t"; 2243 case kSmi: return "s"; 2244 case kDouble: return "d"; 2245 case kInteger32: return "i"; 2246 case kHeapObject: return "h"; 2247 case kExternal: return "x"; 2248 default: 2249 UNREACHABLE(); 2250 return NULL; 2251 } 2252 } 2253 2254 2255 enum RightTrimMode { FROM_GC, FROM_MUTATOR }; 2256 2257 2258 static void ZapEndOfFixedArray(Address new_end, int to_trim) { 2259 // If we are doing a big trim in old space then we zap the space. 2260 Object** zap = reinterpret_cast<Object**>(new_end); 2261 zap++; // Header of filler must be at least one word so skip that. 2262 for (int i = 1; i < to_trim; i++) { 2263 *zap++ = Smi::FromInt(0); 2264 } 2265 } 2266 2267 2268 template<RightTrimMode trim_mode> 2269 static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) { 2270 ASSERT(elms->map() != HEAP->fixed_cow_array_map()); 2271 // For now this trick is only applied to fixed arrays in new and paged space. 2272 ASSERT(!HEAP->lo_space()->Contains(elms)); 2273 2274 const int len = elms->length(); 2275 2276 ASSERT(to_trim < len); 2277 2278 Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim); 2279 2280 if (trim_mode != FROM_GC || Heap::ShouldZapGarbage()) { 2281 ZapEndOfFixedArray(new_end, to_trim); 2282 } 2283 2284 int size_delta = to_trim * kPointerSize; 2285 2286 // Technically in new space this write might be omitted (except for 2287 // debug mode which iterates through the heap), but to play safer 2288 // we still do it. 2289 heap->CreateFillerObjectAt(new_end, size_delta); 2290 2291 elms->set_length(len - to_trim); 2292 2293 // Maintain marking consistency for IncrementalMarking. 2294 if (Marking::IsBlack(Marking::MarkBitFrom(elms))) { 2295 if (trim_mode == FROM_GC) { 2296 MemoryChunk::IncrementLiveBytesFromGC(elms->address(), -size_delta); 2297 } else { 2298 MemoryChunk::IncrementLiveBytesFromMutator(elms->address(), -size_delta); 2299 } 2300 } 2301 } 2302 2303 2304 bool Map::InstancesNeedRewriting(Map* target, 2305 int target_number_of_fields, 2306 int target_inobject, 2307 int target_unused) { 2308 // If fields were added (or removed), rewrite the instance. 2309 int number_of_fields = NumberOfFields(); 2310 ASSERT(target_number_of_fields >= number_of_fields); 2311 if (target_number_of_fields != number_of_fields) return true; 2312 2313 if (FLAG_track_double_fields) { 2314 // If smi descriptors were replaced by double descriptors, rewrite. 2315 DescriptorArray* old_desc = instance_descriptors(); 2316 DescriptorArray* new_desc = target->instance_descriptors(); 2317 int limit = NumberOfOwnDescriptors(); 2318 for (int i = 0; i < limit; i++) { 2319 if (new_desc->GetDetails(i).representation().IsDouble() && 2320 !old_desc->GetDetails(i).representation().IsDouble()) { 2321 return true; 2322 } 2323 } 2324 } 2325 2326 // If no fields were added, and no inobject properties were removed, setting 2327 // the map is sufficient. 2328 if (target_inobject == inobject_properties()) return false; 2329 // In-object slack tracking may have reduced the object size of the new map. 2330 // In that case, succeed if all existing fields were inobject, and they still 2331 // fit within the new inobject size. 2332 ASSERT(target_inobject < inobject_properties()); 2333 if (target_number_of_fields <= target_inobject) { 2334 ASSERT(target_number_of_fields + target_unused == target_inobject); 2335 return false; 2336 } 2337 // Otherwise, properties will need to be moved to the backing store. 2338 return true; 2339 } 2340 2341 2342 // To migrate an instance to a map: 2343 // - First check whether the instance needs to be rewritten. If not, simply 2344 // change the map. 2345 // - Otherwise, allocate a fixed array large enough to hold all fields, in 2346 // addition to unused space. 2347 // - Copy all existing properties in, in the following order: backing store 2348 // properties, unused fields, inobject properties. 2349 // - If all allocation succeeded, commit the state atomically: 2350 // * Copy inobject properties from the backing store back into the object. 2351 // * Trim the difference in instance size of the object. This also cleanly 2352 // frees inobject properties that moved to the backing store. 2353 // * If there are properties left in the backing store, trim of the space used 2354 // to temporarily store the inobject properties. 2355 // * If there are properties left in the backing store, install the backing 2356 // store. 2357 MaybeObject* JSObject::MigrateToMap(Map* new_map) { 2358 Heap* heap = GetHeap(); 2359 Map* old_map = map(); 2360 int number_of_fields = new_map->NumberOfFields(); 2361 int inobject = new_map->inobject_properties(); 2362 int unused = new_map->unused_property_fields(); 2363 2364 // Nothing to do if no functions were converted to fields. 2365 if (!old_map->InstancesNeedRewriting( 2366 new_map, number_of_fields, inobject, unused)) { 2367 set_map(new_map); 2368 return this; 2369 } 2370 2371 int total_size = number_of_fields + unused; 2372 int external = total_size - inobject; 2373 FixedArray* array; 2374 MaybeObject* maybe_array = heap->AllocateFixedArray(total_size); 2375 if (!maybe_array->To(&array)) return maybe_array; 2376 2377 DescriptorArray* old_descriptors = old_map->instance_descriptors(); 2378 DescriptorArray* new_descriptors = new_map->instance_descriptors(); 2379 int descriptors = new_map->NumberOfOwnDescriptors(); 2380 2381 for (int i = 0; i < descriptors; i++) { 2382 PropertyDetails details = new_descriptors->GetDetails(i); 2383 if (details.type() != FIELD) continue; 2384 PropertyDetails old_details = old_descriptors->GetDetails(i); 2385 ASSERT(old_details.type() == CONSTANT || 2386 old_details.type() == FIELD); 2387 Object* value = old_details.type() == CONSTANT 2388 ? old_descriptors->GetValue(i) 2389 : RawFastPropertyAt(old_descriptors->GetFieldIndex(i)); 2390 if (FLAG_track_double_fields && 2391 !old_details.representation().IsDouble() && 2392 details.representation().IsDouble()) { 2393 if (old_details.representation().IsNone()) value = Smi::FromInt(0); 2394 // Objects must be allocated in the old object space, since the 2395 // overall number of HeapNumbers needed for the conversion might 2396 // exceed the capacity of new space, and we would fail repeatedly 2397 // trying to migrate the instance. 2398 MaybeObject* maybe_storage = 2399 value->AllocateNewStorageFor(heap, details.representation(), TENURED); 2400 if (!maybe_storage->To(&value)) return maybe_storage; 2401 } 2402 ASSERT(!(FLAG_track_double_fields && 2403 details.representation().IsDouble() && 2404 value->IsSmi())); 2405 int target_index = new_descriptors->GetFieldIndex(i) - inobject; 2406 if (target_index < 0) target_index += total_size; 2407 array->set(target_index, value); 2408 } 2409 2410 // From here on we cannot fail anymore. 2411 2412 // Copy (real) inobject properties. If necessary, stop at number_of_fields to 2413 // avoid overwriting |one_pointer_filler_map|. 2414 int limit = Min(inobject, number_of_fields); 2415 for (int i = 0; i < limit; i++) { 2416 FastPropertyAtPut(i, array->get(external + i)); 2417 } 2418 2419 // Create filler object past the new instance size. 2420 int new_instance_size = new_map->instance_size(); 2421 int instance_size_delta = old_map->instance_size() - new_instance_size; 2422 ASSERT(instance_size_delta >= 0); 2423 Address address = this->address() + new_instance_size; 2424 heap->CreateFillerObjectAt(address, instance_size_delta); 2425 2426 // If there are properties in the new backing store, trim it to the correct 2427 // size and install the backing store into the object. 2428 if (external > 0) { 2429 RightTrimFixedArray<FROM_MUTATOR>(heap, array, inobject); 2430 set_properties(array); 2431 } 2432 2433 set_map(new_map); 2434 2435 return this; 2436 } 2437 2438 2439 MaybeObject* JSObject::GeneralizeFieldRepresentation( 2440 int modify_index, 2441 Representation new_representation) { 2442 Map* new_map; 2443 MaybeObject* maybe_new_map = 2444 map()->GeneralizeRepresentation(modify_index, new_representation); 2445 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 2446 if (map() == new_map) return this; 2447 2448 return MigrateToMap(new_map); 2449 } 2450 2451 2452 int Map::NumberOfFields() { 2453 DescriptorArray* descriptors = instance_descriptors(); 2454 int result = 0; 2455 for (int i = 0; i < NumberOfOwnDescriptors(); i++) { 2456 if (descriptors->GetDetails(i).type() == FIELD) result++; 2457 } 2458 return result; 2459 } 2460 2461 2462 MaybeObject* Map::CopyGeneralizeAllRepresentations() { 2463 Map* new_map; 2464 MaybeObject* maybe_map = this->Copy(); 2465 if (!maybe_map->To(&new_map)) return maybe_map; 2466 2467 new_map->instance_descriptors()->InitializeRepresentations( 2468 Representation::Tagged()); 2469 if (FLAG_trace_generalization) { 2470 PrintF("failed generalization %p -> %p\n", 2471 static_cast<void*>(this), static_cast<void*>(new_map)); 2472 } 2473 return new_map; 2474 } 2475 2476 2477 void Map::DeprecateTransitionTree() { 2478 if (!FLAG_track_fields) return; 2479 if (is_deprecated()) return; 2480 if (HasTransitionArray()) { 2481 TransitionArray* transitions = this->transitions(); 2482 for (int i = 0; i < transitions->number_of_transitions(); i++) { 2483 transitions->GetTarget(i)->DeprecateTransitionTree(); 2484 } 2485 } 2486 deprecate(); 2487 dependent_code()->DeoptimizeDependentCodeGroup( 2488 GetIsolate(), DependentCode::kTransitionGroup); 2489 NotifyLeafMapLayoutChange(); 2490 } 2491 2492 2493 // Invalidates a transition target at |key|, and installs |new_descriptors| over 2494 // the current instance_descriptors to ensure proper sharing of descriptor 2495 // arrays. 2496 void Map::DeprecateTarget(Name* key, DescriptorArray* new_descriptors) { 2497 if (HasTransitionArray()) { 2498 TransitionArray* transitions = this->transitions(); 2499 int transition = transitions->Search(key); 2500 if (transition != TransitionArray::kNotFound) { 2501 transitions->GetTarget(transition)->DeprecateTransitionTree(); 2502 } 2503 } 2504 2505 // Don't overwrite the empty descriptor array. 2506 if (NumberOfOwnDescriptors() == 0) return; 2507 2508 DescriptorArray* to_replace = instance_descriptors(); 2509 Map* current = this; 2510 while (current->instance_descriptors() == to_replace) { 2511 current->SetEnumLength(Map::kInvalidEnumCache); 2512 current->set_instance_descriptors(new_descriptors); 2513 Object* next = current->GetBackPointer(); 2514 if (next->IsUndefined()) break; 2515 current = Map::cast(next); 2516 } 2517 2518 set_owns_descriptors(false); 2519 } 2520 2521 2522 Map* Map::FindRootMap() { 2523 Map* result = this; 2524 while (true) { 2525 Object* back = result->GetBackPointer(); 2526 if (back->IsUndefined()) return result; 2527 result = Map::cast(back); 2528 } 2529 } 2530 2531 2532 // Returns NULL if the updated map is incompatible. 2533 Map* Map::FindUpdatedMap(int verbatim, 2534 int length, 2535 DescriptorArray* descriptors) { 2536 // This can only be called on roots of transition trees. 2537 ASSERT(GetBackPointer()->IsUndefined()); 2538 2539 Map* current = this; 2540 2541 for (int i = verbatim; i < length; i++) { 2542 if (!current->HasTransitionArray()) break; 2543 Name* name = descriptors->GetKey(i); 2544 TransitionArray* transitions = current->transitions(); 2545 int transition = transitions->Search(name); 2546 if (transition == TransitionArray::kNotFound) break; 2547 current = transitions->GetTarget(transition); 2548 PropertyDetails details = descriptors->GetDetails(i); 2549 PropertyDetails target_details = 2550 current->instance_descriptors()->GetDetails(i); 2551 if (details.attributes() != target_details.attributes()) return NULL; 2552 if (details.type() == CALLBACKS) { 2553 if (target_details.type() != CALLBACKS) return NULL; 2554 if (descriptors->GetValue(i) != 2555 current->instance_descriptors()->GetValue(i)) { 2556 return NULL; 2557 } 2558 } 2559 } 2560 2561 return current; 2562 } 2563 2564 2565 Map* Map::FindLastMatchMap(int verbatim, 2566 int length, 2567 DescriptorArray* descriptors) { 2568 // This can only be called on roots of transition trees. 2569 ASSERT(GetBackPointer()->IsUndefined()); 2570 2571 Map* current = this; 2572 2573 for (int i = verbatim; i < length; i++) { 2574 if (!current->HasTransitionArray()) break; 2575 Name* name = descriptors->GetKey(i); 2576 TransitionArray* transitions = current->transitions(); 2577 int transition = transitions->Search(name); 2578 if (transition == TransitionArray::kNotFound) break; 2579 2580 Map* next = transitions->GetTarget(transition); 2581 DescriptorArray* next_descriptors = next->instance_descriptors(); 2582 2583 if (next_descriptors->GetValue(i) != descriptors->GetValue(i)) break; 2584 2585 PropertyDetails details = descriptors->GetDetails(i); 2586 PropertyDetails next_details = next_descriptors->GetDetails(i); 2587 if (details.type() != next_details.type()) break; 2588 if (details.attributes() != next_details.attributes()) break; 2589 if (!details.representation().Equals(next_details.representation())) break; 2590 2591 current = next; 2592 } 2593 return current; 2594 } 2595 2596 2597 // Generalize the representation of the descriptor at |modify_index|. 2598 // This method rewrites the transition tree to reflect the new change. To avoid 2599 // high degrees over polymorphism, and to stabilize quickly, on every rewrite 2600 // the new type is deduced by merging the current type with any potential new 2601 // (partial) version of the type in the transition tree. 2602 // To do this, on each rewrite: 2603 // - Search the root of the transition tree using FindRootMap. 2604 // - Find |updated|, the newest matching version of this map using 2605 // FindUpdatedMap. This uses the keys in the own map's descriptor array to 2606 // walk the transition tree. 2607 // - Merge/generalize the descriptor array of the current map and |updated|. 2608 // - Generalize the |modify_index| descriptor using |new_representation|. 2609 // - Walk the tree again starting from the root towards |updated|. Stop at 2610 // |split_map|, the first map who's descriptor array does not match the merged 2611 // descriptor array. 2612 // - If |updated| == |split_map|, |updated| is in the expected state. Return it. 2613 // - Otherwise, invalidate the outdated transition target from |updated|, and 2614 // replace its transition tree with a new branch for the updated descriptors. 2615 MaybeObject* Map::GeneralizeRepresentation(int modify_index, 2616 Representation new_representation) { 2617 Map* old_map = this; 2618 DescriptorArray* old_descriptors = old_map->instance_descriptors(); 2619 Representation old_representation = 2620 old_descriptors->GetDetails(modify_index).representation(); 2621 2622 // It's fine to transition from None to anything but double without any 2623 // modification to the object, because the default uninitialized value for 2624 // representation None can be overwritten by both smi and tagged values. 2625 // Doubles, however, would require a box allocation. 2626 if (old_representation.IsNone() && 2627 !new_representation.IsNone() && 2628 !new_representation.IsDouble()) { 2629 if (FLAG_trace_generalization) { 2630 PrintF("initializing representation %i: %p -> %s\n", 2631 modify_index, 2632 static_cast<void*>(this), 2633 new_representation.Mnemonic()); 2634 } 2635 old_descriptors->SetRepresentation(modify_index, new_representation); 2636 return old_map; 2637 } 2638 2639 int descriptors = old_map->NumberOfOwnDescriptors(); 2640 Map* root_map = old_map->FindRootMap(); 2641 2642 // Check the state of the root map. 2643 if (!old_map->EquivalentToForTransition(root_map)) { 2644 return CopyGeneralizeAllRepresentations(); 2645 } 2646 2647 int verbatim = root_map->NumberOfOwnDescriptors(); 2648 2649 Map* updated = root_map->FindUpdatedMap( 2650 verbatim, descriptors, old_descriptors); 2651 if (updated == NULL) return CopyGeneralizeAllRepresentations(); 2652 2653 DescriptorArray* updated_descriptors = updated->instance_descriptors(); 2654 2655 int valid = updated->NumberOfOwnDescriptors(); 2656 if (updated_descriptors->IsMoreGeneralThan( 2657 verbatim, valid, descriptors, old_descriptors)) { 2658 Representation updated_representation = 2659 updated_descriptors->GetDetails(modify_index).representation(); 2660 if (new_representation.fits_into(updated_representation)) { 2661 if (FLAG_trace_generalization && 2662 !(modify_index == 0 && new_representation.IsNone())) { 2663 PropertyDetails old_details = old_descriptors->GetDetails(modify_index); 2664 PrintF("migrating to existing map %p(%s) -> %p(%s)\n", 2665 static_cast<void*>(this), 2666 old_details.representation().Mnemonic(), 2667 static_cast<void*>(updated), 2668 updated_representation.Mnemonic()); 2669 } 2670 return updated; 2671 } 2672 } 2673 2674 DescriptorArray* new_descriptors; 2675 MaybeObject* maybe_descriptors = updated_descriptors->Merge( 2676 verbatim, valid, descriptors, old_descriptors); 2677 if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; 2678 2679 old_representation = 2680 new_descriptors->GetDetails(modify_index).representation(); 2681 Representation updated_representation = 2682 new_representation.generalize(old_representation); 2683 if (!updated_representation.Equals(old_representation)) { 2684 new_descriptors->SetRepresentation(modify_index, updated_representation); 2685 } 2686 2687 Map* split_map = root_map->FindLastMatchMap( 2688 verbatim, descriptors, new_descriptors); 2689 2690 int split_descriptors = split_map->NumberOfOwnDescriptors(); 2691 // This is shadowed by |updated_descriptors| being more general than 2692 // |old_descriptors|. 2693 ASSERT(descriptors != split_descriptors); 2694 2695 int descriptor = split_descriptors; 2696 split_map->DeprecateTarget( 2697 old_descriptors->GetKey(descriptor), new_descriptors); 2698 2699 if (FLAG_trace_generalization && 2700 !(modify_index == 0 && new_representation.IsNone())) { 2701 PrintF("migrating to new map %i: %p(%s) -> %p(%s) (%i steps)\n", 2702 modify_index, 2703 static_cast<void*>(this), 2704 old_representation.Mnemonic(), 2705 static_cast<void*>(new_descriptors), 2706 updated_representation.Mnemonic(), 2707 descriptors - descriptor); 2708 } 2709 2710 Map* new_map = split_map; 2711 // Add missing transitions. 2712 for (; descriptor < descriptors; descriptor++) { 2713 MaybeObject* maybe_map = new_map->CopyInstallDescriptors( 2714 descriptor, new_descriptors); 2715 if (!maybe_map->To(&new_map)) { 2716 // Create a handle for the last created map to ensure it stays alive 2717 // during GC. Its descriptor array is too large, but it will be 2718 // overwritten during retry anyway. 2719 Handle<Map>(new_map); 2720 return maybe_map; 2721 } 2722 new_map->set_migration_target(true); 2723 } 2724 2725 new_map->set_owns_descriptors(true); 2726 return new_map; 2727 } 2728 2729 2730 Map* Map::CurrentMapForDeprecated() { 2731 DisallowHeapAllocation no_allocation; 2732 if (!is_deprecated()) return this; 2733 2734 DescriptorArray* old_descriptors = instance_descriptors(); 2735 2736 int descriptors = NumberOfOwnDescriptors(); 2737 Map* root_map = FindRootMap(); 2738 2739 // Check the state of the root map. 2740 if (!EquivalentToForTransition(root_map)) return NULL; 2741 int verbatim = root_map->NumberOfOwnDescriptors(); 2742 2743 Map* updated = root_map->FindUpdatedMap( 2744 verbatim, descriptors, old_descriptors); 2745 if (updated == NULL) return NULL; 2746 2747 DescriptorArray* updated_descriptors = updated->instance_descriptors(); 2748 int valid = updated->NumberOfOwnDescriptors(); 2749 if (!updated_descriptors->IsMoreGeneralThan( 2750 verbatim, valid, descriptors, old_descriptors)) { 2751 return NULL; 2752 } 2753 2754 return updated; 2755 } 2756 2757 2758 MaybeObject* JSObject::SetPropertyWithInterceptor( 2759 Name* name, 2760 Object* value, 2761 PropertyAttributes attributes, 2762 StrictModeFlag strict_mode) { 2763 // TODO(rossberg): Support symbols in the API. 2764 if (name->IsSymbol()) return value; 2765 Isolate* isolate = GetIsolate(); 2766 HandleScope scope(isolate); 2767 Handle<JSObject> this_handle(this); 2768 Handle<String> name_handle(String::cast(name)); 2769 Handle<Object> value_handle(value, isolate); 2770 Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); 2771 if (!interceptor->setter()->IsUndefined()) { 2772 LOG(isolate, ApiNamedPropertyAccess("interceptor-named-set", this, name)); 2773 PropertyCallbackArguments args(isolate, interceptor->data(), this, this); 2774 v8::NamedPropertySetter setter = 2775 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter()); 2776 Handle<Object> value_unhole(value->IsTheHole() ? 2777 isolate->heap()->undefined_value() : 2778 value, 2779 isolate); 2780 v8::Handle<v8::Value> result = args.Call(setter, 2781 v8::Utils::ToLocal(name_handle), 2782 v8::Utils::ToLocal(value_unhole)); 2783 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 2784 if (!result.IsEmpty()) return *value_handle; 2785 } 2786 MaybeObject* raw_result = 2787 this_handle->SetPropertyPostInterceptor(*name_handle, 2788 *value_handle, 2789 attributes, 2790 strict_mode, 2791 PERFORM_EXTENSIBILITY_CHECK); 2792 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 2793 return raw_result; 2794 } 2795 2796 2797 Handle<Object> JSReceiver::SetProperty(Handle<JSReceiver> object, 2798 Handle<Name> key, 2799 Handle<Object> value, 2800 PropertyAttributes attributes, 2801 StrictModeFlag strict_mode) { 2802 CALL_HEAP_FUNCTION(object->GetIsolate(), 2803 object->SetProperty(*key, *value, attributes, strict_mode), 2804 Object); 2805 } 2806 2807 2808 MaybeObject* JSReceiver::SetPropertyOrFail( 2809 Handle<JSReceiver> object, 2810 Handle<Name> key, 2811 Handle<Object> value, 2812 PropertyAttributes attributes, 2813 StrictModeFlag strict_mode, 2814 JSReceiver::StoreFromKeyed store_mode) { 2815 CALL_HEAP_FUNCTION_PASS_EXCEPTION( 2816 object->GetIsolate(), 2817 object->SetProperty(*key, *value, attributes, strict_mode, store_mode)); 2818 } 2819 2820 2821 MaybeObject* JSReceiver::SetProperty(Name* name, 2822 Object* value, 2823 PropertyAttributes attributes, 2824 StrictModeFlag strict_mode, 2825 JSReceiver::StoreFromKeyed store_mode) { 2826 LookupResult result(GetIsolate()); 2827 LocalLookup(name, &result, true); 2828 if (!result.IsFound()) { 2829 map()->LookupTransition(JSObject::cast(this), name, &result); 2830 } 2831 return SetProperty(&result, name, value, attributes, strict_mode, store_mode); 2832 } 2833 2834 2835 MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, 2836 Name* name, 2837 Object* value, 2838 JSObject* holder, 2839 StrictModeFlag strict_mode) { 2840 Isolate* isolate = GetIsolate(); 2841 HandleScope scope(isolate); 2842 2843 // We should never get here to initialize a const with the hole 2844 // value since a const declaration would conflict with the setter. 2845 ASSERT(!value->IsTheHole()); 2846 Handle<Object> value_handle(value, isolate); 2847 2848 // To accommodate both the old and the new api we switch on the 2849 // data structure used to store the callbacks. Eventually foreign 2850 // callbacks should be phased out. 2851 if (structure->IsForeign()) { 2852 AccessorDescriptor* callback = 2853 reinterpret_cast<AccessorDescriptor*>( 2854 Foreign::cast(structure)->foreign_address()); 2855 MaybeObject* obj = (callback->setter)(this, value, callback->data); 2856 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 2857 if (obj->IsFailure()) return obj; 2858 return *value_handle; 2859 } 2860 2861 if (structure->IsExecutableAccessorInfo()) { 2862 // api style callbacks 2863 ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(structure); 2864 if (!data->IsCompatibleReceiver(this)) { 2865 Handle<Object> name_handle(name, isolate); 2866 Handle<Object> receiver_handle(this, isolate); 2867 Handle<Object> args[2] = { name_handle, receiver_handle }; 2868 Handle<Object> error = 2869 isolate->factory()->NewTypeError("incompatible_method_receiver", 2870 HandleVector(args, 2871 ARRAY_SIZE(args))); 2872 return isolate->Throw(*error); 2873 } 2874 // TODO(rossberg): Support symbols in the API. 2875 if (name->IsSymbol()) return value; 2876 Object* call_obj = data->setter(); 2877 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj); 2878 if (call_fun == NULL) return value; 2879 Handle<String> key(String::cast(name)); 2880 LOG(isolate, ApiNamedPropertyAccess("store", this, name)); 2881 PropertyCallbackArguments args( 2882 isolate, data->data(), this, JSObject::cast(holder)); 2883 args.Call(call_fun, 2884 v8::Utils::ToLocal(key), 2885 v8::Utils::ToLocal(value_handle)); 2886 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 2887 return *value_handle; 2888 } 2889 2890 if (structure->IsAccessorPair()) { 2891 Object* setter = AccessorPair::cast(structure)->setter(); 2892 if (setter->IsSpecFunction()) { 2893 // TODO(rossberg): nicer would be to cast to some JSCallable here... 2894 return SetPropertyWithDefinedSetter(JSReceiver::cast(setter), value); 2895 } else { 2896 if (strict_mode == kNonStrictMode) { 2897 return value; 2898 } 2899 Handle<Name> key(name); 2900 Handle<Object> holder_handle(holder, isolate); 2901 Handle<Object> args[2] = { key, holder_handle }; 2902 return isolate->Throw( 2903 *isolate->factory()->NewTypeError("no_setter_in_callback", 2904 HandleVector(args, 2))); 2905 } 2906 } 2907 2908 // TODO(dcarney): Handle correctly. 2909 if (structure->IsDeclaredAccessorInfo()) { 2910 return value; 2911 } 2912 2913 UNREACHABLE(); 2914 return NULL; 2915 } 2916 2917 2918 MaybeObject* JSReceiver::SetPropertyWithDefinedSetter(JSReceiver* setter, 2919 Object* value) { 2920 Isolate* isolate = GetIsolate(); 2921 Handle<Object> value_handle(value, isolate); 2922 Handle<JSReceiver> fun(setter, isolate); 2923 Handle<JSReceiver> self(this, isolate); 2924 #ifdef ENABLE_DEBUGGER_SUPPORT 2925 Debug* debug = isolate->debug(); 2926 // Handle stepping into a setter if step into is active. 2927 // TODO(rossberg): should this apply to getters that are function proxies? 2928 if (debug->StepInActive() && fun->IsJSFunction()) { 2929 debug->HandleStepIn( 2930 Handle<JSFunction>::cast(fun), Handle<Object>::null(), 0, false); 2931 } 2932 #endif 2933 bool has_pending_exception; 2934 Handle<Object> argv[] = { value_handle }; 2935 Execution::Call(fun, self, ARRAY_SIZE(argv), argv, &has_pending_exception); 2936 // Check for pending exception and return the result. 2937 if (has_pending_exception) return Failure::Exception(); 2938 return *value_handle; 2939 } 2940 2941 2942 MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( 2943 uint32_t index, 2944 Object* value, 2945 bool* found, 2946 StrictModeFlag strict_mode) { 2947 Heap* heap = GetHeap(); 2948 for (Object* pt = GetPrototype(); 2949 pt != heap->null_value(); 2950 pt = pt->GetPrototype(GetIsolate())) { 2951 if (pt->IsJSProxy()) { 2952 String* name; 2953 MaybeObject* maybe = heap->Uint32ToString(index); 2954 if (!maybe->To<String>(&name)) { 2955 *found = true; // Force abort 2956 return maybe; 2957 } 2958 return JSProxy::cast(pt)->SetPropertyViaPrototypesWithHandler( 2959 this, name, value, NONE, strict_mode, found); 2960 } 2961 if (!JSObject::cast(pt)->HasDictionaryElements()) { 2962 continue; 2963 } 2964 SeededNumberDictionary* dictionary = 2965 JSObject::cast(pt)->element_dictionary(); 2966 int entry = dictionary->FindEntry(index); 2967 if (entry != SeededNumberDictionary::kNotFound) { 2968 PropertyDetails details = dictionary->DetailsAt(entry); 2969 if (details.type() == CALLBACKS) { 2970 *found = true; 2971 return SetElementWithCallback(dictionary->ValueAt(entry), 2972 index, 2973 value, 2974 JSObject::cast(pt), 2975 strict_mode); 2976 } 2977 } 2978 } 2979 *found = false; 2980 return heap->the_hole_value(); 2981 } 2982 2983 MaybeObject* JSObject::SetPropertyViaPrototypes( 2984 Name* name, 2985 Object* value, 2986 PropertyAttributes attributes, 2987 StrictModeFlag strict_mode, 2988 bool* done) { 2989 Heap* heap = GetHeap(); 2990 Isolate* isolate = heap->isolate(); 2991 2992 *done = false; 2993 // We could not find a local property so let's check whether there is an 2994 // accessor that wants to handle the property, or whether the property is 2995 // read-only on the prototype chain. 2996 LookupResult result(isolate); 2997 LookupRealNamedPropertyInPrototypes(name, &result); 2998 if (result.IsFound()) { 2999 switch (result.type()) { 3000 case NORMAL: 3001 case FIELD: 3002 case CONSTANT: 3003 *done = result.IsReadOnly(); 3004 break; 3005 case INTERCEPTOR: { 3006 PropertyAttributes attr = 3007 result.holder()->GetPropertyAttributeWithInterceptor( 3008 this, name, true); 3009 *done = !!(attr & READ_ONLY); 3010 break; 3011 } 3012 case CALLBACKS: { 3013 if (!FLAG_es5_readonly && result.IsReadOnly()) break; 3014 *done = true; 3015 return SetPropertyWithCallback(result.GetCallbackObject(), 3016 name, value, result.holder(), strict_mode); 3017 } 3018 case HANDLER: { 3019 return result.proxy()->SetPropertyViaPrototypesWithHandler( 3020 this, name, value, attributes, strict_mode, done); 3021 } 3022 case TRANSITION: 3023 case NONEXISTENT: 3024 UNREACHABLE(); 3025 break; 3026 } 3027 } 3028 3029 // If we get here with *done true, we have encountered a read-only property. 3030 if (!FLAG_es5_readonly) *done = false; 3031 if (*done) { 3032 if (strict_mode == kNonStrictMode) return value; 3033 Handle<Object> args[] = { Handle<Object>(name, isolate), 3034 Handle<Object>(this, isolate)}; 3035 return isolate->Throw(*isolate->factory()->NewTypeError( 3036 "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); 3037 } 3038 return heap->the_hole_value(); 3039 } 3040 3041 3042 void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) { 3043 Handle<DescriptorArray> descriptors(map->instance_descriptors()); 3044 if (slack <= descriptors->NumberOfSlackDescriptors()) return; 3045 int number_of_descriptors = descriptors->number_of_descriptors(); 3046 Isolate* isolate = map->GetIsolate(); 3047 Handle<DescriptorArray> new_descriptors = 3048 isolate->factory()->NewDescriptorArray(number_of_descriptors, slack); 3049 DescriptorArray::WhitenessWitness witness(*new_descriptors); 3050 3051 for (int i = 0; i < number_of_descriptors; ++i) { 3052 new_descriptors->CopyFrom(i, *descriptors, i, witness); 3053 } 3054 3055 map->set_instance_descriptors(*new_descriptors); 3056 } 3057 3058 3059 void Map::AppendCallbackDescriptors(Handle<Map> map, 3060 Handle<Object> descriptors) { 3061 Isolate* isolate = map->GetIsolate(); 3062 Handle<DescriptorArray> array(map->instance_descriptors()); 3063 NeanderArray callbacks(descriptors); 3064 int nof_callbacks = callbacks.length(); 3065 3066 ASSERT(array->NumberOfSlackDescriptors() >= nof_callbacks); 3067 3068 // Ensure the keys are unique names before writing them into the 3069 // instance descriptor. Since it may cause a GC, it has to be done before we 3070 // temporarily put the heap in an invalid state while appending descriptors. 3071 for (int i = 0; i < nof_callbacks; ++i) { 3072 Handle<AccessorInfo> entry(AccessorInfo::cast(callbacks.get(i))); 3073 if (!entry->name()->IsUniqueName()) { 3074 Handle<String> key = 3075 isolate->factory()->InternalizedStringFromString( 3076 Handle<String>(String::cast(entry->name()))); 3077 entry->set_name(*key); 3078 } 3079 } 3080 3081 int nof = map->NumberOfOwnDescriptors(); 3082 3083 // Fill in new callback descriptors. Process the callbacks from 3084 // back to front so that the last callback with a given name takes 3085 // precedence over previously added callbacks with that name. 3086 for (int i = nof_callbacks - 1; i >= 0; i--) { 3087 AccessorInfo* entry = AccessorInfo::cast(callbacks.get(i)); 3088 Name* key = Name::cast(entry->name()); 3089 // Check if a descriptor with this name already exists before writing. 3090 if (array->Search(key, nof) == DescriptorArray::kNotFound) { 3091 CallbacksDescriptor desc(key, entry, entry->property_attributes()); 3092 array->Append(&desc); 3093 nof += 1; 3094 } 3095 } 3096 3097 map->SetNumberOfOwnDescriptors(nof); 3098 } 3099 3100 3101 static bool ContainsMap(MapHandleList* maps, Handle<Map> map) { 3102 ASSERT(!map.is_null()); 3103 for (int i = 0; i < maps->length(); ++i) { 3104 if (!maps->at(i).is_null() && maps->at(i).is_identical_to(map)) return true; 3105 } 3106 return false; 3107 } 3108 3109 3110 template <class T> 3111 static Handle<T> MaybeNull(T* p) { 3112 if (p == NULL) return Handle<T>::null(); 3113 return Handle<T>(p); 3114 } 3115 3116 3117 Handle<Map> Map::FindTransitionedMap(MapHandleList* candidates) { 3118 ElementsKind kind = elements_kind(); 3119 Handle<Map> transitioned_map = Handle<Map>::null(); 3120 Handle<Map> current_map(this); 3121 bool packed = IsFastPackedElementsKind(kind); 3122 if (IsTransitionableFastElementsKind(kind)) { 3123 while (CanTransitionToMoreGeneralFastElementsKind(kind, false)) { 3124 kind = GetNextMoreGeneralFastElementsKind(kind, false); 3125 Handle<Map> maybe_transitioned_map = 3126 MaybeNull(current_map->LookupElementsTransitionMap(kind)); 3127 if (maybe_transitioned_map.is_null()) break; 3128 if (ContainsMap(candidates, maybe_transitioned_map) && 3129 (packed || !IsFastPackedElementsKind(kind))) { 3130 transitioned_map = maybe_transitioned_map; 3131 if (!IsFastPackedElementsKind(kind)) packed = false; 3132 } 3133 current_map = maybe_transitioned_map; 3134 } 3135 } 3136 return transitioned_map; 3137 } 3138 3139 3140 static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) { 3141 Map* current_map = map; 3142 int index = GetSequenceIndexFromFastElementsKind(map->elements_kind()); 3143 int to_index = IsFastElementsKind(to_kind) 3144 ? GetSequenceIndexFromFastElementsKind(to_kind) 3145 : GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); 3146 3147 ASSERT(index <= to_index); 3148 3149 for (; index < to_index; ++index) { 3150 if (!current_map->HasElementsTransition()) return current_map; 3151 current_map = current_map->elements_transition_map(); 3152 } 3153 if (!IsFastElementsKind(to_kind) && current_map->HasElementsTransition()) { 3154 Map* next_map = current_map->elements_transition_map(); 3155 if (next_map->elements_kind() == to_kind) return next_map; 3156 } 3157 ASSERT(IsFastElementsKind(to_kind) 3158 ? current_map->elements_kind() == to_kind 3159 : current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND); 3160 return current_map; 3161 } 3162 3163 3164 Map* Map::LookupElementsTransitionMap(ElementsKind to_kind) { 3165 Map* to_map = FindClosestElementsTransition(this, to_kind); 3166 if (to_map->elements_kind() == to_kind) return to_map; 3167 return NULL; 3168 } 3169 3170 3171 bool Map::IsMapInArrayPrototypeChain() { 3172 Isolate* isolate = GetIsolate(); 3173 if (isolate->initial_array_prototype()->map() == this) { 3174 return true; 3175 } 3176 3177 if (isolate->initial_object_prototype()->map() == this) { 3178 return true; 3179 } 3180 3181 return false; 3182 } 3183 3184 3185 static MaybeObject* AddMissingElementsTransitions(Map* map, 3186 ElementsKind to_kind) { 3187 ASSERT(IsFastElementsKind(map->elements_kind())); 3188 int index = GetSequenceIndexFromFastElementsKind(map->elements_kind()); 3189 int to_index = IsFastElementsKind(to_kind) 3190 ? GetSequenceIndexFromFastElementsKind(to_kind) 3191 : GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); 3192 3193 ASSERT(index <= to_index); 3194 3195 Map* current_map = map; 3196 3197 for (; index < to_index; ++index) { 3198 ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(index + 1); 3199 MaybeObject* maybe_next_map = 3200 current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION); 3201 if (!maybe_next_map->To(¤t_map)) return maybe_next_map; 3202 } 3203 3204 // In case we are exiting the fast elements kind system, just add the map in 3205 // the end. 3206 if (!IsFastElementsKind(to_kind)) { 3207 MaybeObject* maybe_next_map = 3208 current_map->CopyAsElementsKind(to_kind, INSERT_TRANSITION); 3209 if (!maybe_next_map->To(¤t_map)) return maybe_next_map; 3210 } 3211 3212 ASSERT(current_map->elements_kind() == to_kind); 3213 return current_map; 3214 } 3215 3216 3217 Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object, 3218 ElementsKind to_kind) { 3219 Isolate* isolate = object->GetIsolate(); 3220 CALL_HEAP_FUNCTION(isolate, 3221 object->GetElementsTransitionMap(isolate, to_kind), 3222 Map); 3223 } 3224 3225 3226 MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) { 3227 Map* start_map = map(); 3228 ElementsKind from_kind = start_map->elements_kind(); 3229 3230 if (from_kind == to_kind) { 3231 return start_map; 3232 } 3233 3234 bool allow_store_transition = 3235 // Only remember the map transition if there is not an already existing 3236 // non-matching element transition. 3237 !start_map->IsUndefined() && !start_map->is_shared() && 3238 IsFastElementsKind(from_kind); 3239 3240 // Only store fast element maps in ascending generality. 3241 if (IsFastElementsKind(to_kind)) { 3242 allow_store_transition &= 3243 IsTransitionableFastElementsKind(from_kind) && 3244 IsMoreGeneralElementsKindTransition(from_kind, to_kind); 3245 } 3246 3247 if (!allow_store_transition) { 3248 return start_map->CopyAsElementsKind(to_kind, OMIT_TRANSITION); 3249 } 3250 3251 return start_map->AsElementsKind(to_kind); 3252 } 3253 3254 3255 MaybeObject* Map::AsElementsKind(ElementsKind kind) { 3256 Map* closest_map = FindClosestElementsTransition(this, kind); 3257 3258 if (closest_map->elements_kind() == kind) { 3259 return closest_map; 3260 } 3261 3262 return AddMissingElementsTransitions(closest_map, kind); 3263 } 3264 3265 3266 void JSObject::LocalLookupRealNamedProperty(Name* name, LookupResult* result) { 3267 if (IsJSGlobalProxy()) { 3268 Object* proto = GetPrototype(); 3269 if (proto->IsNull()) return result->NotFound(); 3270 ASSERT(proto->IsJSGlobalObject()); 3271 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result); 3272 } 3273 3274 if (HasFastProperties()) { 3275 map()->LookupDescriptor(this, name, result); 3276 // A property or a map transition was found. We return all of these result 3277 // types because LocalLookupRealNamedProperty is used when setting 3278 // properties where map transitions are handled. 3279 ASSERT(!result->IsFound() || 3280 (result->holder() == this && result->IsFastPropertyType())); 3281 // Disallow caching for uninitialized constants. These can only 3282 // occur as fields. 3283 if (result->IsField() && 3284 result->IsReadOnly() && 3285 RawFastPropertyAt(result->GetFieldIndex().field_index())->IsTheHole()) { 3286 result->DisallowCaching(); 3287 } 3288 return; 3289 } 3290 3291 int entry = property_dictionary()->FindEntry(name); 3292 if (entry != NameDictionary::kNotFound) { 3293 Object* value = property_dictionary()->ValueAt(entry); 3294 if (IsGlobalObject()) { 3295 PropertyDetails d = property_dictionary()->DetailsAt(entry); 3296 if (d.IsDeleted()) { 3297 result->NotFound(); 3298 return; 3299 } 3300 value = PropertyCell::cast(value)->value(); 3301 } 3302 // Make sure to disallow caching for uninitialized constants 3303 // found in the dictionary-mode objects. 3304 if (value->IsTheHole()) result->DisallowCaching(); 3305 result->DictionaryResult(this, entry); 3306 return; 3307 } 3308 3309 result->NotFound(); 3310 } 3311 3312 3313 void JSObject::LookupRealNamedProperty(Name* name, LookupResult* result) { 3314 LocalLookupRealNamedProperty(name, result); 3315 if (result->IsFound()) return; 3316 3317 LookupRealNamedPropertyInPrototypes(name, result); 3318 } 3319 3320 3321 void JSObject::LookupRealNamedPropertyInPrototypes(Name* name, 3322 LookupResult* result) { 3323 Isolate* isolate = GetIsolate(); 3324 Heap* heap = isolate->heap(); 3325 for (Object* pt = GetPrototype(); 3326 pt != heap->null_value(); 3327 pt = pt->GetPrototype(isolate)) { 3328 if (pt->IsJSProxy()) { 3329 return result->HandlerResult(JSProxy::cast(pt)); 3330 } 3331 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); 3332 ASSERT(!(result->IsFound() && result->type() == INTERCEPTOR)); 3333 if (result->IsFound()) return; 3334 } 3335 result->NotFound(); 3336 } 3337 3338 3339 // We only need to deal with CALLBACKS and INTERCEPTORS 3340 MaybeObject* JSObject::SetPropertyWithFailedAccessCheck( 3341 LookupResult* result, 3342 Name* name, 3343 Object* value, 3344 bool check_prototype, 3345 StrictModeFlag strict_mode) { 3346 if (check_prototype && !result->IsProperty()) { 3347 LookupRealNamedPropertyInPrototypes(name, result); 3348 } 3349 3350 if (result->IsProperty()) { 3351 if (!result->IsReadOnly()) { 3352 switch (result->type()) { 3353 case CALLBACKS: { 3354 Object* obj = result->GetCallbackObject(); 3355 if (obj->IsAccessorInfo()) { 3356 AccessorInfo* info = AccessorInfo::cast(obj); 3357 if (info->all_can_write()) { 3358 return SetPropertyWithCallback(result->GetCallbackObject(), 3359 name, 3360 value, 3361 result->holder(), 3362 strict_mode); 3363 } 3364 } 3365 break; 3366 } 3367 case INTERCEPTOR: { 3368 // Try lookup real named properties. Note that only property can be 3369 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain. 3370 LookupResult r(GetIsolate()); 3371 LookupRealNamedProperty(name, &r); 3372 if (r.IsProperty()) { 3373 return SetPropertyWithFailedAccessCheck(&r, 3374 name, 3375 value, 3376 check_prototype, 3377 strict_mode); 3378 } 3379 break; 3380 } 3381 default: { 3382 break; 3383 } 3384 } 3385 } 3386 } 3387 3388 Isolate* isolate = GetIsolate(); 3389 HandleScope scope(isolate); 3390 Handle<Object> value_handle(value, isolate); 3391 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); 3392 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 3393 return *value_handle; 3394 } 3395 3396 3397 MaybeObject* JSReceiver::SetProperty(LookupResult* result, 3398 Name* key, 3399 Object* value, 3400 PropertyAttributes attributes, 3401 StrictModeFlag strict_mode, 3402 JSReceiver::StoreFromKeyed store_mode) { 3403 if (result->IsHandler()) { 3404 return result->proxy()->SetPropertyWithHandler( 3405 this, key, value, attributes, strict_mode); 3406 } else { 3407 return JSObject::cast(this)->SetPropertyForResult( 3408 result, key, value, attributes, strict_mode, store_mode); 3409 } 3410 } 3411 3412 3413 bool JSProxy::HasPropertyWithHandler(Name* name_raw) { 3414 Isolate* isolate = GetIsolate(); 3415 HandleScope scope(isolate); 3416 Handle<Object> receiver(this, isolate); 3417 Handle<Object> name(name_raw, isolate); 3418 3419 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 3420 if (name->IsSymbol()) return false; 3421 3422 Handle<Object> args[] = { name }; 3423 Handle<Object> result = CallTrap( 3424 "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args); 3425 if (isolate->has_pending_exception()) return false; 3426 3427 return result->BooleanValue(); 3428 } 3429 3430 3431 MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler( 3432 JSReceiver* receiver_raw, 3433 Name* name_raw, 3434 Object* value_raw, 3435 PropertyAttributes attributes, 3436 StrictModeFlag strict_mode) { 3437 Isolate* isolate = GetIsolate(); 3438 HandleScope scope(isolate); 3439 Handle<JSReceiver> receiver(receiver_raw); 3440 Handle<Object> name(name_raw, isolate); 3441 Handle<Object> value(value_raw, isolate); 3442 3443 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 3444 if (name->IsSymbol()) return *value; 3445 3446 Handle<Object> args[] = { receiver, name, value }; 3447 CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args); 3448 if (isolate->has_pending_exception()) return Failure::Exception(); 3449 3450 return *value; 3451 } 3452 3453 3454 MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler( 3455 JSReceiver* receiver_raw, 3456 Name* name_raw, 3457 Object* value_raw, 3458 PropertyAttributes attributes, 3459 StrictModeFlag strict_mode, 3460 bool* done) { 3461 Isolate* isolate = GetIsolate(); 3462 Handle<JSProxy> proxy(this); 3463 Handle<JSReceiver> receiver(receiver_raw); 3464 Handle<Name> name(name_raw); 3465 Handle<Object> value(value_raw, isolate); 3466 Handle<Object> handler(this->handler(), isolate); // Trap might morph proxy. 3467 3468 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 3469 if (name->IsSymbol()) { 3470 *done = false; 3471 return isolate->heap()->the_hole_value(); 3472 } 3473 3474 *done = true; // except where redefined... 3475 Handle<Object> args[] = { name }; 3476 Handle<Object> result = proxy->CallTrap( 3477 "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); 3478 if (isolate->has_pending_exception()) return Failure::Exception(); 3479 3480 if (result->IsUndefined()) { 3481 *done = false; 3482 return isolate->heap()->the_hole_value(); 3483 } 3484 3485 // Emulate [[GetProperty]] semantics for proxies. 3486 bool has_pending_exception; 3487 Handle<Object> argv[] = { result }; 3488 Handle<Object> desc = 3489 Execution::Call(isolate->to_complete_property_descriptor(), result, 3490 ARRAY_SIZE(argv), argv, &has_pending_exception); 3491 if (has_pending_exception) return Failure::Exception(); 3492 3493 // [[GetProperty]] requires to check that all properties are configurable. 3494 Handle<String> configurable_name = 3495 isolate->factory()->InternalizeOneByteString( 3496 STATIC_ASCII_VECTOR("configurable_")); 3497 Handle<Object> configurable( 3498 v8::internal::GetProperty(isolate, desc, configurable_name)); 3499 ASSERT(!isolate->has_pending_exception()); 3500 ASSERT(configurable->IsTrue() || configurable->IsFalse()); 3501 if (configurable->IsFalse()) { 3502 Handle<String> trap = 3503 isolate->factory()->InternalizeOneByteString( 3504 STATIC_ASCII_VECTOR("getPropertyDescriptor")); 3505 Handle<Object> args[] = { handler, trap, name }; 3506 Handle<Object> error = isolate->factory()->NewTypeError( 3507 "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); 3508 return isolate->Throw(*error); 3509 } 3510 ASSERT(configurable->IsTrue()); 3511 3512 // Check for DataDescriptor. 3513 Handle<String> hasWritable_name = 3514 isolate->factory()->InternalizeOneByteString( 3515 STATIC_ASCII_VECTOR("hasWritable_")); 3516 Handle<Object> hasWritable( 3517 v8::internal::GetProperty(isolate, desc, hasWritable_name)); 3518 ASSERT(!isolate->has_pending_exception()); 3519 ASSERT(hasWritable->IsTrue() || hasWritable->IsFalse()); 3520 if (hasWritable->IsTrue()) { 3521 Handle<String> writable_name = 3522 isolate->factory()->InternalizeOneByteString( 3523 STATIC_ASCII_VECTOR("writable_")); 3524 Handle<Object> writable( 3525 v8::internal::GetProperty(isolate, desc, writable_name)); 3526 ASSERT(!isolate->has_pending_exception()); 3527 ASSERT(writable->IsTrue() || writable->IsFalse()); 3528 *done = writable->IsFalse(); 3529 if (!*done) return GetHeap()->the_hole_value(); 3530 if (strict_mode == kNonStrictMode) return *value; 3531 Handle<Object> args[] = { name, receiver }; 3532 Handle<Object> error = isolate->factory()->NewTypeError( 3533 "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args))); 3534 return isolate->Throw(*error); 3535 } 3536 3537 // We have an AccessorDescriptor. 3538 Handle<String> set_name = isolate->factory()->InternalizeOneByteString( 3539 STATIC_ASCII_VECTOR("set_")); 3540 Handle<Object> setter(v8::internal::GetProperty(isolate, desc, set_name)); 3541 ASSERT(!isolate->has_pending_exception()); 3542 if (!setter->IsUndefined()) { 3543 // TODO(rossberg): nicer would be to cast to some JSCallable here... 3544 return receiver->SetPropertyWithDefinedSetter( 3545 JSReceiver::cast(*setter), *value); 3546 } 3547 3548 if (strict_mode == kNonStrictMode) return *value; 3549 Handle<Object> args2[] = { name, proxy }; 3550 Handle<Object> error = isolate->factory()->NewTypeError( 3551 "no_setter_in_callback", HandleVector(args2, ARRAY_SIZE(args2))); 3552 return isolate->Throw(*error); 3553 } 3554 3555 3556 Handle<Object> JSProxy::DeletePropertyWithHandler( 3557 Handle<JSProxy> object, Handle<Name> name, DeleteMode mode) { 3558 Isolate* isolate = object->GetIsolate(); 3559 3560 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 3561 if (name->IsSymbol()) return isolate->factory()->false_value(); 3562 3563 Handle<Object> args[] = { name }; 3564 Handle<Object> result = object->CallTrap( 3565 "delete", Handle<Object>(), ARRAY_SIZE(args), args); 3566 if (isolate->has_pending_exception()) return Handle<Object>(); 3567 3568 bool result_bool = result->BooleanValue(); 3569 if (mode == STRICT_DELETION && !result_bool) { 3570 Handle<Object> handler(object->handler(), isolate); 3571 Handle<String> trap_name = isolate->factory()->InternalizeOneByteString( 3572 STATIC_ASCII_VECTOR("delete")); 3573 Handle<Object> args[] = { handler, trap_name }; 3574 Handle<Object> error = isolate->factory()->NewTypeError( 3575 "handler_failed", HandleVector(args, ARRAY_SIZE(args))); 3576 isolate->Throw(*error); 3577 return Handle<Object>(); 3578 } 3579 return isolate->factory()->ToBoolean(result_bool); 3580 } 3581 3582 3583 Handle<Object> JSProxy::DeleteElementWithHandler( 3584 Handle<JSProxy> object, uint32_t index, DeleteMode mode) { 3585 Isolate* isolate = object->GetIsolate(); 3586 Handle<String> name = isolate->factory()->Uint32ToString(index); 3587 return JSProxy::DeletePropertyWithHandler(object, name, mode); 3588 } 3589 3590 3591 MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( 3592 JSReceiver* receiver_raw, 3593 Name* name_raw) { 3594 Isolate* isolate = GetIsolate(); 3595 HandleScope scope(isolate); 3596 Handle<JSProxy> proxy(this); 3597 Handle<Object> handler(this->handler(), isolate); // Trap might morph proxy. 3598 Handle<JSReceiver> receiver(receiver_raw); 3599 Handle<Object> name(name_raw, isolate); 3600 3601 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 3602 if (name->IsSymbol()) return ABSENT; 3603 3604 Handle<Object> args[] = { name }; 3605 Handle<Object> result = CallTrap( 3606 "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); 3607 if (isolate->has_pending_exception()) return NONE; 3608 3609 if (result->IsUndefined()) return ABSENT; 3610 3611 bool has_pending_exception; 3612 Handle<Object> argv[] = { result }; 3613 Handle<Object> desc = 3614 Execution::Call(isolate->to_complete_property_descriptor(), result, 3615 ARRAY_SIZE(argv), argv, &has_pending_exception); 3616 if (has_pending_exception) return NONE; 3617 3618 // Convert result to PropertyAttributes. 3619 Handle<String> enum_n = isolate->factory()->InternalizeOneByteString( 3620 STATIC_ASCII_VECTOR("enumerable_")); 3621 Handle<Object> enumerable(v8::internal::GetProperty(isolate, desc, enum_n)); 3622 if (isolate->has_pending_exception()) return NONE; 3623 Handle<String> conf_n = isolate->factory()->InternalizeOneByteString( 3624 STATIC_ASCII_VECTOR("configurable_")); 3625 Handle<Object> configurable(v8::internal::GetProperty(isolate, desc, conf_n)); 3626 if (isolate->has_pending_exception()) return NONE; 3627 Handle<String> writ_n = isolate->factory()->InternalizeOneByteString( 3628 STATIC_ASCII_VECTOR("writable_")); 3629 Handle<Object> writable(v8::internal::GetProperty(isolate, desc, writ_n)); 3630 if (isolate->has_pending_exception()) return NONE; 3631 if (!writable->BooleanValue()) { 3632 Handle<String> set_n = isolate->factory()->InternalizeOneByteString( 3633 STATIC_ASCII_VECTOR("set_")); 3634 Handle<Object> setter(v8::internal::GetProperty(isolate, desc, set_n)); 3635 if (isolate->has_pending_exception()) return NONE; 3636 writable = isolate->factory()->ToBoolean(!setter->IsUndefined()); 3637 } 3638 3639 if (configurable->IsFalse()) { 3640 Handle<String> trap = isolate->factory()->InternalizeOneByteString( 3641 STATIC_ASCII_VECTOR("getPropertyDescriptor")); 3642 Handle<Object> args[] = { handler, trap, name }; 3643 Handle<Object> error = isolate->factory()->NewTypeError( 3644 "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); 3645 isolate->Throw(*error); 3646 return NONE; 3647 } 3648 3649 int attributes = NONE; 3650 if (!enumerable->BooleanValue()) attributes |= DONT_ENUM; 3651 if (!configurable->BooleanValue()) attributes |= DONT_DELETE; 3652 if (!writable->BooleanValue()) attributes |= READ_ONLY; 3653 return static_cast<PropertyAttributes>(attributes); 3654 } 3655 3656 3657 MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler( 3658 JSReceiver* receiver_raw, 3659 uint32_t index) { 3660 Isolate* isolate = GetIsolate(); 3661 HandleScope scope(isolate); 3662 Handle<JSProxy> proxy(this); 3663 Handle<JSReceiver> receiver(receiver_raw); 3664 Handle<String> name = isolate->factory()->Uint32ToString(index); 3665 return proxy->GetPropertyAttributeWithHandler(*receiver, *name); 3666 } 3667 3668 3669 void JSProxy::Fix() { 3670 Isolate* isolate = GetIsolate(); 3671 HandleScope scope(isolate); 3672 Handle<JSProxy> self(this); 3673 3674 // Save identity hash. 3675 MaybeObject* maybe_hash = GetIdentityHash(OMIT_CREATION); 3676 3677 if (IsJSFunctionProxy()) { 3678 isolate->factory()->BecomeJSFunction(self); 3679 // Code will be set on the JavaScript side. 3680 } else { 3681 isolate->factory()->BecomeJSObject(self); 3682 } 3683 ASSERT(self->IsJSObject()); 3684 3685 // Inherit identity, if it was present. 3686 Object* hash; 3687 if (maybe_hash->To<Object>(&hash) && hash->IsSmi()) { 3688 Handle<JSObject> new_self(JSObject::cast(*self)); 3689 isolate->factory()->SetIdentityHash(new_self, Smi::cast(hash)); 3690 } 3691 } 3692 3693 3694 MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(const char* name, 3695 Handle<Object> derived, 3696 int argc, 3697 Handle<Object> argv[]) { 3698 Isolate* isolate = GetIsolate(); 3699 Handle<Object> handler(this->handler(), isolate); 3700 3701 Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name); 3702 Handle<Object> trap(v8::internal::GetProperty(isolate, handler, trap_name)); 3703 if (isolate->has_pending_exception()) return trap; 3704 3705 if (trap->IsUndefined()) { 3706 if (derived.is_null()) { 3707 Handle<Object> args[] = { handler, trap_name }; 3708 Handle<Object> error = isolate->factory()->NewTypeError( 3709 "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args))); 3710 isolate->Throw(*error); 3711 return Handle<Object>(); 3712 } 3713 trap = Handle<Object>(derived); 3714 } 3715 3716 bool threw; 3717 return Execution::Call(trap, handler, argc, argv, &threw); 3718 } 3719 3720 3721 void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) { 3722 CALL_HEAP_FUNCTION_VOID( 3723 object->GetIsolate(), 3724 object->AllocateStorageForMap(*map)); 3725 } 3726 3727 3728 void JSObject::MigrateInstance(Handle<JSObject> object) { 3729 if (FLAG_trace_migration) { 3730 PrintF("migrating instance %p (%p)\n", 3731 static_cast<void*>(*object), 3732 static_cast<void*>(object->map())); 3733 } 3734 CALL_HEAP_FUNCTION_VOID( 3735 object->GetIsolate(), 3736 object->MigrateInstance()); 3737 } 3738 3739 3740 Handle<Object> JSObject::TryMigrateInstance(Handle<JSObject> object) { 3741 if (FLAG_trace_migration) { 3742 PrintF("migrating instance (no new maps) %p (%p)\n", 3743 static_cast<void*>(*object), 3744 static_cast<void*>(object->map())); 3745 } 3746 CALL_HEAP_FUNCTION( 3747 object->GetIsolate(), 3748 object->MigrateInstance(), 3749 Object); 3750 } 3751 3752 3753 Handle<Map> Map::GeneralizeRepresentation(Handle<Map> map, 3754 int modify_index, 3755 Representation representation) { 3756 CALL_HEAP_FUNCTION( 3757 map->GetIsolate(), 3758 map->GeneralizeRepresentation(modify_index, representation), 3759 Map); 3760 } 3761 3762 3763 MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup, 3764 Name* name_raw, 3765 Object* value_raw, 3766 PropertyAttributes attributes, 3767 StrictModeFlag strict_mode, 3768 StoreFromKeyed store_mode) { 3769 Heap* heap = GetHeap(); 3770 Isolate* isolate = heap->isolate(); 3771 // Make sure that the top context does not change when doing callbacks or 3772 // interceptor calls. 3773 AssertNoContextChange ncc; 3774 3775 // Optimization for 2-byte strings often used as keys in a decompression 3776 // dictionary. We internalize these short keys to avoid constantly 3777 // reallocating them. 3778 if (name_raw->IsString() && !name_raw->IsInternalizedString() && 3779 String::cast(name_raw)->length() <= 2) { 3780 Object* internalized_version; 3781 { MaybeObject* maybe_string_version = 3782 heap->InternalizeString(String::cast(name_raw)); 3783 if (maybe_string_version->ToObject(&internalized_version)) { 3784 name_raw = String::cast(internalized_version); 3785 } 3786 } 3787 } 3788 3789 // Check access rights if needed. 3790 if (IsAccessCheckNeeded()) { 3791 if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { 3792 return SetPropertyWithFailedAccessCheck( 3793 lookup, name_raw, value_raw, true, strict_mode); 3794 } 3795 } 3796 3797 if (IsJSGlobalProxy()) { 3798 Object* proto = GetPrototype(); 3799 if (proto->IsNull()) return value_raw; 3800 ASSERT(proto->IsJSGlobalObject()); 3801 return JSObject::cast(proto)->SetPropertyForResult( 3802 lookup, name_raw, value_raw, attributes, strict_mode, store_mode); 3803 } 3804 3805 ASSERT(!lookup->IsFound() || lookup->holder() == this || 3806 lookup->holder()->map()->is_hidden_prototype()); 3807 3808 // From this point on everything needs to be handlified, because 3809 // SetPropertyViaPrototypes might call back into JavaScript. 3810 HandleScope scope(isolate); 3811 Handle<JSObject> self(this); 3812 Handle<Name> name(name_raw); 3813 Handle<Object> value(value_raw, isolate); 3814 3815 if (!lookup->IsProperty() && !self->IsJSContextExtensionObject()) { 3816 bool done = false; 3817 MaybeObject* result_object = self->SetPropertyViaPrototypes( 3818 *name, *value, attributes, strict_mode, &done); 3819 if (done) return result_object; 3820 } 3821 3822 if (!lookup->IsFound()) { 3823 // Neither properties nor transitions found. 3824 return self->AddProperty( 3825 *name, *value, attributes, strict_mode, store_mode); 3826 } 3827 3828 if (lookup->IsProperty() && lookup->IsReadOnly()) { 3829 if (strict_mode == kStrictMode) { 3830 Handle<Object> args[] = { name, self }; 3831 return isolate->Throw(*isolate->factory()->NewTypeError( 3832 "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); 3833 } else { 3834 return *value; 3835 } 3836 } 3837 3838 Handle<Object> old_value(heap->the_hole_value(), isolate); 3839 if (FLAG_harmony_observation && 3840 map()->is_observed() && lookup->IsDataProperty()) { 3841 old_value = Object::GetProperty(self, name); 3842 } 3843 3844 // This is a real property that is not read-only, or it is a 3845 // transition or null descriptor and there are no setters in the prototypes. 3846 MaybeObject* result = *value; 3847 switch (lookup->type()) { 3848 case NORMAL: 3849 result = lookup->holder()->SetNormalizedProperty(lookup, *value); 3850 break; 3851 case FIELD: { 3852 Representation representation = lookup->representation(); 3853 if (!value->FitsRepresentation(representation)) { 3854 MaybeObject* maybe_failure = 3855 lookup->holder()->GeneralizeFieldRepresentation( 3856 lookup->GetDescriptorIndex(), value->OptimalRepresentation()); 3857 if (maybe_failure->IsFailure()) return maybe_failure; 3858 DescriptorArray* desc = lookup->holder()->map()->instance_descriptors(); 3859 int descriptor = lookup->GetDescriptorIndex(); 3860 representation = desc->GetDetails(descriptor).representation(); 3861 } 3862 if (FLAG_track_double_fields && representation.IsDouble()) { 3863 HeapNumber* storage = 3864 HeapNumber::cast(lookup->holder()->RawFastPropertyAt( 3865 lookup->GetFieldIndex().field_index())); 3866 storage->set_value(value->Number()); 3867 result = *value; 3868 break; 3869 } 3870 lookup->holder()->FastPropertyAtPut( 3871 lookup->GetFieldIndex().field_index(), *value); 3872 result = *value; 3873 break; 3874 } 3875 case CONSTANT: 3876 // Only replace the constant if necessary. 3877 if (*value == lookup->GetConstant()) return *value; 3878 // Preserve the attributes of this existing property. 3879 attributes = lookup->GetAttributes(); 3880 result = lookup->holder()->ConvertDescriptorToField( 3881 *name, *value, attributes); 3882 break; 3883 case CALLBACKS: { 3884 Object* callback_object = lookup->GetCallbackObject(); 3885 return self->SetPropertyWithCallback( 3886 callback_object, *name, *value, lookup->holder(), strict_mode); 3887 } 3888 case INTERCEPTOR: 3889 result = lookup->holder()->SetPropertyWithInterceptor( 3890 *name, *value, attributes, strict_mode); 3891 break; 3892 case TRANSITION: { 3893 Map* transition_map = lookup->GetTransitionTarget(); 3894 int descriptor = transition_map->LastAdded(); 3895 3896 DescriptorArray* descriptors = transition_map->instance_descriptors(); 3897 PropertyDetails details = descriptors->GetDetails(descriptor); 3898 3899 if (details.type() == FIELD) { 3900 if (attributes == details.attributes()) { 3901 Representation representation = details.representation(); 3902 if (!value->FitsRepresentation(representation)) { 3903 MaybeObject* maybe_map = transition_map->GeneralizeRepresentation( 3904 descriptor, value->OptimalRepresentation()); 3905 if (!maybe_map->To(&transition_map)) return maybe_map; 3906 Object* back = transition_map->GetBackPointer(); 3907 if (back->IsMap()) { 3908 MaybeObject* maybe_failure = 3909 lookup->holder()->MigrateToMap(Map::cast(back)); 3910 if (maybe_failure->IsFailure()) return maybe_failure; 3911 } 3912 descriptors = transition_map->instance_descriptors(); 3913 representation = 3914 descriptors->GetDetails(descriptor).representation(); 3915 } 3916 int field_index = descriptors->GetFieldIndex(descriptor); 3917 result = lookup->holder()->AddFastPropertyUsingMap( 3918 transition_map, *name, *value, field_index, representation); 3919 } else { 3920 result = lookup->holder()->ConvertDescriptorToField( 3921 *name, *value, attributes); 3922 } 3923 } else if (details.type() == CALLBACKS) { 3924 result = lookup->holder()->ConvertDescriptorToField( 3925 *name, *value, attributes); 3926 } else { 3927 ASSERT(details.type() == CONSTANT); 3928 3929 Object* constant = descriptors->GetValue(descriptor); 3930 if (constant == *value) { 3931 // If the same constant function is being added we can simply 3932 // transition to the target map. 3933 lookup->holder()->set_map(transition_map); 3934 result = constant; 3935 } else { 3936 // Otherwise, replace with a map transition to a new map with a FIELD, 3937 // even if the value is a constant function. 3938 result = lookup->holder()->ConvertTransitionToMapTransition( 3939 lookup->GetTransitionIndex(), *name, *value, attributes); 3940 } 3941 } 3942 break; 3943 } 3944 case HANDLER: 3945 case NONEXISTENT: 3946 UNREACHABLE(); 3947 } 3948 3949 Handle<Object> hresult; 3950 if (!result->ToHandle(&hresult, isolate)) return result; 3951 3952 if (FLAG_harmony_observation && self->map()->is_observed()) { 3953 if (lookup->IsTransition()) { 3954 EnqueueChangeRecord(self, "new", name, old_value); 3955 } else { 3956 LookupResult new_lookup(isolate); 3957 self->LocalLookup(*name, &new_lookup, true); 3958 if (new_lookup.IsDataProperty()) { 3959 Handle<Object> new_value = Object::GetProperty(self, name); 3960 if (!new_value->SameValue(*old_value)) { 3961 EnqueueChangeRecord(self, "updated", name, old_value); 3962 } 3963 } 3964 } 3965 } 3966 3967 return *hresult; 3968 } 3969 3970 3971 // Set a real local property, even if it is READ_ONLY. If the property is not 3972 // present, add it with attributes NONE. This code is an exact clone of 3973 // SetProperty, with the check for IsReadOnly and the check for a 3974 // callback setter removed. The two lines looking up the LookupResult 3975 // result are also added. If one of the functions is changed, the other 3976 // should be. 3977 // Note that this method cannot be used to set the prototype of a function 3978 // because ConvertDescriptorToField() which is called in "case CALLBACKS:" 3979 // doesn't handle function prototypes correctly. 3980 Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes( 3981 Handle<JSObject> object, 3982 Handle<Name> key, 3983 Handle<Object> value, 3984 PropertyAttributes attributes, 3985 ValueType value_type, 3986 StoreMode mode) { 3987 CALL_HEAP_FUNCTION( 3988 object->GetIsolate(), 3989 object->SetLocalPropertyIgnoreAttributes( 3990 *key, *value, attributes, value_type, mode), 3991 Object); 3992 } 3993 3994 3995 MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( 3996 Name* name_raw, 3997 Object* value_raw, 3998 PropertyAttributes attributes, 3999 ValueType value_type, 4000 StoreMode mode) { 4001 // Make sure that the top context does not change when doing callbacks or 4002 // interceptor calls. 4003 AssertNoContextChange ncc; 4004 Isolate* isolate = GetIsolate(); 4005 LookupResult lookup(isolate); 4006 LocalLookup(name_raw, &lookup, true); 4007 if (!lookup.IsFound()) map()->LookupTransition(this, name_raw, &lookup); 4008 // Check access rights if needed. 4009 if (IsAccessCheckNeeded()) { 4010 if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { 4011 return SetPropertyWithFailedAccessCheck(&lookup, 4012 name_raw, 4013 value_raw, 4014 false, 4015 kNonStrictMode); 4016 } 4017 } 4018 4019 if (IsJSGlobalProxy()) { 4020 Object* proto = GetPrototype(); 4021 if (proto->IsNull()) return value_raw; 4022 ASSERT(proto->IsJSGlobalObject()); 4023 return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes( 4024 name_raw, 4025 value_raw, 4026 attributes, 4027 value_type, 4028 mode); 4029 } 4030 4031 // Check for accessor in prototype chain removed here in clone. 4032 if (!lookup.IsFound()) { 4033 // Neither properties nor transitions found. 4034 return AddProperty( 4035 name_raw, value_raw, attributes, kNonStrictMode, 4036 MAY_BE_STORE_FROM_KEYED, PERFORM_EXTENSIBILITY_CHECK, value_type, mode); 4037 } 4038 4039 // From this point on everything needs to be handlified. 4040 HandleScope scope(isolate); 4041 Handle<JSObject> self(this); 4042 Handle<Name> name(name_raw); 4043 Handle<Object> value(value_raw, isolate); 4044 4045 Handle<Object> old_value(isolate->heap()->the_hole_value(), isolate); 4046 PropertyAttributes old_attributes = ABSENT; 4047 bool is_observed = FLAG_harmony_observation && self->map()->is_observed(); 4048 if (is_observed && lookup.IsProperty()) { 4049 if (lookup.IsDataProperty()) old_value = Object::GetProperty(self, name); 4050 old_attributes = lookup.GetAttributes(); 4051 } 4052 4053 // Check of IsReadOnly removed from here in clone. 4054 MaybeObject* result = *value; 4055 switch (lookup.type()) { 4056 case NORMAL: { 4057 PropertyDetails details = PropertyDetails(attributes, NORMAL, 0); 4058 result = self->SetNormalizedProperty(*name, *value, details); 4059 break; 4060 } 4061 case FIELD: { 4062 Representation representation = lookup.representation(); 4063 Representation value_representation = 4064 value->OptimalRepresentation(value_type); 4065 if (value_representation.IsNone()) break; 4066 if (!value_representation.fits_into(representation)) { 4067 MaybeObject* maybe_failure = self->GeneralizeFieldRepresentation( 4068 lookup.GetDescriptorIndex(), value_representation); 4069 if (maybe_failure->IsFailure()) return maybe_failure; 4070 DescriptorArray* desc = self->map()->instance_descriptors(); 4071 int descriptor = lookup.GetDescriptorIndex(); 4072 representation = desc->GetDetails(descriptor).representation(); 4073 } 4074 if (FLAG_track_double_fields && representation.IsDouble()) { 4075 HeapNumber* storage = 4076 HeapNumber::cast(self->RawFastPropertyAt( 4077 lookup.GetFieldIndex().field_index())); 4078 storage->set_value(value->Number()); 4079 result = *value; 4080 break; 4081 } 4082 self->FastPropertyAtPut(lookup.GetFieldIndex().field_index(), *value); 4083 result = *value; 4084 break; 4085 } 4086 case CONSTANT: 4087 // Only replace the function if necessary. 4088 if (*value != lookup.GetConstant()) { 4089 // Preserve the attributes of this existing property. 4090 attributes = lookup.GetAttributes(); 4091 result = self->ConvertDescriptorToField(*name, *value, attributes); 4092 } 4093 break; 4094 case CALLBACKS: 4095 case INTERCEPTOR: 4096 // Override callback in clone 4097 result = self->ConvertDescriptorToField(*name, *value, attributes); 4098 break; 4099 case TRANSITION: { 4100 Map* transition_map = lookup.GetTransitionTarget(); 4101 int descriptor = transition_map->LastAdded(); 4102 4103 DescriptorArray* descriptors = transition_map->instance_descriptors(); 4104 PropertyDetails details = descriptors->GetDetails(descriptor); 4105 4106 if (details.type() == FIELD) { 4107 if (attributes == details.attributes()) { 4108 Representation representation = details.representation(); 4109 Representation value_representation = 4110 value->OptimalRepresentation(value_type); 4111 if (!value_representation.fits_into(representation)) { 4112 MaybeObject* maybe_map = transition_map->GeneralizeRepresentation( 4113 descriptor, value_representation); 4114 if (!maybe_map->To(&transition_map)) return maybe_map; 4115 Object* back = transition_map->GetBackPointer(); 4116 if (back->IsMap()) { 4117 MaybeObject* maybe_failure = self->MigrateToMap(Map::cast(back)); 4118 if (maybe_failure->IsFailure()) return maybe_failure; 4119 } 4120 descriptors = transition_map->instance_descriptors(); 4121 representation = 4122 descriptors->GetDetails(descriptor).representation(); 4123 } 4124 int field_index = descriptors->GetFieldIndex(descriptor); 4125 result = self->AddFastPropertyUsingMap( 4126 transition_map, *name, *value, field_index, representation); 4127 } else { 4128 result = self->ConvertDescriptorToField(*name, *value, attributes); 4129 } 4130 } else if (details.type() == CALLBACKS) { 4131 result = self->ConvertDescriptorToField(*name, *value, attributes); 4132 } else { 4133 ASSERT(details.type() == CONSTANT); 4134 4135 // Replace transition to CONSTANT FUNCTION with a map transition to a 4136 // new map with a FIELD, even if the value is a function. 4137 result = self->ConvertTransitionToMapTransition( 4138 lookup.GetTransitionIndex(), *name, *value, attributes); 4139 } 4140 break; 4141 } 4142 case HANDLER: 4143 case NONEXISTENT: 4144 UNREACHABLE(); 4145 } 4146 4147 Handle<Object> hresult; 4148 if (!result->ToHandle(&hresult, isolate)) return result; 4149 4150 if (is_observed) { 4151 if (lookup.IsTransition()) { 4152 EnqueueChangeRecord(self, "new", name, old_value); 4153 } else if (old_value->IsTheHole()) { 4154 EnqueueChangeRecord(self, "reconfigured", name, old_value); 4155 } else { 4156 LookupResult new_lookup(isolate); 4157 self->LocalLookup(*name, &new_lookup, true); 4158 bool value_changed = false; 4159 if (new_lookup.IsDataProperty()) { 4160 Handle<Object> new_value = Object::GetProperty(self, name); 4161 value_changed = !old_value->SameValue(*new_value); 4162 } 4163 if (new_lookup.GetAttributes() != old_attributes) { 4164 if (!value_changed) old_value = isolate->factory()->the_hole_value(); 4165 EnqueueChangeRecord(self, "reconfigured", name, old_value); 4166 } else if (value_changed) { 4167 EnqueueChangeRecord(self, "updated", name, old_value); 4168 } 4169 } 4170 } 4171 4172 return *hresult; 4173 } 4174 4175 4176 PropertyAttributes JSObject::GetPropertyAttributePostInterceptor( 4177 JSObject* receiver, 4178 Name* name, 4179 bool continue_search) { 4180 // Check local property, ignore interceptor. 4181 LookupResult result(GetIsolate()); 4182 LocalLookupRealNamedProperty(name, &result); 4183 if (result.IsFound()) return result.GetAttributes(); 4184 4185 if (continue_search) { 4186 // Continue searching via the prototype chain. 4187 Object* pt = GetPrototype(); 4188 if (!pt->IsNull()) { 4189 return JSObject::cast(pt)-> 4190 GetPropertyAttributeWithReceiver(receiver, name); 4191 } 4192 } 4193 return ABSENT; 4194 } 4195 4196 4197 PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor( 4198 JSObject* receiver, 4199 Name* name, 4200 bool continue_search) { 4201 // TODO(rossberg): Support symbols in the API. 4202 if (name->IsSymbol()) return ABSENT; 4203 4204 Isolate* isolate = GetIsolate(); 4205 4206 // Make sure that the top context does not change when doing 4207 // callbacks or interceptor calls. 4208 AssertNoContextChange ncc; 4209 4210 HandleScope scope(isolate); 4211 Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); 4212 Handle<JSObject> receiver_handle(receiver); 4213 Handle<JSObject> holder_handle(this); 4214 Handle<String> name_handle(String::cast(name)); 4215 PropertyCallbackArguments args(isolate, interceptor->data(), receiver, this); 4216 if (!interceptor->query()->IsUndefined()) { 4217 v8::NamedPropertyQuery query = 4218 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query()); 4219 LOG(isolate, 4220 ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name)); 4221 v8::Handle<v8::Integer> result = 4222 args.Call(query, v8::Utils::ToLocal(name_handle)); 4223 if (!result.IsEmpty()) { 4224 ASSERT(result->IsInt32()); 4225 return static_cast<PropertyAttributes>(result->Int32Value()); 4226 } 4227 } else if (!interceptor->getter()->IsUndefined()) { 4228 v8::NamedPropertyGetter getter = 4229 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter()); 4230 LOG(isolate, 4231 ApiNamedPropertyAccess("interceptor-named-get-has", this, name)); 4232 v8::Handle<v8::Value> result = 4233 args.Call(getter, v8::Utils::ToLocal(name_handle)); 4234 if (!result.IsEmpty()) return DONT_ENUM; 4235 } 4236 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle, 4237 *name_handle, 4238 continue_search); 4239 } 4240 4241 4242 PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver( 4243 JSReceiver* receiver, 4244 Name* key) { 4245 uint32_t index = 0; 4246 if (IsJSObject() && key->AsArrayIndex(&index)) { 4247 return JSObject::cast(this)->GetElementAttributeWithReceiver( 4248 receiver, index, true); 4249 } 4250 // Named property. 4251 LookupResult lookup(GetIsolate()); 4252 Lookup(key, &lookup); 4253 return GetPropertyAttributeForResult(receiver, &lookup, key, true); 4254 } 4255 4256 4257 PropertyAttributes JSReceiver::GetPropertyAttributeForResult( 4258 JSReceiver* receiver, 4259 LookupResult* lookup, 4260 Name* name, 4261 bool continue_search) { 4262 // Check access rights if needed. 4263 if (IsAccessCheckNeeded()) { 4264 JSObject* this_obj = JSObject::cast(this); 4265 Heap* heap = GetHeap(); 4266 if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) { 4267 return this_obj->GetPropertyAttributeWithFailedAccessCheck( 4268 receiver, lookup, name, continue_search); 4269 } 4270 } 4271 if (lookup->IsFound()) { 4272 switch (lookup->type()) { 4273 case NORMAL: // fall through 4274 case FIELD: 4275 case CONSTANT: 4276 case CALLBACKS: 4277 return lookup->GetAttributes(); 4278 case HANDLER: { 4279 return JSProxy::cast(lookup->proxy())->GetPropertyAttributeWithHandler( 4280 receiver, name); 4281 } 4282 case INTERCEPTOR: 4283 return lookup->holder()->GetPropertyAttributeWithInterceptor( 4284 JSObject::cast(receiver), name, continue_search); 4285 case TRANSITION: 4286 case NONEXISTENT: 4287 UNREACHABLE(); 4288 } 4289 } 4290 return ABSENT; 4291 } 4292 4293 4294 PropertyAttributes JSReceiver::GetLocalPropertyAttribute(Name* name) { 4295 // Check whether the name is an array index. 4296 uint32_t index = 0; 4297 if (IsJSObject() && name->AsArrayIndex(&index)) { 4298 return GetLocalElementAttribute(index); 4299 } 4300 // Named property. 4301 LookupResult lookup(GetIsolate()); 4302 LocalLookup(name, &lookup, true); 4303 return GetPropertyAttributeForResult(this, &lookup, name, false); 4304 } 4305 4306 4307 PropertyAttributes JSObject::GetElementAttributeWithReceiver( 4308 JSReceiver* receiver, uint32_t index, bool continue_search) { 4309 Isolate* isolate = GetIsolate(); 4310 4311 // Check access rights if needed. 4312 if (IsAccessCheckNeeded()) { 4313 if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { 4314 isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 4315 return ABSENT; 4316 } 4317 } 4318 4319 if (IsJSGlobalProxy()) { 4320 Object* proto = GetPrototype(); 4321 if (proto->IsNull()) return ABSENT; 4322 ASSERT(proto->IsJSGlobalObject()); 4323 return JSObject::cast(proto)->GetElementAttributeWithReceiver( 4324 receiver, index, continue_search); 4325 } 4326 4327 // Check for lookup interceptor except when bootstrapping. 4328 if (HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) { 4329 return GetElementAttributeWithInterceptor(receiver, index, continue_search); 4330 } 4331 4332 return GetElementAttributeWithoutInterceptor( 4333 receiver, index, continue_search); 4334 } 4335 4336 4337 PropertyAttributes JSObject::GetElementAttributeWithInterceptor( 4338 JSReceiver* receiver, uint32_t index, bool continue_search) { 4339 Isolate* isolate = GetIsolate(); 4340 // Make sure that the top context does not change when doing 4341 // callbacks or interceptor calls. 4342 AssertNoContextChange ncc; 4343 HandleScope scope(isolate); 4344 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 4345 Handle<JSReceiver> hreceiver(receiver); 4346 Handle<JSObject> holder(this); 4347 PropertyCallbackArguments args(isolate, interceptor->data(), receiver, this); 4348 if (!interceptor->query()->IsUndefined()) { 4349 v8::IndexedPropertyQuery query = 4350 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); 4351 LOG(isolate, 4352 ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); 4353 v8::Handle<v8::Integer> result = args.Call(query, index); 4354 if (!result.IsEmpty()) 4355 return static_cast<PropertyAttributes>(result->Int32Value()); 4356 } else if (!interceptor->getter()->IsUndefined()) { 4357 v8::IndexedPropertyGetter getter = 4358 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); 4359 LOG(isolate, 4360 ApiIndexedPropertyAccess("interceptor-indexed-get-has", this, index)); 4361 v8::Handle<v8::Value> result = args.Call(getter, index); 4362 if (!result.IsEmpty()) return NONE; 4363 } 4364 4365 return holder->GetElementAttributeWithoutInterceptor( 4366 *hreceiver, index, continue_search); 4367 } 4368 4369 4370 PropertyAttributes JSObject::GetElementAttributeWithoutInterceptor( 4371 JSReceiver* receiver, uint32_t index, bool continue_search) { 4372 PropertyAttributes attr = GetElementsAccessor()->GetAttributes( 4373 receiver, this, index); 4374 if (attr != ABSENT) return attr; 4375 4376 // Handle [] on String objects. 4377 if (IsStringObjectWithCharacterAt(index)) { 4378 return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); 4379 } 4380 4381 if (!continue_search) return ABSENT; 4382 4383 Object* pt = GetPrototype(); 4384 if (pt->IsJSProxy()) { 4385 // We need to follow the spec and simulate a call to [[GetOwnProperty]]. 4386 return JSProxy::cast(pt)->GetElementAttributeWithHandler(receiver, index); 4387 } 4388 if (pt->IsNull()) return ABSENT; 4389 return JSObject::cast(pt)->GetElementAttributeWithReceiver( 4390 receiver, index, true); 4391 } 4392 4393 4394 MaybeObject* NormalizedMapCache::Get(JSObject* obj, 4395 PropertyNormalizationMode mode) { 4396 Isolate* isolate = obj->GetIsolate(); 4397 Map* fast = obj->map(); 4398 int index = fast->Hash() % kEntries; 4399 Object* result = get(index); 4400 if (result->IsMap() && 4401 Map::cast(result)->EquivalentToForNormalization(fast, mode)) { 4402 #ifdef VERIFY_HEAP 4403 if (FLAG_verify_heap) { 4404 Map::cast(result)->SharedMapVerify(); 4405 } 4406 #endif 4407 #ifdef DEBUG 4408 if (FLAG_enable_slow_asserts) { 4409 // The cached map should match newly created normalized map bit-by-bit, 4410 // except for the code cache, which can contain some ics which can be 4411 // applied to the shared map. 4412 Object* fresh; 4413 MaybeObject* maybe_fresh = 4414 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP); 4415 if (maybe_fresh->ToObject(&fresh)) { 4416 ASSERT(memcmp(Map::cast(fresh)->address(), 4417 Map::cast(result)->address(), 4418 Map::kCodeCacheOffset) == 0); 4419 STATIC_ASSERT(Map::kDependentCodeOffset == 4420 Map::kCodeCacheOffset + kPointerSize); 4421 int offset = Map::kDependentCodeOffset + kPointerSize; 4422 ASSERT(memcmp(Map::cast(fresh)->address() + offset, 4423 Map::cast(result)->address() + offset, 4424 Map::kSize - offset) == 0); 4425 } 4426 } 4427 #endif 4428 return result; 4429 } 4430 4431 { MaybeObject* maybe_result = 4432 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP); 4433 if (!maybe_result->ToObject(&result)) return maybe_result; 4434 } 4435 ASSERT(Map::cast(result)->is_dictionary_map()); 4436 set(index, result); 4437 isolate->counters()->normalized_maps()->Increment(); 4438 4439 return result; 4440 } 4441 4442 4443 void NormalizedMapCache::Clear() { 4444 int entries = length(); 4445 for (int i = 0; i != entries; i++) { 4446 set_undefined(i); 4447 } 4448 } 4449 4450 4451 void JSObject::UpdateMapCodeCache(Handle<JSObject> object, 4452 Handle<Name> name, 4453 Handle<Code> code) { 4454 Isolate* isolate = object->GetIsolate(); 4455 CALL_HEAP_FUNCTION_VOID(isolate, 4456 object->UpdateMapCodeCache(*name, *code)); 4457 } 4458 4459 4460 MaybeObject* JSObject::UpdateMapCodeCache(Name* name, Code* code) { 4461 if (map()->is_shared()) { 4462 // Fast case maps are never marked as shared. 4463 ASSERT(!HasFastProperties()); 4464 // Replace the map with an identical copy that can be safely modified. 4465 Object* obj; 4466 { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES, 4467 UNIQUE_NORMALIZED_MAP); 4468 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 4469 } 4470 GetIsolate()->counters()->normalized_maps()->Increment(); 4471 4472 set_map(Map::cast(obj)); 4473 } 4474 return map()->UpdateCodeCache(name, code); 4475 } 4476 4477 4478 void JSObject::NormalizeProperties(Handle<JSObject> object, 4479 PropertyNormalizationMode mode, 4480 int expected_additional_properties) { 4481 CALL_HEAP_FUNCTION_VOID(object->GetIsolate(), 4482 object->NormalizeProperties( 4483 mode, expected_additional_properties)); 4484 } 4485 4486 4487 MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, 4488 int expected_additional_properties) { 4489 if (!HasFastProperties()) return this; 4490 4491 // The global object is always normalized. 4492 ASSERT(!IsGlobalObject()); 4493 // JSGlobalProxy must never be normalized 4494 ASSERT(!IsJSGlobalProxy()); 4495 4496 Map* map_of_this = map(); 4497 4498 // Allocate new content. 4499 int real_size = map_of_this->NumberOfOwnDescriptors(); 4500 int property_count = real_size; 4501 if (expected_additional_properties > 0) { 4502 property_count += expected_additional_properties; 4503 } else { 4504 property_count += 2; // Make space for two more properties. 4505 } 4506 NameDictionary* dictionary; 4507 MaybeObject* maybe_dictionary = 4508 NameDictionary::Allocate(GetHeap(), property_count); 4509 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 4510 4511 DescriptorArray* descs = map_of_this->instance_descriptors(); 4512 for (int i = 0; i < real_size; i++) { 4513 PropertyDetails details = descs->GetDetails(i); 4514 switch (details.type()) { 4515 case CONSTANT: { 4516 PropertyDetails d = PropertyDetails( 4517 details.attributes(), NORMAL, i + 1); 4518 Object* value = descs->GetConstant(i); 4519 MaybeObject* maybe_dictionary = 4520 dictionary->Add(descs->GetKey(i), value, d); 4521 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 4522 break; 4523 } 4524 case FIELD: { 4525 PropertyDetails d = 4526 PropertyDetails(details.attributes(), NORMAL, i + 1); 4527 Object* value = RawFastPropertyAt(descs->GetFieldIndex(i)); 4528 MaybeObject* maybe_dictionary = 4529 dictionary->Add(descs->GetKey(i), value, d); 4530 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 4531 break; 4532 } 4533 case CALLBACKS: { 4534 Object* value = descs->GetCallbacksObject(i); 4535 PropertyDetails d = PropertyDetails( 4536 details.attributes(), CALLBACKS, i + 1); 4537 MaybeObject* maybe_dictionary = 4538 dictionary->Add(descs->GetKey(i), value, d); 4539 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 4540 break; 4541 } 4542 case INTERCEPTOR: 4543 break; 4544 case HANDLER: 4545 case NORMAL: 4546 case TRANSITION: 4547 case NONEXISTENT: 4548 UNREACHABLE(); 4549 break; 4550 } 4551 } 4552 4553 Heap* current_heap = GetHeap(); 4554 4555 // Copy the next enumeration index from instance descriptor. 4556 dictionary->SetNextEnumerationIndex(real_size + 1); 4557 4558 Map* new_map; 4559 MaybeObject* maybe_map = 4560 current_heap->isolate()->context()->native_context()-> 4561 normalized_map_cache()->Get(this, mode); 4562 if (!maybe_map->To(&new_map)) return maybe_map; 4563 ASSERT(new_map->is_dictionary_map()); 4564 4565 // We have now successfully allocated all the necessary objects. 4566 // Changes can now be made with the guarantee that all of them take effect. 4567 4568 // Resize the object in the heap if necessary. 4569 int new_instance_size = new_map->instance_size(); 4570 int instance_size_delta = map_of_this->instance_size() - new_instance_size; 4571 ASSERT(instance_size_delta >= 0); 4572 current_heap->CreateFillerObjectAt(this->address() + new_instance_size, 4573 instance_size_delta); 4574 if (Marking::IsBlack(Marking::MarkBitFrom(this))) { 4575 MemoryChunk::IncrementLiveBytesFromMutator(this->address(), 4576 -instance_size_delta); 4577 } 4578 4579 set_map(new_map); 4580 map_of_this->NotifyLeafMapLayoutChange(); 4581 4582 set_properties(dictionary); 4583 4584 current_heap->isolate()->counters()->props_to_dictionary()->Increment(); 4585 4586 #ifdef DEBUG 4587 if (FLAG_trace_normalization) { 4588 PrintF("Object properties have been normalized:\n"); 4589 Print(); 4590 } 4591 #endif 4592 return this; 4593 } 4594 4595 4596 void JSObject::TransformToFastProperties(Handle<JSObject> object, 4597 int unused_property_fields) { 4598 CALL_HEAP_FUNCTION_VOID( 4599 object->GetIsolate(), 4600 object->TransformToFastProperties(unused_property_fields)); 4601 } 4602 4603 4604 MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) { 4605 if (HasFastProperties()) return this; 4606 ASSERT(!IsGlobalObject()); 4607 return property_dictionary()-> 4608 TransformPropertiesToFastFor(this, unused_property_fields); 4609 } 4610 4611 4612 static MUST_USE_RESULT MaybeObject* CopyFastElementsToDictionary( 4613 Isolate* isolate, 4614 FixedArrayBase* array, 4615 int length, 4616 SeededNumberDictionary* dictionary) { 4617 Heap* heap = isolate->heap(); 4618 bool has_double_elements = array->IsFixedDoubleArray(); 4619 for (int i = 0; i < length; i++) { 4620 Object* value = NULL; 4621 if (has_double_elements) { 4622 FixedDoubleArray* double_array = FixedDoubleArray::cast(array); 4623 if (double_array->is_the_hole(i)) { 4624 value = isolate->heap()->the_hole_value(); 4625 } else { 4626 // Objects must be allocated in the old object space, since the 4627 // overall number of HeapNumbers needed for the conversion might 4628 // exceed the capacity of new space, and we would fail repeatedly 4629 // trying to convert the FixedDoubleArray. 4630 MaybeObject* maybe_value_object = 4631 heap->AllocateHeapNumber(double_array->get_scalar(i), TENURED); 4632 if (!maybe_value_object->ToObject(&value)) return maybe_value_object; 4633 } 4634 } else { 4635 value = FixedArray::cast(array)->get(i); 4636 } 4637 if (!value->IsTheHole()) { 4638 PropertyDetails details = PropertyDetails(NONE, NORMAL, 0); 4639 MaybeObject* maybe_result = 4640 dictionary->AddNumberEntry(i, value, details); 4641 if (!maybe_result->To(&dictionary)) return maybe_result; 4642 } 4643 } 4644 return dictionary; 4645 } 4646 4647 4648 Handle<SeededNumberDictionary> JSObject::NormalizeElements( 4649 Handle<JSObject> object) { 4650 CALL_HEAP_FUNCTION(object->GetIsolate(), 4651 object->NormalizeElements(), 4652 SeededNumberDictionary); 4653 } 4654 4655 4656 MaybeObject* JSObject::NormalizeElements() { 4657 ASSERT(!HasExternalArrayElements()); 4658 4659 // Find the backing store. 4660 FixedArrayBase* array = FixedArrayBase::cast(elements()); 4661 Map* old_map = array->map(); 4662 bool is_arguments = 4663 (old_map == old_map->GetHeap()->non_strict_arguments_elements_map()); 4664 if (is_arguments) { 4665 array = FixedArrayBase::cast(FixedArray::cast(array)->get(1)); 4666 } 4667 if (array->IsDictionary()) return array; 4668 4669 ASSERT(HasFastSmiOrObjectElements() || 4670 HasFastDoubleElements() || 4671 HasFastArgumentsElements()); 4672 // Compute the effective length and allocate a new backing store. 4673 int length = IsJSArray() 4674 ? Smi::cast(JSArray::cast(this)->length())->value() 4675 : array->length(); 4676 int old_capacity = 0; 4677 int used_elements = 0; 4678 GetElementsCapacityAndUsage(&old_capacity, &used_elements); 4679 SeededNumberDictionary* dictionary; 4680 MaybeObject* maybe_dictionary = 4681 SeededNumberDictionary::Allocate(GetHeap(), used_elements); 4682 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 4683 4684 maybe_dictionary = CopyFastElementsToDictionary( 4685 GetIsolate(), array, length, dictionary); 4686 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 4687 4688 // Switch to using the dictionary as the backing storage for elements. 4689 if (is_arguments) { 4690 FixedArray::cast(elements())->set(1, dictionary); 4691 } else { 4692 // Set the new map first to satify the elements type assert in 4693 // set_elements(). 4694 Map* new_map; 4695 MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(), 4696 DICTIONARY_ELEMENTS); 4697 if (!maybe->To(&new_map)) return maybe; 4698 set_map(new_map); 4699 set_elements(dictionary); 4700 } 4701 4702 old_map->GetHeap()->isolate()->counters()->elements_to_dictionary()-> 4703 Increment(); 4704 4705 #ifdef DEBUG 4706 if (FLAG_trace_normalization) { 4707 PrintF("Object elements have been normalized:\n"); 4708 Print(); 4709 } 4710 #endif 4711 4712 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 4713 return dictionary; 4714 } 4715 4716 4717 Smi* JSReceiver::GenerateIdentityHash() { 4718 Isolate* isolate = GetIsolate(); 4719 4720 int hash_value; 4721 int attempts = 0; 4722 do { 4723 // Generate a random 32-bit hash value but limit range to fit 4724 // within a smi. 4725 hash_value = V8::RandomPrivate(isolate) & Smi::kMaxValue; 4726 attempts++; 4727 } while (hash_value == 0 && attempts < 30); 4728 hash_value = hash_value != 0 ? hash_value : 1; // never return 0 4729 4730 return Smi::FromInt(hash_value); 4731 } 4732 4733 4734 MaybeObject* JSObject::SetIdentityHash(Smi* hash, CreationFlag flag) { 4735 MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_string(), 4736 hash); 4737 if (maybe->IsFailure()) return maybe; 4738 return this; 4739 } 4740 4741 4742 int JSObject::GetIdentityHash(Handle<JSObject> obj) { 4743 CALL_AND_RETRY_OR_DIE(obj->GetIsolate(), 4744 obj->GetIdentityHash(ALLOW_CREATION), 4745 return Smi::cast(__object__)->value(), 4746 return 0); 4747 } 4748 4749 4750 MaybeObject* JSObject::GetIdentityHash(CreationFlag flag) { 4751 Object* stored_value = GetHiddenProperty(GetHeap()->identity_hash_string()); 4752 if (stored_value->IsSmi()) return stored_value; 4753 4754 // Do not generate permanent identity hash code if not requested. 4755 if (flag == OMIT_CREATION) return GetHeap()->undefined_value(); 4756 4757 Smi* hash = GenerateIdentityHash(); 4758 MaybeObject* result = SetHiddenProperty(GetHeap()->identity_hash_string(), 4759 hash); 4760 if (result->IsFailure()) return result; 4761 if (result->ToObjectUnchecked()->IsUndefined()) { 4762 // Trying to get hash of detached proxy. 4763 return Smi::FromInt(0); 4764 } 4765 return hash; 4766 } 4767 4768 4769 MaybeObject* JSProxy::GetIdentityHash(CreationFlag flag) { 4770 Object* hash = this->hash(); 4771 if (!hash->IsSmi() && flag == ALLOW_CREATION) { 4772 hash = GenerateIdentityHash(); 4773 set_hash(hash); 4774 } 4775 return hash; 4776 } 4777 4778 4779 Object* JSObject::GetHiddenProperty(Name* key) { 4780 ASSERT(key->IsUniqueName()); 4781 if (IsJSGlobalProxy()) { 4782 // For a proxy, use the prototype as target object. 4783 Object* proxy_parent = GetPrototype(); 4784 // If the proxy is detached, return undefined. 4785 if (proxy_parent->IsNull()) return GetHeap()->the_hole_value(); 4786 ASSERT(proxy_parent->IsJSGlobalObject()); 4787 return JSObject::cast(proxy_parent)->GetHiddenProperty(key); 4788 } 4789 ASSERT(!IsJSGlobalProxy()); 4790 MaybeObject* hidden_lookup = 4791 GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE); 4792 Object* inline_value = hidden_lookup->ToObjectUnchecked(); 4793 4794 if (inline_value->IsSmi()) { 4795 // Handle inline-stored identity hash. 4796 if (key == GetHeap()->identity_hash_string()) { 4797 return inline_value; 4798 } else { 4799 return GetHeap()->the_hole_value(); 4800 } 4801 } 4802 4803 if (inline_value->IsUndefined()) return GetHeap()->the_hole_value(); 4804 4805 ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value); 4806 Object* entry = hashtable->Lookup(key); 4807 return entry; 4808 } 4809 4810 4811 Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj, 4812 Handle<Name> key, 4813 Handle<Object> value) { 4814 CALL_HEAP_FUNCTION(obj->GetIsolate(), 4815 obj->SetHiddenProperty(*key, *value), 4816 Object); 4817 } 4818 4819 4820 MaybeObject* JSObject::SetHiddenProperty(Name* key, Object* value) { 4821 ASSERT(key->IsUniqueName()); 4822 if (IsJSGlobalProxy()) { 4823 // For a proxy, use the prototype as target object. 4824 Object* proxy_parent = GetPrototype(); 4825 // If the proxy is detached, return undefined. 4826 if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); 4827 ASSERT(proxy_parent->IsJSGlobalObject()); 4828 return JSObject::cast(proxy_parent)->SetHiddenProperty(key, value); 4829 } 4830 ASSERT(!IsJSGlobalProxy()); 4831 MaybeObject* hidden_lookup = 4832 GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE); 4833 Object* inline_value = hidden_lookup->ToObjectUnchecked(); 4834 4835 // If there is no backing store yet, store the identity hash inline. 4836 if (value->IsSmi() && 4837 key == GetHeap()->identity_hash_string() && 4838 (inline_value->IsUndefined() || inline_value->IsSmi())) { 4839 return SetHiddenPropertiesHashTable(value); 4840 } 4841 4842 hidden_lookup = GetHiddenPropertiesHashTable(CREATE_NEW_IF_ABSENT); 4843 ObjectHashTable* hashtable; 4844 if (!hidden_lookup->To(&hashtable)) return hidden_lookup; 4845 4846 // If it was found, check if the key is already in the dictionary. 4847 MaybeObject* insert_result = hashtable->Put(key, value); 4848 ObjectHashTable* new_table; 4849 if (!insert_result->To(&new_table)) return insert_result; 4850 if (new_table != hashtable) { 4851 // If adding the key expanded the dictionary (i.e., Add returned a new 4852 // dictionary), store it back to the object. 4853 MaybeObject* store_result = SetHiddenPropertiesHashTable(new_table); 4854 if (store_result->IsFailure()) return store_result; 4855 } 4856 // Return this to mark success. 4857 return this; 4858 } 4859 4860 4861 void JSObject::DeleteHiddenProperty(Name* key) { 4862 ASSERT(key->IsUniqueName()); 4863 if (IsJSGlobalProxy()) { 4864 // For a proxy, use the prototype as target object. 4865 Object* proxy_parent = GetPrototype(); 4866 // If the proxy is detached, return immediately. 4867 if (proxy_parent->IsNull()) return; 4868 ASSERT(proxy_parent->IsJSGlobalObject()); 4869 JSObject::cast(proxy_parent)->DeleteHiddenProperty(key); 4870 return; 4871 } 4872 ASSERT(!IsJSGlobalProxy()); 4873 MaybeObject* hidden_lookup = 4874 GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE); 4875 Object* inline_value = hidden_lookup->ToObjectUnchecked(); 4876 4877 // We never delete (inline-stored) identity hashes. 4878 ASSERT(key != GetHeap()->identity_hash_string()); 4879 if (inline_value->IsUndefined() || inline_value->IsSmi()) return; 4880 4881 ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value); 4882 MaybeObject* delete_result = hashtable->Put(key, GetHeap()->the_hole_value()); 4883 USE(delete_result); 4884 ASSERT(!delete_result->IsFailure()); // Delete does not cause GC. 4885 } 4886 4887 4888 bool JSObject::HasHiddenProperties() { 4889 return GetPropertyAttributePostInterceptor(this, 4890 GetHeap()->hidden_string(), 4891 false) != ABSENT; 4892 } 4893 4894 4895 MaybeObject* JSObject::GetHiddenPropertiesHashTable( 4896 InitializeHiddenProperties init_option) { 4897 ASSERT(!IsJSGlobalProxy()); 4898 Object* inline_value; 4899 if (HasFastProperties()) { 4900 // If the object has fast properties, check whether the first slot 4901 // in the descriptor array matches the hidden string. Since the 4902 // hidden strings hash code is zero (and no other name has hash 4903 // code zero) it will always occupy the first entry if present. 4904 DescriptorArray* descriptors = this->map()->instance_descriptors(); 4905 if (descriptors->number_of_descriptors() > 0) { 4906 int sorted_index = descriptors->GetSortedKeyIndex(0); 4907 if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_string() && 4908 sorted_index < map()->NumberOfOwnDescriptors()) { 4909 ASSERT(descriptors->GetType(sorted_index) == FIELD); 4910 MaybeObject* maybe_value = this->FastPropertyAt( 4911 descriptors->GetDetails(sorted_index).representation(), 4912 descriptors->GetFieldIndex(sorted_index)); 4913 if (!maybe_value->To(&inline_value)) return maybe_value; 4914 } else { 4915 inline_value = GetHeap()->undefined_value(); 4916 } 4917 } else { 4918 inline_value = GetHeap()->undefined_value(); 4919 } 4920 } else { 4921 PropertyAttributes attributes; 4922 // You can't install a getter on a property indexed by the hidden string, 4923 // so we can be sure that GetLocalPropertyPostInterceptor returns a real 4924 // object. 4925 inline_value = 4926 GetLocalPropertyPostInterceptor(this, 4927 GetHeap()->hidden_string(), 4928 &attributes)->ToObjectUnchecked(); 4929 } 4930 4931 if (init_option == ONLY_RETURN_INLINE_VALUE || 4932 inline_value->IsHashTable()) { 4933 return inline_value; 4934 } 4935 4936 ObjectHashTable* hashtable; 4937 static const int kInitialCapacity = 4; 4938 MaybeObject* maybe_obj = 4939 ObjectHashTable::Allocate(GetHeap(), 4940 kInitialCapacity, 4941 ObjectHashTable::USE_CUSTOM_MINIMUM_CAPACITY); 4942 if (!maybe_obj->To<ObjectHashTable>(&hashtable)) return maybe_obj; 4943 4944 if (inline_value->IsSmi()) { 4945 // We were storing the identity hash inline and now allocated an actual 4946 // dictionary. Put the identity hash into the new dictionary. 4947 MaybeObject* insert_result = 4948 hashtable->Put(GetHeap()->identity_hash_string(), inline_value); 4949 ObjectHashTable* new_table; 4950 if (!insert_result->To(&new_table)) return insert_result; 4951 // We expect no resizing for the first insert. 4952 ASSERT_EQ(hashtable, new_table); 4953 } 4954 4955 MaybeObject* store_result = 4956 SetPropertyPostInterceptor(GetHeap()->hidden_string(), 4957 hashtable, 4958 DONT_ENUM, 4959 kNonStrictMode, 4960 OMIT_EXTENSIBILITY_CHECK, 4961 FORCE_FIELD); 4962 if (store_result->IsFailure()) return store_result; 4963 return hashtable; 4964 } 4965 4966 4967 MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) { 4968 ASSERT(!IsJSGlobalProxy()); 4969 // We can store the identity hash inline iff there is no backing store 4970 // for hidden properties yet. 4971 ASSERT(HasHiddenProperties() != value->IsSmi()); 4972 if (HasFastProperties()) { 4973 // If the object has fast properties, check whether the first slot 4974 // in the descriptor array matches the hidden string. Since the 4975 // hidden strings hash code is zero (and no other name has hash 4976 // code zero) it will always occupy the first entry if present. 4977 DescriptorArray* descriptors = this->map()->instance_descriptors(); 4978 if (descriptors->number_of_descriptors() > 0) { 4979 int sorted_index = descriptors->GetSortedKeyIndex(0); 4980 if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_string() && 4981 sorted_index < map()->NumberOfOwnDescriptors()) { 4982 ASSERT(descriptors->GetType(sorted_index) == FIELD); 4983 FastPropertyAtPut(descriptors->GetFieldIndex(sorted_index), value); 4984 return this; 4985 } 4986 } 4987 } 4988 MaybeObject* store_result = 4989 SetPropertyPostInterceptor(GetHeap()->hidden_string(), 4990 value, 4991 DONT_ENUM, 4992 kNonStrictMode, 4993 OMIT_EXTENSIBILITY_CHECK, 4994 FORCE_FIELD); 4995 if (store_result->IsFailure()) return store_result; 4996 return this; 4997 } 4998 4999 5000 Handle<Object> JSObject::DeletePropertyPostInterceptor(Handle<JSObject> object, 5001 Handle<Name> name, 5002 DeleteMode mode) { 5003 // Check local property, ignore interceptor. 5004 Isolate* isolate = object->GetIsolate(); 5005 LookupResult result(isolate); 5006 object->LocalLookupRealNamedProperty(*name, &result); 5007 if (!result.IsFound()) return isolate->factory()->true_value(); 5008 5009 // Normalize object if needed. 5010 NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0); 5011 5012 return DeleteNormalizedProperty(object, name, mode); 5013 } 5014 5015 5016 Handle<Object> JSObject::DeletePropertyWithInterceptor(Handle<JSObject> object, 5017 Handle<Name> name) { 5018 Isolate* isolate = object->GetIsolate(); 5019 5020 // TODO(rossberg): Support symbols in the API. 5021 if (name->IsSymbol()) return isolate->factory()->false_value(); 5022 5023 Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor()); 5024 if (!interceptor->deleter()->IsUndefined()) { 5025 v8::NamedPropertyDeleter deleter = 5026 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter()); 5027 LOG(isolate, 5028 ApiNamedPropertyAccess("interceptor-named-delete", *object, *name)); 5029 PropertyCallbackArguments args( 5030 isolate, interceptor->data(), *object, *object); 5031 v8::Handle<v8::Boolean> result = 5032 args.Call(deleter, v8::Utils::ToLocal(Handle<String>::cast(name))); 5033 RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); 5034 if (!result.IsEmpty()) { 5035 ASSERT(result->IsBoolean()); 5036 Handle<Object> result_internal = v8::Utils::OpenHandle(*result); 5037 result_internal->VerifyApiCallResultType(); 5038 // Rebox CustomArguments::kReturnValueOffset before returning. 5039 return handle(*result_internal, isolate); 5040 } 5041 } 5042 Handle<Object> result = 5043 DeletePropertyPostInterceptor(object, name, NORMAL_DELETION); 5044 RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); 5045 return result; 5046 } 5047 5048 5049 MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) { 5050 Isolate* isolate = GetIsolate(); 5051 Heap* heap = isolate->heap(); 5052 // Make sure that the top context does not change when doing 5053 // callbacks or interceptor calls. 5054 AssertNoContextChange ncc; 5055 HandleScope scope(isolate); 5056 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 5057 if (interceptor->deleter()->IsUndefined()) return heap->false_value(); 5058 v8::IndexedPropertyDeleter deleter = 5059 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter()); 5060 Handle<JSObject> this_handle(this); 5061 LOG(isolate, 5062 ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index)); 5063 PropertyCallbackArguments args(isolate, interceptor->data(), this, this); 5064 v8::Handle<v8::Boolean> result = args.Call(deleter, index); 5065 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 5066 if (!result.IsEmpty()) { 5067 ASSERT(result->IsBoolean()); 5068 Handle<Object> result_internal = v8::Utils::OpenHandle(*result); 5069 result_internal->VerifyApiCallResultType(); 5070 return *result_internal; 5071 } 5072 MaybeObject* raw_result = this_handle->GetElementsAccessor()->Delete( 5073 *this_handle, 5074 index, 5075 NORMAL_DELETION); 5076 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 5077 return raw_result; 5078 } 5079 5080 5081 Handle<Object> JSObject::DeleteElement(Handle<JSObject> obj, 5082 uint32_t index, 5083 DeleteMode mode) { 5084 CALL_HEAP_FUNCTION(obj->GetIsolate(), 5085 obj->DeleteElement(index, mode), 5086 Object); 5087 } 5088 5089 5090 MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { 5091 Isolate* isolate = GetIsolate(); 5092 // Check access rights if needed. 5093 if (IsAccessCheckNeeded() && 5094 !isolate->MayIndexedAccess(this, index, v8::ACCESS_DELETE)) { 5095 isolate->ReportFailedAccessCheck(this, v8::ACCESS_DELETE); 5096 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 5097 return isolate->heap()->false_value(); 5098 } 5099 5100 if (IsStringObjectWithCharacterAt(index)) { 5101 if (mode == STRICT_DELETION) { 5102 // Deleting a non-configurable property in strict mode. 5103 HandleScope scope(isolate); 5104 Handle<Object> holder(this, isolate); 5105 Handle<Object> name = isolate->factory()->NewNumberFromUint(index); 5106 Handle<Object> args[2] = { name, holder }; 5107 Handle<Object> error = 5108 isolate->factory()->NewTypeError("strict_delete_property", 5109 HandleVector(args, 2)); 5110 return isolate->Throw(*error); 5111 } 5112 return isolate->heap()->false_value(); 5113 } 5114 5115 if (IsJSGlobalProxy()) { 5116 Object* proto = GetPrototype(); 5117 if (proto->IsNull()) return isolate->heap()->false_value(); 5118 ASSERT(proto->IsJSGlobalObject()); 5119 return JSGlobalObject::cast(proto)->DeleteElement(index, mode); 5120 } 5121 5122 // From this point on everything needs to be handlified. 5123 HandleScope scope(isolate); 5124 Handle<JSObject> self(this); 5125 5126 Handle<Object> old_value; 5127 bool should_enqueue_change_record = false; 5128 if (FLAG_harmony_observation && self->map()->is_observed()) { 5129 should_enqueue_change_record = self->HasLocalElement(index); 5130 if (should_enqueue_change_record) { 5131 old_value = self->GetLocalElementAccessorPair(index) != NULL 5132 ? Handle<Object>::cast(isolate->factory()->the_hole_value()) 5133 : Object::GetElement(self, index); 5134 } 5135 } 5136 5137 MaybeObject* result; 5138 // Skip interceptor if forcing deletion. 5139 if (self->HasIndexedInterceptor() && mode != FORCE_DELETION) { 5140 result = self->DeleteElementWithInterceptor(index); 5141 } else { 5142 result = self->GetElementsAccessor()->Delete(*self, index, mode); 5143 } 5144 5145 Handle<Object> hresult; 5146 if (!result->ToHandle(&hresult, isolate)) return result; 5147 5148 if (should_enqueue_change_record && !self->HasLocalElement(index)) { 5149 Handle<String> name = isolate->factory()->Uint32ToString(index); 5150 EnqueueChangeRecord(self, "deleted", name, old_value); 5151 } 5152 5153 return *hresult; 5154 } 5155 5156 5157 Handle<Object> JSObject::DeleteProperty(Handle<JSObject> object, 5158 Handle<Name> name, 5159 DeleteMode mode) { 5160 Isolate* isolate = object->GetIsolate(); 5161 // ECMA-262, 3rd, 8.6.2.5 5162 ASSERT(name->IsName()); 5163 5164 // Check access rights if needed. 5165 if (object->IsAccessCheckNeeded() && 5166 !isolate->MayNamedAccess(*object, *name, v8::ACCESS_DELETE)) { 5167 isolate->ReportFailedAccessCheck(*object, v8::ACCESS_DELETE); 5168 RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); 5169 return isolate->factory()->false_value(); 5170 } 5171 5172 if (object->IsJSGlobalProxy()) { 5173 Object* proto = object->GetPrototype(); 5174 if (proto->IsNull()) return isolate->factory()->false_value(); 5175 ASSERT(proto->IsJSGlobalObject()); 5176 return JSGlobalObject::DeleteProperty( 5177 handle(JSGlobalObject::cast(proto)), name, mode); 5178 } 5179 5180 uint32_t index = 0; 5181 if (name->AsArrayIndex(&index)) { 5182 return DeleteElement(object, index, mode); 5183 } 5184 5185 LookupResult lookup(isolate); 5186 object->LocalLookup(*name, &lookup, true); 5187 if (!lookup.IsFound()) return isolate->factory()->true_value(); 5188 // Ignore attributes if forcing a deletion. 5189 if (lookup.IsDontDelete() && mode != FORCE_DELETION) { 5190 if (mode == STRICT_DELETION) { 5191 // Deleting a non-configurable property in strict mode. 5192 Handle<Object> args[2] = { name, object }; 5193 Handle<Object> error = isolate->factory()->NewTypeError( 5194 "strict_delete_property", HandleVector(args, ARRAY_SIZE(args))); 5195 isolate->Throw(*error); 5196 return Handle<Object>(); 5197 } 5198 return isolate->factory()->false_value(); 5199 } 5200 5201 Handle<Object> old_value = isolate->factory()->the_hole_value(); 5202 bool is_observed = FLAG_harmony_observation && object->map()->is_observed(); 5203 if (is_observed && lookup.IsDataProperty()) { 5204 old_value = Object::GetProperty(object, name); 5205 } 5206 Handle<Object> result; 5207 5208 // Check for interceptor. 5209 if (lookup.IsInterceptor()) { 5210 // Skip interceptor if forcing a deletion. 5211 if (mode == FORCE_DELETION) { 5212 result = DeletePropertyPostInterceptor(object, name, mode); 5213 } else { 5214 result = DeletePropertyWithInterceptor(object, name); 5215 } 5216 } else { 5217 // Normalize object if needed. 5218 NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0); 5219 // Make sure the properties are normalized before removing the entry. 5220 result = DeleteNormalizedProperty(object, name, mode); 5221 } 5222 5223 if (is_observed && !object->HasLocalProperty(*name)) { 5224 EnqueueChangeRecord(object, "deleted", name, old_value); 5225 } 5226 5227 return result; 5228 } 5229 5230 5231 Handle<Object> JSReceiver::DeleteElement(Handle<JSReceiver> object, 5232 uint32_t index, 5233 DeleteMode mode) { 5234 if (object->IsJSProxy()) { 5235 return JSProxy::DeleteElementWithHandler( 5236 Handle<JSProxy>::cast(object), index, mode); 5237 } 5238 return JSObject::DeleteElement(Handle<JSObject>::cast(object), index, mode); 5239 } 5240 5241 5242 Handle<Object> JSReceiver::DeleteProperty(Handle<JSReceiver> object, 5243 Handle<Name> name, 5244 DeleteMode mode) { 5245 if (object->IsJSProxy()) { 5246 return JSProxy::DeletePropertyWithHandler( 5247 Handle<JSProxy>::cast(object), name, mode); 5248 } 5249 return JSObject::DeleteProperty(Handle<JSObject>::cast(object), name, mode); 5250 } 5251 5252 5253 bool JSObject::ReferencesObjectFromElements(FixedArray* elements, 5254 ElementsKind kind, 5255 Object* object) { 5256 ASSERT(IsFastObjectElementsKind(kind) || 5257 kind == DICTIONARY_ELEMENTS); 5258 if (IsFastObjectElementsKind(kind)) { 5259 int length = IsJSArray() 5260 ? Smi::cast(JSArray::cast(this)->length())->value() 5261 : elements->length(); 5262 for (int i = 0; i < length; ++i) { 5263 Object* element = elements->get(i); 5264 if (!element->IsTheHole() && element == object) return true; 5265 } 5266 } else { 5267 Object* key = 5268 SeededNumberDictionary::cast(elements)->SlowReverseLookup(object); 5269 if (!key->IsUndefined()) return true; 5270 } 5271 return false; 5272 } 5273 5274 5275 // Check whether this object references another object. 5276 bool JSObject::ReferencesObject(Object* obj) { 5277 Map* map_of_this = map(); 5278 Heap* heap = GetHeap(); 5279 DisallowHeapAllocation no_allocation; 5280 5281 // Is the object the constructor for this object? 5282 if (map_of_this->constructor() == obj) { 5283 return true; 5284 } 5285 5286 // Is the object the prototype for this object? 5287 if (map_of_this->prototype() == obj) { 5288 return true; 5289 } 5290 5291 // Check if the object is among the named properties. 5292 Object* key = SlowReverseLookup(obj); 5293 if (!key->IsUndefined()) { 5294 return true; 5295 } 5296 5297 // Check if the object is among the indexed properties. 5298 ElementsKind kind = GetElementsKind(); 5299 switch (kind) { 5300 case EXTERNAL_PIXEL_ELEMENTS: 5301 case EXTERNAL_BYTE_ELEMENTS: 5302 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 5303 case EXTERNAL_SHORT_ELEMENTS: 5304 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 5305 case EXTERNAL_INT_ELEMENTS: 5306 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 5307 case EXTERNAL_FLOAT_ELEMENTS: 5308 case EXTERNAL_DOUBLE_ELEMENTS: 5309 case FAST_DOUBLE_ELEMENTS: 5310 case FAST_HOLEY_DOUBLE_ELEMENTS: 5311 // Raw pixels and external arrays do not reference other 5312 // objects. 5313 break; 5314 case FAST_SMI_ELEMENTS: 5315 case FAST_HOLEY_SMI_ELEMENTS: 5316 break; 5317 case FAST_ELEMENTS: 5318 case FAST_HOLEY_ELEMENTS: 5319 case DICTIONARY_ELEMENTS: { 5320 FixedArray* elements = FixedArray::cast(this->elements()); 5321 if (ReferencesObjectFromElements(elements, kind, obj)) return true; 5322 break; 5323 } 5324 case NON_STRICT_ARGUMENTS_ELEMENTS: { 5325 FixedArray* parameter_map = FixedArray::cast(elements()); 5326 // Check the mapped parameters. 5327 int length = parameter_map->length(); 5328 for (int i = 2; i < length; ++i) { 5329 Object* value = parameter_map->get(i); 5330 if (!value->IsTheHole() && value == obj) return true; 5331 } 5332 // Check the arguments. 5333 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 5334 kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : 5335 FAST_HOLEY_ELEMENTS; 5336 if (ReferencesObjectFromElements(arguments, kind, obj)) return true; 5337 break; 5338 } 5339 } 5340 5341 // For functions check the context. 5342 if (IsJSFunction()) { 5343 // Get the constructor function for arguments array. 5344 JSObject* arguments_boilerplate = 5345 heap->isolate()->context()->native_context()-> 5346 arguments_boilerplate(); 5347 JSFunction* arguments_function = 5348 JSFunction::cast(arguments_boilerplate->map()->constructor()); 5349 5350 // Get the context and don't check if it is the native context. 5351 JSFunction* f = JSFunction::cast(this); 5352 Context* context = f->context(); 5353 if (context->IsNativeContext()) { 5354 return false; 5355 } 5356 5357 // Check the non-special context slots. 5358 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) { 5359 // Only check JS objects. 5360 if (context->get(i)->IsJSObject()) { 5361 JSObject* ctxobj = JSObject::cast(context->get(i)); 5362 // If it is an arguments array check the content. 5363 if (ctxobj->map()->constructor() == arguments_function) { 5364 if (ctxobj->ReferencesObject(obj)) { 5365 return true; 5366 } 5367 } else if (ctxobj == obj) { 5368 return true; 5369 } 5370 } 5371 } 5372 5373 // Check the context extension (if any) if it can have references. 5374 if (context->has_extension() && !context->IsCatchContext()) { 5375 return JSObject::cast(context->extension())->ReferencesObject(obj); 5376 } 5377 } 5378 5379 // No references to object. 5380 return false; 5381 } 5382 5383 5384 Handle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { 5385 CALL_HEAP_FUNCTION(object->GetIsolate(), object->PreventExtensions(), Object); 5386 } 5387 5388 5389 MaybeObject* JSObject::PreventExtensions() { 5390 Isolate* isolate = GetIsolate(); 5391 if (IsAccessCheckNeeded() && 5392 !isolate->MayNamedAccess(this, 5393 isolate->heap()->undefined_value(), 5394 v8::ACCESS_KEYS)) { 5395 isolate->ReportFailedAccessCheck(this, v8::ACCESS_KEYS); 5396 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 5397 return isolate->heap()->false_value(); 5398 } 5399 5400 if (IsJSGlobalProxy()) { 5401 Object* proto = GetPrototype(); 5402 if (proto->IsNull()) return this; 5403 ASSERT(proto->IsJSGlobalObject()); 5404 return JSObject::cast(proto)->PreventExtensions(); 5405 } 5406 5407 // It's not possible to seal objects with external array elements 5408 if (HasExternalArrayElements()) { 5409 HandleScope scope(isolate); 5410 Handle<Object> object(this, isolate); 5411 Handle<Object> error = 5412 isolate->factory()->NewTypeError( 5413 "cant_prevent_ext_external_array_elements", 5414 HandleVector(&object, 1)); 5415 return isolate->Throw(*error); 5416 } 5417 5418 // If there are fast elements we normalize. 5419 SeededNumberDictionary* dictionary = NULL; 5420 { MaybeObject* maybe = NormalizeElements(); 5421 if (!maybe->To<SeededNumberDictionary>(&dictionary)) return maybe; 5422 } 5423 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 5424 // Make sure that we never go back to fast case. 5425 dictionary->set_requires_slow_elements(); 5426 5427 // Do a map transition, other objects with this map may still 5428 // be extensible. 5429 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. 5430 Map* new_map; 5431 MaybeObject* maybe = map()->Copy(); 5432 if (!maybe->To(&new_map)) return maybe; 5433 5434 new_map->set_is_extensible(false); 5435 set_map(new_map); 5436 ASSERT(!map()->is_extensible()); 5437 return new_map; 5438 } 5439 5440 5441 template<typename Dictionary> 5442 static void FreezeDictionary(Dictionary* dictionary) { 5443 int capacity = dictionary->Capacity(); 5444 for (int i = 0; i < capacity; i++) { 5445 Object* k = dictionary->KeyAt(i); 5446 if (dictionary->IsKey(k)) { 5447 PropertyDetails details = dictionary->DetailsAt(i); 5448 int attrs = DONT_DELETE; 5449 // READ_ONLY is an invalid attribute for JS setters/getters. 5450 if (details.type() != CALLBACKS || 5451 !dictionary->ValueAt(i)->IsAccessorPair()) { 5452 attrs |= READ_ONLY; 5453 } 5454 details = details.CopyAddAttributes( 5455 static_cast<PropertyAttributes>(attrs)); 5456 dictionary->DetailsAtPut(i, details); 5457 } 5458 } 5459 } 5460 5461 5462 MUST_USE_RESULT MaybeObject* JSObject::Freeze(Isolate* isolate) { 5463 // Freezing non-strict arguments should be handled elsewhere. 5464 ASSERT(!HasNonStrictArgumentsElements()); 5465 5466 Heap* heap = isolate->heap(); 5467 5468 if (map()->is_frozen()) return this; 5469 5470 if (IsAccessCheckNeeded() && 5471 !isolate->MayNamedAccess(this, 5472 heap->undefined_value(), 5473 v8::ACCESS_KEYS)) { 5474 isolate->ReportFailedAccessCheck(this, v8::ACCESS_KEYS); 5475 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 5476 return heap->false_value(); 5477 } 5478 5479 if (IsJSGlobalProxy()) { 5480 Object* proto = GetPrototype(); 5481 if (proto->IsNull()) return this; 5482 ASSERT(proto->IsJSGlobalObject()); 5483 return JSObject::cast(proto)->Freeze(isolate); 5484 } 5485 5486 // It's not possible to freeze objects with external array elements 5487 if (HasExternalArrayElements()) { 5488 HandleScope scope(isolate); 5489 Handle<Object> object(this, isolate); 5490 Handle<Object> error = 5491 isolate->factory()->NewTypeError( 5492 "cant_prevent_ext_external_array_elements", 5493 HandleVector(&object, 1)); 5494 return isolate->Throw(*error); 5495 } 5496 5497 SeededNumberDictionary* new_element_dictionary = NULL; 5498 if (!elements()->IsDictionary()) { 5499 int length = IsJSArray() 5500 ? Smi::cast(JSArray::cast(this)->length())->value() 5501 : elements()->length(); 5502 if (length > 0) { 5503 int capacity = 0; 5504 int used = 0; 5505 GetElementsCapacityAndUsage(&capacity, &used); 5506 MaybeObject* maybe_dict = SeededNumberDictionary::Allocate(heap, used); 5507 if (!maybe_dict->To(&new_element_dictionary)) return maybe_dict; 5508 5509 // Move elements to a dictionary; avoid calling NormalizeElements to avoid 5510 // unnecessary transitions. 5511 maybe_dict = CopyFastElementsToDictionary(isolate, elements(), length, 5512 new_element_dictionary); 5513 if (!maybe_dict->To(&new_element_dictionary)) return maybe_dict; 5514 } else { 5515 // No existing elements, use a pre-allocated empty backing store 5516 new_element_dictionary = heap->empty_slow_element_dictionary(); 5517 } 5518 } 5519 5520 LookupResult result(isolate); 5521 map()->LookupTransition(this, heap->frozen_symbol(), &result); 5522 if (result.IsTransition()) { 5523 Map* transition_map = result.GetTransitionTarget(); 5524 ASSERT(transition_map->has_dictionary_elements()); 5525 ASSERT(transition_map->is_frozen()); 5526 ASSERT(!transition_map->is_extensible()); 5527 set_map(transition_map); 5528 } else if (HasFastProperties() && map()->CanHaveMoreTransitions()) { 5529 // Create a new descriptor array with fully-frozen properties 5530 int num_descriptors = map()->NumberOfOwnDescriptors(); 5531 DescriptorArray* new_descriptors; 5532 MaybeObject* maybe_descriptors = 5533 map()->instance_descriptors()->CopyUpToAddAttributes(num_descriptors, 5534 FROZEN); 5535 if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; 5536 5537 Map* new_map; 5538 MaybeObject* maybe_new_map = map()->CopyReplaceDescriptors( 5539 new_descriptors, INSERT_TRANSITION, heap->frozen_symbol()); 5540 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 5541 new_map->freeze(); 5542 new_map->set_is_extensible(false); 5543 new_map->set_elements_kind(DICTIONARY_ELEMENTS); 5544 set_map(new_map); 5545 } else { 5546 // Slow path: need to normalize properties for safety 5547 MaybeObject* maybe = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 5548 if (maybe->IsFailure()) return maybe; 5549 5550 // Create a new map, since other objects with this map may be extensible. 5551 // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. 5552 Map* new_map; 5553 MaybeObject* maybe_copy = map()->Copy(); 5554 if (!maybe_copy->To(&new_map)) return maybe_copy; 5555 new_map->freeze(); 5556 new_map->set_is_extensible(false); 5557 new_map->set_elements_kind(DICTIONARY_ELEMENTS); 5558 set_map(new_map); 5559 5560 // Freeze dictionary-mode properties 5561 FreezeDictionary(property_dictionary()); 5562 } 5563 5564 ASSERT(map()->has_dictionary_elements()); 5565 if (new_element_dictionary != NULL) { 5566 set_elements(new_element_dictionary); 5567 } 5568 5569 if (elements() != heap->empty_slow_element_dictionary()) { 5570 SeededNumberDictionary* dictionary = element_dictionary(); 5571 // Make sure we never go back to the fast case 5572 dictionary->set_requires_slow_elements(); 5573 // Freeze all elements in the dictionary 5574 FreezeDictionary(dictionary); 5575 } 5576 5577 return this; 5578 } 5579 5580 5581 MUST_USE_RESULT MaybeObject* JSObject::SetObserved(Isolate* isolate) { 5582 if (map()->is_observed()) 5583 return isolate->heap()->undefined_value(); 5584 5585 Heap* heap = isolate->heap(); 5586 5587 if (!HasExternalArrayElements()) { 5588 // Go to dictionary mode, so that we don't skip map checks. 5589 MaybeObject* maybe = NormalizeElements(); 5590 if (maybe->IsFailure()) return maybe; 5591 ASSERT(!HasFastElements()); 5592 } 5593 5594 LookupResult result(isolate); 5595 map()->LookupTransition(this, heap->observed_symbol(), &result); 5596 5597 Map* new_map; 5598 if (result.IsTransition()) { 5599 new_map = result.GetTransitionTarget(); 5600 ASSERT(new_map->is_observed()); 5601 } else if (map()->CanHaveMoreTransitions()) { 5602 MaybeObject* maybe_new_map = map()->CopyForObserved(); 5603 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 5604 } else { 5605 MaybeObject* maybe_copy = map()->Copy(); 5606 if (!maybe_copy->To(&new_map)) return maybe_copy; 5607 new_map->set_is_observed(true); 5608 } 5609 set_map(new_map); 5610 5611 return heap->undefined_value(); 5612 } 5613 5614 5615 MUST_USE_RESULT MaybeObject* JSObject::DeepCopy(Isolate* isolate) { 5616 StackLimitCheck check(isolate); 5617 if (check.HasOverflowed()) return isolate->StackOverflow(); 5618 5619 if (map()->is_deprecated()) { 5620 MaybeObject* maybe_failure = MigrateInstance(); 5621 if (maybe_failure->IsFailure()) return maybe_failure; 5622 } 5623 5624 Heap* heap = isolate->heap(); 5625 Object* result; 5626 { MaybeObject* maybe_result = heap->CopyJSObject(this); 5627 if (!maybe_result->ToObject(&result)) return maybe_result; 5628 } 5629 JSObject* copy = JSObject::cast(result); 5630 5631 // Deep copy local properties. 5632 if (copy->HasFastProperties()) { 5633 DescriptorArray* descriptors = copy->map()->instance_descriptors(); 5634 int limit = copy->map()->NumberOfOwnDescriptors(); 5635 for (int i = 0; i < limit; i++) { 5636 PropertyDetails details = descriptors->GetDetails(i); 5637 if (details.type() != FIELD) continue; 5638 int index = descriptors->GetFieldIndex(i); 5639 Object* value = RawFastPropertyAt(index); 5640 if (value->IsJSObject()) { 5641 JSObject* js_object = JSObject::cast(value); 5642 MaybeObject* maybe_copy = js_object->DeepCopy(isolate); 5643 if (!maybe_copy->To(&value)) return maybe_copy; 5644 } else { 5645 Representation representation = details.representation(); 5646 MaybeObject* maybe_storage = 5647 value->AllocateNewStorageFor(heap, representation); 5648 if (!maybe_storage->To(&value)) return maybe_storage; 5649 } 5650 copy->FastPropertyAtPut(index, value); 5651 } 5652 } else { 5653 { MaybeObject* maybe_result = 5654 heap->AllocateFixedArray(copy->NumberOfLocalProperties()); 5655 if (!maybe_result->ToObject(&result)) return maybe_result; 5656 } 5657 FixedArray* names = FixedArray::cast(result); 5658 copy->GetLocalPropertyNames(names, 0); 5659 for (int i = 0; i < names->length(); i++) { 5660 ASSERT(names->get(i)->IsString()); 5661 String* key_string = String::cast(names->get(i)); 5662 PropertyAttributes attributes = 5663 copy->GetLocalPropertyAttribute(key_string); 5664 // Only deep copy fields from the object literal expression. 5665 // In particular, don't try to copy the length attribute of 5666 // an array. 5667 if (attributes != NONE) continue; 5668 Object* value = 5669 copy->GetProperty(key_string, &attributes)->ToObjectUnchecked(); 5670 if (value->IsJSObject()) { 5671 JSObject* js_object = JSObject::cast(value); 5672 { MaybeObject* maybe_result = js_object->DeepCopy(isolate); 5673 if (!maybe_result->ToObject(&result)) return maybe_result; 5674 } 5675 { MaybeObject* maybe_result = 5676 // Creating object copy for literals. No strict mode needed. 5677 copy->SetProperty(key_string, result, NONE, kNonStrictMode); 5678 if (!maybe_result->ToObject(&result)) return maybe_result; 5679 } 5680 } 5681 } 5682 } 5683 5684 // Deep copy local elements. 5685 // Pixel elements cannot be created using an object literal. 5686 ASSERT(!copy->HasExternalArrayElements()); 5687 switch (copy->GetElementsKind()) { 5688 case FAST_SMI_ELEMENTS: 5689 case FAST_ELEMENTS: 5690 case FAST_HOLEY_SMI_ELEMENTS: 5691 case FAST_HOLEY_ELEMENTS: { 5692 FixedArray* elements = FixedArray::cast(copy->elements()); 5693 if (elements->map() == heap->fixed_cow_array_map()) { 5694 isolate->counters()->cow_arrays_created_runtime()->Increment(); 5695 #ifdef DEBUG 5696 for (int i = 0; i < elements->length(); i++) { 5697 ASSERT(!elements->get(i)->IsJSObject()); 5698 } 5699 #endif 5700 } else { 5701 for (int i = 0; i < elements->length(); i++) { 5702 Object* value = elements->get(i); 5703 ASSERT(value->IsSmi() || 5704 value->IsTheHole() || 5705 (IsFastObjectElementsKind(copy->GetElementsKind()))); 5706 if (value->IsJSObject()) { 5707 JSObject* js_object = JSObject::cast(value); 5708 { MaybeObject* maybe_result = js_object->DeepCopy(isolate); 5709 if (!maybe_result->ToObject(&result)) return maybe_result; 5710 } 5711 elements->set(i, result); 5712 } 5713 } 5714 } 5715 break; 5716 } 5717 case DICTIONARY_ELEMENTS: { 5718 SeededNumberDictionary* element_dictionary = copy->element_dictionary(); 5719 int capacity = element_dictionary->Capacity(); 5720 for (int i = 0; i < capacity; i++) { 5721 Object* k = element_dictionary->KeyAt(i); 5722 if (element_dictionary->IsKey(k)) { 5723 Object* value = element_dictionary->ValueAt(i); 5724 if (value->IsJSObject()) { 5725 JSObject* js_object = JSObject::cast(value); 5726 { MaybeObject* maybe_result = js_object->DeepCopy(isolate); 5727 if (!maybe_result->ToObject(&result)) return maybe_result; 5728 } 5729 element_dictionary->ValueAtPut(i, result); 5730 } 5731 } 5732 } 5733 break; 5734 } 5735 case NON_STRICT_ARGUMENTS_ELEMENTS: 5736 UNIMPLEMENTED(); 5737 break; 5738 case EXTERNAL_PIXEL_ELEMENTS: 5739 case EXTERNAL_BYTE_ELEMENTS: 5740 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 5741 case EXTERNAL_SHORT_ELEMENTS: 5742 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 5743 case EXTERNAL_INT_ELEMENTS: 5744 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 5745 case EXTERNAL_FLOAT_ELEMENTS: 5746 case EXTERNAL_DOUBLE_ELEMENTS: 5747 case FAST_DOUBLE_ELEMENTS: 5748 case FAST_HOLEY_DOUBLE_ELEMENTS: 5749 // No contained objects, nothing to do. 5750 break; 5751 } 5752 return copy; 5753 } 5754 5755 5756 // Tests for the fast common case for property enumeration: 5757 // - This object and all prototypes has an enum cache (which means that 5758 // it is no proxy, has no interceptors and needs no access checks). 5759 // - This object has no elements. 5760 // - No prototype has enumerable properties/elements. 5761 bool JSReceiver::IsSimpleEnum() { 5762 Heap* heap = GetHeap(); 5763 for (Object* o = this; 5764 o != heap->null_value(); 5765 o = JSObject::cast(o)->GetPrototype()) { 5766 if (!o->IsJSObject()) return false; 5767 JSObject* curr = JSObject::cast(o); 5768 int enum_length = curr->map()->EnumLength(); 5769 if (enum_length == Map::kInvalidEnumCache) return false; 5770 ASSERT(!curr->HasNamedInterceptor()); 5771 ASSERT(!curr->HasIndexedInterceptor()); 5772 ASSERT(!curr->IsAccessCheckNeeded()); 5773 if (curr->NumberOfEnumElements() > 0) return false; 5774 if (curr != this && enum_length != 0) return false; 5775 } 5776 return true; 5777 } 5778 5779 5780 int Map::NumberOfDescribedProperties(DescriptorFlag which, 5781 PropertyAttributes filter) { 5782 int result = 0; 5783 DescriptorArray* descs = instance_descriptors(); 5784 int limit = which == ALL_DESCRIPTORS 5785 ? descs->number_of_descriptors() 5786 : NumberOfOwnDescriptors(); 5787 for (int i = 0; i < limit; i++) { 5788 if ((descs->GetDetails(i).attributes() & filter) == 0 && 5789 ((filter & SYMBOLIC) == 0 || !descs->GetKey(i)->IsSymbol())) { 5790 result++; 5791 } 5792 } 5793 return result; 5794 } 5795 5796 5797 int Map::NextFreePropertyIndex() { 5798 int max_index = -1; 5799 int number_of_own_descriptors = NumberOfOwnDescriptors(); 5800 DescriptorArray* descs = instance_descriptors(); 5801 for (int i = 0; i < number_of_own_descriptors; i++) { 5802 if (descs->GetType(i) == FIELD) { 5803 int current_index = descs->GetFieldIndex(i); 5804 if (current_index > max_index) max_index = current_index; 5805 } 5806 } 5807 return max_index + 1; 5808 } 5809 5810 5811 AccessorDescriptor* Map::FindAccessor(Name* name) { 5812 DescriptorArray* descs = instance_descriptors(); 5813 int number_of_own_descriptors = NumberOfOwnDescriptors(); 5814 for (int i = 0; i < number_of_own_descriptors; i++) { 5815 if (descs->GetType(i) == CALLBACKS && name->Equals(descs->GetKey(i))) { 5816 return descs->GetCallbacks(i); 5817 } 5818 } 5819 return NULL; 5820 } 5821 5822 5823 void JSReceiver::LocalLookup( 5824 Name* name, LookupResult* result, bool search_hidden_prototypes) { 5825 ASSERT(name->IsName()); 5826 5827 Heap* heap = GetHeap(); 5828 5829 if (IsJSGlobalProxy()) { 5830 Object* proto = GetPrototype(); 5831 if (proto->IsNull()) return result->NotFound(); 5832 ASSERT(proto->IsJSGlobalObject()); 5833 return JSReceiver::cast(proto)->LocalLookup( 5834 name, result, search_hidden_prototypes); 5835 } 5836 5837 if (IsJSProxy()) { 5838 result->HandlerResult(JSProxy::cast(this)); 5839 return; 5840 } 5841 5842 // Do not use inline caching if the object is a non-global object 5843 // that requires access checks. 5844 if (IsAccessCheckNeeded()) { 5845 result->DisallowCaching(); 5846 } 5847 5848 JSObject* js_object = JSObject::cast(this); 5849 5850 // Check for lookup interceptor except when bootstrapping. 5851 if (js_object->HasNamedInterceptor() && 5852 !heap->isolate()->bootstrapper()->IsActive()) { 5853 result->InterceptorResult(js_object); 5854 return; 5855 } 5856 5857 js_object->LocalLookupRealNamedProperty(name, result); 5858 if (result->IsFound() || !search_hidden_prototypes) return; 5859 5860 Object* proto = js_object->GetPrototype(); 5861 if (!proto->IsJSReceiver()) return; 5862 JSReceiver* receiver = JSReceiver::cast(proto); 5863 if (receiver->map()->is_hidden_prototype()) { 5864 receiver->LocalLookup(name, result, search_hidden_prototypes); 5865 } 5866 } 5867 5868 5869 void JSReceiver::Lookup(Name* name, LookupResult* result) { 5870 // Ecma-262 3rd 8.6.2.4 5871 Heap* heap = GetHeap(); 5872 for (Object* current = this; 5873 current != heap->null_value(); 5874 current = JSObject::cast(current)->GetPrototype()) { 5875 JSReceiver::cast(current)->LocalLookup(name, result, false); 5876 if (result->IsFound()) return; 5877 } 5878 result->NotFound(); 5879 } 5880 5881 5882 // Search object and its prototype chain for callback properties. 5883 void JSObject::LookupCallbackProperty(Name* name, LookupResult* result) { 5884 Heap* heap = GetHeap(); 5885 for (Object* current = this; 5886 current != heap->null_value() && current->IsJSObject(); 5887 current = JSObject::cast(current)->GetPrototype()) { 5888 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result); 5889 if (result->IsPropertyCallbacks()) return; 5890 } 5891 result->NotFound(); 5892 } 5893 5894 5895 // Try to update an accessor in an elements dictionary. Return true if the 5896 // update succeeded, and false otherwise. 5897 static bool UpdateGetterSetterInDictionary( 5898 SeededNumberDictionary* dictionary, 5899 uint32_t index, 5900 Object* getter, 5901 Object* setter, 5902 PropertyAttributes attributes) { 5903 int entry = dictionary->FindEntry(index); 5904 if (entry != SeededNumberDictionary::kNotFound) { 5905 Object* result = dictionary->ValueAt(entry); 5906 PropertyDetails details = dictionary->DetailsAt(entry); 5907 if (details.type() == CALLBACKS && result->IsAccessorPair()) { 5908 ASSERT(!details.IsDontDelete()); 5909 if (details.attributes() != attributes) { 5910 dictionary->DetailsAtPut( 5911 entry, 5912 PropertyDetails(attributes, CALLBACKS, index)); 5913 } 5914 AccessorPair::cast(result)->SetComponents(getter, setter); 5915 return true; 5916 } 5917 } 5918 return false; 5919 } 5920 5921 5922 void JSObject::DefineElementAccessor(Handle<JSObject> object, 5923 uint32_t index, 5924 Handle<Object> getter, 5925 Handle<Object> setter, 5926 PropertyAttributes attributes) { 5927 switch (object->GetElementsKind()) { 5928 case FAST_SMI_ELEMENTS: 5929 case FAST_ELEMENTS: 5930 case FAST_DOUBLE_ELEMENTS: 5931 case FAST_HOLEY_SMI_ELEMENTS: 5932 case FAST_HOLEY_ELEMENTS: 5933 case FAST_HOLEY_DOUBLE_ELEMENTS: 5934 break; 5935 case EXTERNAL_PIXEL_ELEMENTS: 5936 case EXTERNAL_BYTE_ELEMENTS: 5937 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 5938 case EXTERNAL_SHORT_ELEMENTS: 5939 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 5940 case EXTERNAL_INT_ELEMENTS: 5941 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 5942 case EXTERNAL_FLOAT_ELEMENTS: 5943 case EXTERNAL_DOUBLE_ELEMENTS: 5944 // Ignore getters and setters on pixel and external array elements. 5945 return; 5946 case DICTIONARY_ELEMENTS: 5947 if (UpdateGetterSetterInDictionary(object->element_dictionary(), 5948 index, 5949 *getter, 5950 *setter, 5951 attributes)) { 5952 return; 5953 } 5954 break; 5955 case NON_STRICT_ARGUMENTS_ELEMENTS: { 5956 // Ascertain whether we have read-only properties or an existing 5957 // getter/setter pair in an arguments elements dictionary backing 5958 // store. 5959 FixedArray* parameter_map = FixedArray::cast(object->elements()); 5960 uint32_t length = parameter_map->length(); 5961 Object* probe = 5962 index < (length - 2) ? parameter_map->get(index + 2) : NULL; 5963 if (probe == NULL || probe->IsTheHole()) { 5964 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 5965 if (arguments->IsDictionary()) { 5966 SeededNumberDictionary* dictionary = 5967 SeededNumberDictionary::cast(arguments); 5968 if (UpdateGetterSetterInDictionary(dictionary, 5969 index, 5970 *getter, 5971 *setter, 5972 attributes)) { 5973 return; 5974 } 5975 } 5976 } 5977 break; 5978 } 5979 } 5980 5981 Isolate* isolate = object->GetIsolate(); 5982 Handle<AccessorPair> accessors = isolate->factory()->NewAccessorPair(); 5983 accessors->SetComponents(*getter, *setter); 5984 5985 CALL_HEAP_FUNCTION_VOID( 5986 isolate, object->SetElementCallback(index, *accessors, attributes)); 5987 } 5988 5989 5990 Handle<AccessorPair> JSObject::CreateAccessorPairFor(Handle<JSObject> object, 5991 Handle<Name> name) { 5992 Isolate* isolate = object->GetIsolate(); 5993 LookupResult result(isolate); 5994 object->LocalLookupRealNamedProperty(*name, &result); 5995 if (result.IsPropertyCallbacks()) { 5996 // Note that the result can actually have IsDontDelete() == true when we 5997 // e.g. have to fall back to the slow case while adding a setter after 5998 // successfully reusing a map transition for a getter. Nevertheless, this is 5999 // OK, because the assertion only holds for the whole addition of both 6000 // accessors, not for the addition of each part. See first comment in 6001 // DefinePropertyAccessor below. 6002 Object* obj = result.GetCallbackObject(); 6003 if (obj->IsAccessorPair()) { 6004 return AccessorPair::Copy(handle(AccessorPair::cast(obj), isolate)); 6005 } 6006 } 6007 return isolate->factory()->NewAccessorPair(); 6008 } 6009 6010 6011 void JSObject::DefinePropertyAccessor(Handle<JSObject> object, 6012 Handle<Name> name, 6013 Handle<Object> getter, 6014 Handle<Object> setter, 6015 PropertyAttributes attributes) { 6016 // We could assert that the property is configurable here, but we would need 6017 // to do a lookup, which seems to be a bit of overkill. 6018 bool only_attribute_changes = getter->IsNull() && setter->IsNull(); 6019 if (object->HasFastProperties() && !only_attribute_changes && 6020 (object->map()->NumberOfOwnDescriptors() < 6021 DescriptorArray::kMaxNumberOfDescriptors)) { 6022 bool getterOk = getter->IsNull() || 6023 DefineFastAccessor(object, name, ACCESSOR_GETTER, getter, attributes); 6024 bool setterOk = !getterOk || setter->IsNull() || 6025 DefineFastAccessor(object, name, ACCESSOR_SETTER, setter, attributes); 6026 if (getterOk && setterOk) return; 6027 } 6028 6029 Handle<AccessorPair> accessors = CreateAccessorPairFor(object, name); 6030 accessors->SetComponents(*getter, *setter); 6031 6032 CALL_HEAP_FUNCTION_VOID( 6033 object->GetIsolate(), 6034 object->SetPropertyCallback(*name, *accessors, attributes)); 6035 } 6036 6037 6038 bool JSObject::CanSetCallback(Name* name) { 6039 ASSERT(!IsAccessCheckNeeded() || 6040 GetIsolate()->MayNamedAccess(this, name, v8::ACCESS_SET)); 6041 6042 // Check if there is an API defined callback object which prohibits 6043 // callback overwriting in this object or its prototype chain. 6044 // This mechanism is needed for instance in a browser setting, where 6045 // certain accessors such as window.location should not be allowed 6046 // to be overwritten because allowing overwriting could potentially 6047 // cause security problems. 6048 LookupResult callback_result(GetIsolate()); 6049 LookupCallbackProperty(name, &callback_result); 6050 if (callback_result.IsFound()) { 6051 Object* obj = callback_result.GetCallbackObject(); 6052 if (obj->IsAccessorInfo() && 6053 AccessorInfo::cast(obj)->prohibits_overwriting()) { 6054 return false; 6055 } 6056 } 6057 6058 return true; 6059 } 6060 6061 6062 MaybeObject* JSObject::SetElementCallback(uint32_t index, 6063 Object* structure, 6064 PropertyAttributes attributes) { 6065 PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0); 6066 6067 // Normalize elements to make this operation simple. 6068 SeededNumberDictionary* dictionary; 6069 { MaybeObject* maybe_dictionary = NormalizeElements(); 6070 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 6071 } 6072 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 6073 6074 // Update the dictionary with the new CALLBACKS property. 6075 { MaybeObject* maybe_dictionary = dictionary->Set(index, structure, details); 6076 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 6077 } 6078 6079 dictionary->set_requires_slow_elements(); 6080 // Update the dictionary backing store on the object. 6081 if (elements()->map() == GetHeap()->non_strict_arguments_elements_map()) { 6082 // Also delete any parameter alias. 6083 // 6084 // TODO(kmillikin): when deleting the last parameter alias we could 6085 // switch to a direct backing store without the parameter map. This 6086 // would allow GC of the context. 6087 FixedArray* parameter_map = FixedArray::cast(elements()); 6088 if (index < static_cast<uint32_t>(parameter_map->length()) - 2) { 6089 parameter_map->set(index + 2, GetHeap()->the_hole_value()); 6090 } 6091 parameter_map->set(1, dictionary); 6092 } else { 6093 set_elements(dictionary); 6094 } 6095 6096 return GetHeap()->undefined_value(); 6097 } 6098 6099 6100 MaybeObject* JSObject::SetPropertyCallback(Name* name, 6101 Object* structure, 6102 PropertyAttributes attributes) { 6103 // Normalize object to make this operation simple. 6104 MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 6105 if (maybe_ok->IsFailure()) return maybe_ok; 6106 6107 // For the global object allocate a new map to invalidate the global inline 6108 // caches which have a global property cell reference directly in the code. 6109 if (IsGlobalObject()) { 6110 Map* new_map; 6111 MaybeObject* maybe_new_map = map()->CopyDropDescriptors(); 6112 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 6113 ASSERT(new_map->is_dictionary_map()); 6114 6115 set_map(new_map); 6116 // When running crankshaft, changing the map is not enough. We 6117 // need to deoptimize all functions that rely on this global 6118 // object. 6119 Deoptimizer::DeoptimizeGlobalObject(this); 6120 } 6121 6122 // Update the dictionary with the new CALLBACKS property. 6123 PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0); 6124 maybe_ok = SetNormalizedProperty(name, structure, details); 6125 if (maybe_ok->IsFailure()) return maybe_ok; 6126 6127 return GetHeap()->undefined_value(); 6128 } 6129 6130 6131 void JSObject::DefineAccessor(Handle<JSObject> object, 6132 Handle<Name> name, 6133 Handle<Object> getter, 6134 Handle<Object> setter, 6135 PropertyAttributes attributes) { 6136 Isolate* isolate = object->GetIsolate(); 6137 // Check access rights if needed. 6138 if (object->IsAccessCheckNeeded() && 6139 !isolate->MayNamedAccess(*object, *name, v8::ACCESS_SET)) { 6140 isolate->ReportFailedAccessCheck(*object, v8::ACCESS_SET); 6141 return; 6142 } 6143 6144 if (object->IsJSGlobalProxy()) { 6145 Handle<Object> proto(object->GetPrototype(), isolate); 6146 if (proto->IsNull()) return; 6147 ASSERT(proto->IsJSGlobalObject()); 6148 DefineAccessor( 6149 Handle<JSObject>::cast(proto), name, getter, setter, attributes); 6150 return; 6151 } 6152 6153 // Make sure that the top context does not change when doing callbacks or 6154 // interceptor calls. 6155 AssertNoContextChange ncc; 6156 6157 // Try to flatten before operating on the string. 6158 if (name->IsString()) String::cast(*name)->TryFlatten(); 6159 6160 if (!object->CanSetCallback(*name)) return; 6161 6162 uint32_t index = 0; 6163 bool is_element = name->AsArrayIndex(&index); 6164 6165 Handle<Object> old_value = isolate->factory()->the_hole_value(); 6166 bool is_observed = FLAG_harmony_observation && object->map()->is_observed(); 6167 bool preexists = false; 6168 if (is_observed) { 6169 if (is_element) { 6170 preexists = object->HasLocalElement(index); 6171 if (preexists && object->GetLocalElementAccessorPair(index) == NULL) { 6172 old_value = Object::GetElement(object, index); 6173 } 6174 } else { 6175 LookupResult lookup(isolate); 6176 object->LocalLookup(*name, &lookup, true); 6177 preexists = lookup.IsProperty(); 6178 if (preexists && lookup.IsDataProperty()) { 6179 old_value = Object::GetProperty(object, name); 6180 } 6181 } 6182 } 6183 6184 if (is_element) { 6185 DefineElementAccessor(object, index, getter, setter, attributes); 6186 } else { 6187 DefinePropertyAccessor(object, name, getter, setter, attributes); 6188 } 6189 6190 if (is_observed) { 6191 const char* type = preexists ? "reconfigured" : "new"; 6192 EnqueueChangeRecord(object, type, name, old_value); 6193 } 6194 } 6195 6196 6197 static bool TryAccessorTransition(JSObject* self, 6198 Map* transitioned_map, 6199 int target_descriptor, 6200 AccessorComponent component, 6201 Object* accessor, 6202 PropertyAttributes attributes) { 6203 DescriptorArray* descs = transitioned_map->instance_descriptors(); 6204 PropertyDetails details = descs->GetDetails(target_descriptor); 6205 6206 // If the transition target was not callbacks, fall back to the slow case. 6207 if (details.type() != CALLBACKS) return false; 6208 Object* descriptor = descs->GetCallbacksObject(target_descriptor); 6209 if (!descriptor->IsAccessorPair()) return false; 6210 6211 Object* target_accessor = AccessorPair::cast(descriptor)->get(component); 6212 PropertyAttributes target_attributes = details.attributes(); 6213 6214 // Reuse transition if adding same accessor with same attributes. 6215 if (target_accessor == accessor && target_attributes == attributes) { 6216 self->set_map(transitioned_map); 6217 return true; 6218 } 6219 6220 // If either not the same accessor, or not the same attributes, fall back to 6221 // the slow case. 6222 return false; 6223 } 6224 6225 6226 static MaybeObject* CopyInsertDescriptor(Map* map, 6227 Name* name, 6228 AccessorPair* accessors, 6229 PropertyAttributes attributes) { 6230 CallbacksDescriptor new_accessors_desc(name, accessors, attributes); 6231 return map->CopyInsertDescriptor(&new_accessors_desc, INSERT_TRANSITION); 6232 } 6233 6234 6235 static Handle<Map> CopyInsertDescriptor(Handle<Map> map, 6236 Handle<Name> name, 6237 Handle<AccessorPair> accessors, 6238 PropertyAttributes attributes) { 6239 CALL_HEAP_FUNCTION(map->GetIsolate(), 6240 CopyInsertDescriptor(*map, *name, *accessors, attributes), 6241 Map); 6242 } 6243 6244 6245 bool JSObject::DefineFastAccessor(Handle<JSObject> object, 6246 Handle<Name> name, 6247 AccessorComponent component, 6248 Handle<Object> accessor, 6249 PropertyAttributes attributes) { 6250 ASSERT(accessor->IsSpecFunction() || accessor->IsUndefined()); 6251 Isolate* isolate = object->GetIsolate(); 6252 LookupResult result(isolate); 6253 object->LocalLookup(*name, &result); 6254 6255 if (result.IsFound() && !result.IsPropertyCallbacks()) { 6256 return false; 6257 } 6258 6259 // Return success if the same accessor with the same attributes already exist. 6260 AccessorPair* source_accessors = NULL; 6261 if (result.IsPropertyCallbacks()) { 6262 Object* callback_value = result.GetCallbackObject(); 6263 if (callback_value->IsAccessorPair()) { 6264 source_accessors = AccessorPair::cast(callback_value); 6265 Object* entry = source_accessors->get(component); 6266 if (entry == *accessor && result.GetAttributes() == attributes) { 6267 return true; 6268 } 6269 } else { 6270 return false; 6271 } 6272 6273 int descriptor_number = result.GetDescriptorIndex(); 6274 6275 object->map()->LookupTransition(*object, *name, &result); 6276 6277 if (result.IsFound()) { 6278 Map* target = result.GetTransitionTarget(); 6279 ASSERT(target->NumberOfOwnDescriptors() == 6280 object->map()->NumberOfOwnDescriptors()); 6281 // This works since descriptors are sorted in order of addition. 6282 ASSERT(object->map()->instance_descriptors()-> 6283 GetKey(descriptor_number) == *name); 6284 return TryAccessorTransition(*object, target, descriptor_number, 6285 component, *accessor, attributes); 6286 } 6287 } else { 6288 // If not, lookup a transition. 6289 object->map()->LookupTransition(*object, *name, &result); 6290 6291 // If there is a transition, try to follow it. 6292 if (result.IsFound()) { 6293 Map* target = result.GetTransitionTarget(); 6294 int descriptor_number = target->LastAdded(); 6295 ASSERT(target->instance_descriptors()->GetKey(descriptor_number) 6296 ->Equals(*name)); 6297 return TryAccessorTransition(*object, target, descriptor_number, 6298 component, *accessor, attributes); 6299 } 6300 } 6301 6302 // If there is no transition yet, add a transition to the a new accessor pair 6303 // containing the accessor. Allocate a new pair if there were no source 6304 // accessors. Otherwise, copy the pair and modify the accessor. 6305 Handle<AccessorPair> accessors = source_accessors != NULL 6306 ? AccessorPair::Copy(Handle<AccessorPair>(source_accessors)) 6307 : isolate->factory()->NewAccessorPair(); 6308 accessors->set(component, *accessor); 6309 Handle<Map> new_map = CopyInsertDescriptor(Handle<Map>(object->map()), 6310 name, accessors, attributes); 6311 object->set_map(*new_map); 6312 return true; 6313 } 6314 6315 6316 MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { 6317 Isolate* isolate = GetIsolate(); 6318 Name* name = Name::cast(info->name()); 6319 // Check access rights if needed. 6320 if (IsAccessCheckNeeded() && 6321 !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { 6322 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); 6323 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 6324 return isolate->heap()->undefined_value(); 6325 } 6326 6327 if (IsJSGlobalProxy()) { 6328 Object* proto = GetPrototype(); 6329 if (proto->IsNull()) return this; 6330 ASSERT(proto->IsJSGlobalObject()); 6331 return JSObject::cast(proto)->DefineAccessor(info); 6332 } 6333 6334 // Make sure that the top context does not change when doing callbacks or 6335 // interceptor calls. 6336 AssertNoContextChange ncc; 6337 6338 // Try to flatten before operating on the string. 6339 if (name->IsString()) String::cast(name)->TryFlatten(); 6340 6341 if (!CanSetCallback(name)) return isolate->heap()->undefined_value(); 6342 6343 uint32_t index = 0; 6344 bool is_element = name->AsArrayIndex(&index); 6345 6346 if (is_element) { 6347 if (IsJSArray()) return isolate->heap()->undefined_value(); 6348 6349 // Accessors overwrite previous callbacks (cf. with getters/setters). 6350 switch (GetElementsKind()) { 6351 case FAST_SMI_ELEMENTS: 6352 case FAST_ELEMENTS: 6353 case FAST_DOUBLE_ELEMENTS: 6354 case FAST_HOLEY_SMI_ELEMENTS: 6355 case FAST_HOLEY_ELEMENTS: 6356 case FAST_HOLEY_DOUBLE_ELEMENTS: 6357 break; 6358 case EXTERNAL_PIXEL_ELEMENTS: 6359 case EXTERNAL_BYTE_ELEMENTS: 6360 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 6361 case EXTERNAL_SHORT_ELEMENTS: 6362 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 6363 case EXTERNAL_INT_ELEMENTS: 6364 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 6365 case EXTERNAL_FLOAT_ELEMENTS: 6366 case EXTERNAL_DOUBLE_ELEMENTS: 6367 // Ignore getters and setters on pixel and external array 6368 // elements. 6369 return isolate->heap()->undefined_value(); 6370 case DICTIONARY_ELEMENTS: 6371 break; 6372 case NON_STRICT_ARGUMENTS_ELEMENTS: 6373 UNIMPLEMENTED(); 6374 break; 6375 } 6376 6377 MaybeObject* maybe_ok = 6378 SetElementCallback(index, info, info->property_attributes()); 6379 if (maybe_ok->IsFailure()) return maybe_ok; 6380 } else { 6381 // Lookup the name. 6382 LookupResult result(isolate); 6383 LocalLookup(name, &result, true); 6384 // ES5 forbids turning a property into an accessor if it's not 6385 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5). 6386 if (result.IsFound() && (result.IsReadOnly() || result.IsDontDelete())) { 6387 return isolate->heap()->undefined_value(); 6388 } 6389 6390 MaybeObject* maybe_ok = 6391 SetPropertyCallback(name, info, info->property_attributes()); 6392 if (maybe_ok->IsFailure()) return maybe_ok; 6393 } 6394 6395 return this; 6396 } 6397 6398 6399 MaybeObject* JSObject::LookupAccessor(Name* name, AccessorComponent component) { 6400 Heap* heap = GetHeap(); 6401 6402 // Make sure that the top context does not change when doing callbacks or 6403 // interceptor calls. 6404 AssertNoContextChange ncc; 6405 6406 // Check access rights if needed. 6407 if (IsAccessCheckNeeded() && 6408 !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_HAS)) { 6409 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 6410 RETURN_IF_SCHEDULED_EXCEPTION(heap->isolate()); 6411 return heap->undefined_value(); 6412 } 6413 6414 // Make the lookup and include prototypes. 6415 uint32_t index = 0; 6416 if (name->AsArrayIndex(&index)) { 6417 for (Object* obj = this; 6418 obj != heap->null_value(); 6419 obj = JSReceiver::cast(obj)->GetPrototype()) { 6420 if (obj->IsJSObject() && JSObject::cast(obj)->HasDictionaryElements()) { 6421 JSObject* js_object = JSObject::cast(obj); 6422 SeededNumberDictionary* dictionary = js_object->element_dictionary(); 6423 int entry = dictionary->FindEntry(index); 6424 if (entry != SeededNumberDictionary::kNotFound) { 6425 Object* element = dictionary->ValueAt(entry); 6426 if (dictionary->DetailsAt(entry).type() == CALLBACKS && 6427 element->IsAccessorPair()) { 6428 return AccessorPair::cast(element)->GetComponent(component); 6429 } 6430 } 6431 } 6432 } 6433 } else { 6434 for (Object* obj = this; 6435 obj != heap->null_value(); 6436 obj = JSReceiver::cast(obj)->GetPrototype()) { 6437 LookupResult result(heap->isolate()); 6438 JSReceiver::cast(obj)->LocalLookup(name, &result); 6439 if (result.IsFound()) { 6440 if (result.IsReadOnly()) return heap->undefined_value(); 6441 if (result.IsPropertyCallbacks()) { 6442 Object* obj = result.GetCallbackObject(); 6443 if (obj->IsAccessorPair()) { 6444 return AccessorPair::cast(obj)->GetComponent(component); 6445 } 6446 } 6447 } 6448 } 6449 } 6450 return heap->undefined_value(); 6451 } 6452 6453 6454 Object* JSObject::SlowReverseLookup(Object* value) { 6455 if (HasFastProperties()) { 6456 int number_of_own_descriptors = map()->NumberOfOwnDescriptors(); 6457 DescriptorArray* descs = map()->instance_descriptors(); 6458 for (int i = 0; i < number_of_own_descriptors; i++) { 6459 if (descs->GetType(i) == FIELD) { 6460 Object* property = RawFastPropertyAt(descs->GetFieldIndex(i)); 6461 if (FLAG_track_double_fields && 6462 descs->GetDetails(i).representation().IsDouble()) { 6463 ASSERT(property->IsHeapNumber()); 6464 if (value->IsNumber() && property->Number() == value->Number()) { 6465 return descs->GetKey(i); 6466 } 6467 } else if (property == value) { 6468 return descs->GetKey(i); 6469 } 6470 } else if (descs->GetType(i) == CONSTANT) { 6471 if (descs->GetConstant(i) == value) { 6472 return descs->GetKey(i); 6473 } 6474 } 6475 } 6476 return GetHeap()->undefined_value(); 6477 } else { 6478 return property_dictionary()->SlowReverseLookup(value); 6479 } 6480 } 6481 6482 6483 MaybeObject* Map::RawCopy(int instance_size) { 6484 Map* result; 6485 MaybeObject* maybe_result = 6486 GetHeap()->AllocateMap(instance_type(), instance_size); 6487 if (!maybe_result->To(&result)) return maybe_result; 6488 6489 result->set_prototype(prototype()); 6490 result->set_constructor(constructor()); 6491 result->set_bit_field(bit_field()); 6492 result->set_bit_field2(bit_field2()); 6493 int new_bit_field3 = bit_field3(); 6494 new_bit_field3 = OwnsDescriptors::update(new_bit_field3, true); 6495 new_bit_field3 = NumberOfOwnDescriptorsBits::update(new_bit_field3, 0); 6496 new_bit_field3 = EnumLengthBits::update(new_bit_field3, kInvalidEnumCache); 6497 new_bit_field3 = Deprecated::update(new_bit_field3, false); 6498 new_bit_field3 = IsUnstable::update(new_bit_field3, false); 6499 result->set_bit_field3(new_bit_field3); 6500 return result; 6501 } 6502 6503 6504 MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode, 6505 NormalizedMapSharingMode sharing) { 6506 int new_instance_size = instance_size(); 6507 if (mode == CLEAR_INOBJECT_PROPERTIES) { 6508 new_instance_size -= inobject_properties() * kPointerSize; 6509 } 6510 6511 Map* result; 6512 MaybeObject* maybe_result = RawCopy(new_instance_size); 6513 if (!maybe_result->To(&result)) return maybe_result; 6514 6515 if (mode != CLEAR_INOBJECT_PROPERTIES) { 6516 result->set_inobject_properties(inobject_properties()); 6517 } 6518 6519 result->set_is_shared(sharing == SHARED_NORMALIZED_MAP); 6520 result->set_dictionary_map(true); 6521 result->set_migration_target(false); 6522 6523 #ifdef VERIFY_HEAP 6524 if (FLAG_verify_heap && result->is_shared()) { 6525 result->SharedMapVerify(); 6526 } 6527 #endif 6528 6529 return result; 6530 } 6531 6532 6533 Handle<Map> Map::CopyDropDescriptors(Handle<Map> map) { 6534 CALL_HEAP_FUNCTION(map->GetIsolate(), map->CopyDropDescriptors(), Map); 6535 } 6536 6537 6538 MaybeObject* Map::CopyDropDescriptors() { 6539 Map* result; 6540 MaybeObject* maybe_result = RawCopy(instance_size()); 6541 if (!maybe_result->To(&result)) return maybe_result; 6542 6543 // Please note instance_type and instance_size are set when allocated. 6544 result->set_inobject_properties(inobject_properties()); 6545 result->set_unused_property_fields(unused_property_fields()); 6546 6547 result->set_pre_allocated_property_fields(pre_allocated_property_fields()); 6548 result->set_is_shared(false); 6549 result->ClearCodeCache(GetHeap()); 6550 NotifyLeafMapLayoutChange(); 6551 return result; 6552 } 6553 6554 6555 MaybeObject* Map::ShareDescriptor(DescriptorArray* descriptors, 6556 Descriptor* descriptor) { 6557 // Sanity check. This path is only to be taken if the map owns its descriptor 6558 // array, implying that its NumberOfOwnDescriptors equals the number of 6559 // descriptors in the descriptor array. 6560 ASSERT(NumberOfOwnDescriptors() == 6561 instance_descriptors()->number_of_descriptors()); 6562 Map* result; 6563 MaybeObject* maybe_result = CopyDropDescriptors(); 6564 if (!maybe_result->To(&result)) return maybe_result; 6565 6566 Name* name = descriptor->GetKey(); 6567 6568 TransitionArray* transitions; 6569 MaybeObject* maybe_transitions = 6570 AddTransition(name, result, SIMPLE_TRANSITION); 6571 if (!maybe_transitions->To(&transitions)) return maybe_transitions; 6572 6573 int old_size = descriptors->number_of_descriptors(); 6574 6575 DescriptorArray* new_descriptors; 6576 6577 if (descriptors->NumberOfSlackDescriptors() > 0) { 6578 new_descriptors = descriptors; 6579 new_descriptors->Append(descriptor); 6580 } else { 6581 // Descriptor arrays grow by 50%. 6582 MaybeObject* maybe_descriptors = DescriptorArray::Allocate( 6583 old_size, old_size < 4 ? 1 : old_size / 2); 6584 if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; 6585 6586 DescriptorArray::WhitenessWitness witness(new_descriptors); 6587 6588 // Copy the descriptors, inserting a descriptor. 6589 for (int i = 0; i < old_size; ++i) { 6590 new_descriptors->CopyFrom(i, descriptors, i, witness); 6591 } 6592 6593 new_descriptors->Append(descriptor, witness); 6594 6595 if (old_size > 0) { 6596 // If the source descriptors had an enum cache we copy it. This ensures 6597 // that the maps to which we push the new descriptor array back can rely 6598 // on a cache always being available once it is set. If the map has more 6599 // enumerated descriptors than available in the original cache, the cache 6600 // will be lazily replaced by the extended cache when needed. 6601 if (descriptors->HasEnumCache()) { 6602 new_descriptors->CopyEnumCacheFrom(descriptors); 6603 } 6604 6605 Map* map; 6606 // Replace descriptors by new_descriptors in all maps that share it. 6607 for (Object* current = GetBackPointer(); 6608 !current->IsUndefined(); 6609 current = map->GetBackPointer()) { 6610 map = Map::cast(current); 6611 if (map->instance_descriptors() != descriptors) break; 6612 map->set_instance_descriptors(new_descriptors); 6613 } 6614 6615 set_instance_descriptors(new_descriptors); 6616 } 6617 } 6618 6619 result->SetBackPointer(this); 6620 result->InitializeDescriptors(new_descriptors); 6621 ASSERT(result->NumberOfOwnDescriptors() == NumberOfOwnDescriptors() + 1); 6622 6623 set_transitions(transitions); 6624 set_owns_descriptors(false); 6625 6626 return result; 6627 } 6628 6629 6630 MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, 6631 TransitionFlag flag, 6632 Name* name, 6633 SimpleTransitionFlag simple_flag) { 6634 ASSERT(descriptors->IsSortedNoDuplicates()); 6635 6636 Map* result; 6637 MaybeObject* maybe_result = CopyDropDescriptors(); 6638 if (!maybe_result->To(&result)) return maybe_result; 6639 6640 result->InitializeDescriptors(descriptors); 6641 6642 if (flag == INSERT_TRANSITION && CanHaveMoreTransitions()) { 6643 TransitionArray* transitions; 6644 MaybeObject* maybe_transitions = AddTransition(name, result, simple_flag); 6645 if (!maybe_transitions->To(&transitions)) return maybe_transitions; 6646 set_transitions(transitions); 6647 result->SetBackPointer(this); 6648 } else if (flag != OMIT_TRANSITION_KEEP_REPRESENTATIONS) { 6649 descriptors->InitializeRepresentations(Representation::Tagged()); 6650 } 6651 6652 return result; 6653 } 6654 6655 6656 // Since this method is used to rewrite an existing transition tree, it can 6657 // always insert transitions without checking. 6658 MaybeObject* Map::CopyInstallDescriptors(int new_descriptor, 6659 DescriptorArray* descriptors) { 6660 ASSERT(descriptors->IsSortedNoDuplicates()); 6661 6662 Map* result; 6663 MaybeObject* maybe_result = CopyDropDescriptors(); 6664 if (!maybe_result->To(&result)) return maybe_result; 6665 6666 result->InitializeDescriptors(descriptors); 6667 result->SetNumberOfOwnDescriptors(new_descriptor + 1); 6668 6669 int unused_property_fields = this->unused_property_fields(); 6670 if (descriptors->GetDetails(new_descriptor).type() == FIELD) { 6671 unused_property_fields = this->unused_property_fields() - 1; 6672 if (unused_property_fields < 0) { 6673 unused_property_fields += JSObject::kFieldsAdded; 6674 } 6675 } 6676 6677 result->set_unused_property_fields(unused_property_fields); 6678 result->set_owns_descriptors(false); 6679 6680 Name* name = descriptors->GetKey(new_descriptor); 6681 TransitionArray* transitions; 6682 MaybeObject* maybe_transitions = 6683 AddTransition(name, result, SIMPLE_TRANSITION); 6684 if (!maybe_transitions->To(&transitions)) return maybe_transitions; 6685 6686 set_transitions(transitions); 6687 result->SetBackPointer(this); 6688 6689 return result; 6690 } 6691 6692 6693 MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) { 6694 if (flag == INSERT_TRANSITION) { 6695 ASSERT(!HasElementsTransition() || 6696 ((elements_transition_map()->elements_kind() == DICTIONARY_ELEMENTS || 6697 IsExternalArrayElementsKind( 6698 elements_transition_map()->elements_kind())) && 6699 (kind == DICTIONARY_ELEMENTS || 6700 IsExternalArrayElementsKind(kind)))); 6701 ASSERT(!IsFastElementsKind(kind) || 6702 IsMoreGeneralElementsKindTransition(elements_kind(), kind)); 6703 ASSERT(kind != elements_kind()); 6704 } 6705 6706 bool insert_transition = 6707 flag == INSERT_TRANSITION && !HasElementsTransition(); 6708 6709 if (insert_transition && owns_descriptors()) { 6710 // In case the map owned its own descriptors, share the descriptors and 6711 // transfer ownership to the new map. 6712 Map* new_map; 6713 MaybeObject* maybe_new_map = CopyDropDescriptors(); 6714 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 6715 6716 MaybeObject* added_elements = set_elements_transition_map(new_map); 6717 if (added_elements->IsFailure()) return added_elements; 6718 6719 new_map->set_elements_kind(kind); 6720 new_map->InitializeDescriptors(instance_descriptors()); 6721 new_map->SetBackPointer(this); 6722 set_owns_descriptors(false); 6723 return new_map; 6724 } 6725 6726 // In case the map did not own its own descriptors, a split is forced by 6727 // copying the map; creating a new descriptor array cell. 6728 // Create a new free-floating map only if we are not allowed to store it. 6729 Map* new_map; 6730 MaybeObject* maybe_new_map = Copy(); 6731 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 6732 6733 new_map->set_elements_kind(kind); 6734 6735 if (insert_transition) { 6736 MaybeObject* added_elements = set_elements_transition_map(new_map); 6737 if (added_elements->IsFailure()) return added_elements; 6738 new_map->SetBackPointer(this); 6739 } 6740 6741 return new_map; 6742 } 6743 6744 6745 MaybeObject* Map::CopyForObserved() { 6746 ASSERT(!is_observed()); 6747 6748 // In case the map owned its own descriptors, share the descriptors and 6749 // transfer ownership to the new map. 6750 Map* new_map; 6751 MaybeObject* maybe_new_map; 6752 if (owns_descriptors()) { 6753 maybe_new_map = CopyDropDescriptors(); 6754 } else { 6755 maybe_new_map = Copy(); 6756 } 6757 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 6758 6759 TransitionArray* transitions; 6760 MaybeObject* maybe_transitions = AddTransition(GetHeap()->observed_symbol(), 6761 new_map, 6762 FULL_TRANSITION); 6763 if (!maybe_transitions->To(&transitions)) return maybe_transitions; 6764 set_transitions(transitions); 6765 6766 new_map->set_is_observed(true); 6767 6768 if (owns_descriptors()) { 6769 new_map->InitializeDescriptors(instance_descriptors()); 6770 set_owns_descriptors(false); 6771 } 6772 6773 new_map->SetBackPointer(this); 6774 return new_map; 6775 } 6776 6777 6778 MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() { 6779 if (pre_allocated_property_fields() == 0) return CopyDropDescriptors(); 6780 6781 // If the map has pre-allocated properties always start out with a descriptor 6782 // array describing these properties. 6783 ASSERT(constructor()->IsJSFunction()); 6784 JSFunction* ctor = JSFunction::cast(constructor()); 6785 Map* map = ctor->initial_map(); 6786 DescriptorArray* descriptors = map->instance_descriptors(); 6787 6788 int number_of_own_descriptors = map->NumberOfOwnDescriptors(); 6789 DescriptorArray* new_descriptors; 6790 MaybeObject* maybe_descriptors = 6791 descriptors->CopyUpTo(number_of_own_descriptors); 6792 if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; 6793 6794 return CopyReplaceDescriptors(new_descriptors, OMIT_TRANSITION); 6795 } 6796 6797 6798 Handle<Map> Map::Copy(Handle<Map> map) { 6799 CALL_HEAP_FUNCTION(map->GetIsolate(), map->Copy(), Map); 6800 } 6801 6802 6803 MaybeObject* Map::Copy() { 6804 DescriptorArray* descriptors = instance_descriptors(); 6805 DescriptorArray* new_descriptors; 6806 int number_of_own_descriptors = NumberOfOwnDescriptors(); 6807 MaybeObject* maybe_descriptors = 6808 descriptors->CopyUpTo(number_of_own_descriptors); 6809 if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; 6810 6811 return CopyReplaceDescriptors(new_descriptors, OMIT_TRANSITION); 6812 } 6813 6814 6815 MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor, 6816 TransitionFlag flag) { 6817 DescriptorArray* descriptors = instance_descriptors(); 6818 6819 // Ensure the key is unique. 6820 MaybeObject* maybe_failure = descriptor->KeyToUniqueName(); 6821 if (maybe_failure->IsFailure()) return maybe_failure; 6822 6823 int old_size = NumberOfOwnDescriptors(); 6824 int new_size = old_size + 1; 6825 6826 if (flag == INSERT_TRANSITION && 6827 owns_descriptors() && 6828 CanHaveMoreTransitions()) { 6829 return ShareDescriptor(descriptors, descriptor); 6830 } 6831 6832 DescriptorArray* new_descriptors; 6833 MaybeObject* maybe_descriptors = DescriptorArray::Allocate(old_size, 1); 6834 if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; 6835 6836 DescriptorArray::WhitenessWitness witness(new_descriptors); 6837 6838 // Copy the descriptors, inserting a descriptor. 6839 for (int i = 0; i < old_size; ++i) { 6840 new_descriptors->CopyFrom(i, descriptors, i, witness); 6841 } 6842 6843 if (old_size != descriptors->number_of_descriptors()) { 6844 new_descriptors->SetNumberOfDescriptors(new_size); 6845 new_descriptors->Set(old_size, descriptor, witness); 6846 new_descriptors->Sort(); 6847 } else { 6848 new_descriptors->Append(descriptor, witness); 6849 } 6850 6851 Name* key = descriptor->GetKey(); 6852 return CopyReplaceDescriptors(new_descriptors, flag, key, SIMPLE_TRANSITION); 6853 } 6854 6855 6856 MaybeObject* Map::CopyInsertDescriptor(Descriptor* descriptor, 6857 TransitionFlag flag) { 6858 DescriptorArray* old_descriptors = instance_descriptors(); 6859 6860 // Ensure the key is unique. 6861 MaybeObject* maybe_result = descriptor->KeyToUniqueName(); 6862 if (maybe_result->IsFailure()) return maybe_result; 6863 6864 // We replace the key if it is already present. 6865 int index = old_descriptors->SearchWithCache(descriptor->GetKey(), this); 6866 if (index != DescriptorArray::kNotFound) { 6867 return CopyReplaceDescriptor(old_descriptors, descriptor, index, flag); 6868 } 6869 return CopyAddDescriptor(descriptor, flag); 6870 } 6871 6872 6873 MaybeObject* DescriptorArray::CopyUpToAddAttributes( 6874 int enumeration_index, PropertyAttributes attributes) { 6875 if (enumeration_index == 0) return GetHeap()->empty_descriptor_array(); 6876 6877 int size = enumeration_index; 6878 6879 DescriptorArray* descriptors; 6880 MaybeObject* maybe_descriptors = Allocate(size); 6881 if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors; 6882 DescriptorArray::WhitenessWitness witness(descriptors); 6883 6884 if (attributes != NONE) { 6885 for (int i = 0; i < size; ++i) { 6886 Object* value = GetValue(i); 6887 PropertyDetails details = GetDetails(i); 6888 int mask = DONT_DELETE | DONT_ENUM; 6889 // READ_ONLY is an invalid attribute for JS setters/getters. 6890 if (details.type() != CALLBACKS || !value->IsAccessorPair()) { 6891 mask |= READ_ONLY; 6892 } 6893 details = details.CopyAddAttributes( 6894 static_cast<PropertyAttributes>(attributes & mask)); 6895 Descriptor desc(GetKey(i), value, details); 6896 descriptors->Set(i, &desc, witness); 6897 } 6898 } else { 6899 for (int i = 0; i < size; ++i) { 6900 descriptors->CopyFrom(i, this, i, witness); 6901 } 6902 } 6903 6904 if (number_of_descriptors() != enumeration_index) descriptors->Sort(); 6905 6906 return descriptors; 6907 } 6908 6909 6910 MaybeObject* Map::CopyReplaceDescriptor(DescriptorArray* descriptors, 6911 Descriptor* descriptor, 6912 int insertion_index, 6913 TransitionFlag flag) { 6914 // Ensure the key is unique. 6915 MaybeObject* maybe_failure = descriptor->KeyToUniqueName(); 6916 if (maybe_failure->IsFailure()) return maybe_failure; 6917 6918 Name* key = descriptor->GetKey(); 6919 ASSERT(key == descriptors->GetKey(insertion_index)); 6920 6921 int new_size = NumberOfOwnDescriptors(); 6922 ASSERT(0 <= insertion_index && insertion_index < new_size); 6923 6924 ASSERT_LT(insertion_index, new_size); 6925 6926 DescriptorArray* new_descriptors; 6927 MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size); 6928 if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; 6929 DescriptorArray::WhitenessWitness witness(new_descriptors); 6930 6931 for (int i = 0; i < new_size; ++i) { 6932 if (i == insertion_index) { 6933 new_descriptors->Set(i, descriptor, witness); 6934 } else { 6935 new_descriptors->CopyFrom(i, descriptors, i, witness); 6936 } 6937 } 6938 6939 // Re-sort if descriptors were removed. 6940 if (new_size != descriptors->length()) new_descriptors->Sort(); 6941 6942 SimpleTransitionFlag simple_flag = 6943 (insertion_index == descriptors->number_of_descriptors() - 1) 6944 ? SIMPLE_TRANSITION 6945 : FULL_TRANSITION; 6946 return CopyReplaceDescriptors(new_descriptors, flag, key, simple_flag); 6947 } 6948 6949 6950 void Map::UpdateCodeCache(Handle<Map> map, 6951 Handle<Name> name, 6952 Handle<Code> code) { 6953 Isolate* isolate = map->GetIsolate(); 6954 CALL_HEAP_FUNCTION_VOID(isolate, 6955 map->UpdateCodeCache(*name, *code)); 6956 } 6957 6958 6959 MaybeObject* Map::UpdateCodeCache(Name* name, Code* code) { 6960 ASSERT(!is_shared() || code->allowed_in_shared_map_code_cache()); 6961 6962 // Allocate the code cache if not present. 6963 if (code_cache()->IsFixedArray()) { 6964 Object* result; 6965 { MaybeObject* maybe_result = GetHeap()->AllocateCodeCache(); 6966 if (!maybe_result->ToObject(&result)) return maybe_result; 6967 } 6968 set_code_cache(result); 6969 } 6970 6971 // Update the code cache. 6972 return CodeCache::cast(code_cache())->Update(name, code); 6973 } 6974 6975 6976 Object* Map::FindInCodeCache(Name* name, Code::Flags flags) { 6977 // Do a lookup if a code cache exists. 6978 if (!code_cache()->IsFixedArray()) { 6979 return CodeCache::cast(code_cache())->Lookup(name, flags); 6980 } else { 6981 return GetHeap()->undefined_value(); 6982 } 6983 } 6984 6985 6986 int Map::IndexInCodeCache(Object* name, Code* code) { 6987 // Get the internal index if a code cache exists. 6988 if (!code_cache()->IsFixedArray()) { 6989 return CodeCache::cast(code_cache())->GetIndex(name, code); 6990 } 6991 return -1; 6992 } 6993 6994 6995 void Map::RemoveFromCodeCache(Name* name, Code* code, int index) { 6996 // No GC is supposed to happen between a call to IndexInCodeCache and 6997 // RemoveFromCodeCache so the code cache must be there. 6998 ASSERT(!code_cache()->IsFixedArray()); 6999 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index); 7000 } 7001 7002 7003 // An iterator over all map transitions in an descriptor array, reusing the map 7004 // field of the contens array while it is running. 7005 class IntrusiveMapTransitionIterator { 7006 public: 7007 explicit IntrusiveMapTransitionIterator(TransitionArray* transition_array) 7008 : transition_array_(transition_array) { } 7009 7010 void Start() { 7011 ASSERT(!IsIterating()); 7012 *TransitionArrayHeader() = Smi::FromInt(0); 7013 } 7014 7015 bool IsIterating() { 7016 return (*TransitionArrayHeader())->IsSmi(); 7017 } 7018 7019 Map* Next() { 7020 ASSERT(IsIterating()); 7021 int index = Smi::cast(*TransitionArrayHeader())->value(); 7022 int number_of_transitions = transition_array_->number_of_transitions(); 7023 while (index < number_of_transitions) { 7024 *TransitionArrayHeader() = Smi::FromInt(index + 1); 7025 return transition_array_->GetTarget(index); 7026 } 7027 7028 *TransitionArrayHeader() = transition_array_->GetHeap()->fixed_array_map(); 7029 return NULL; 7030 } 7031 7032 private: 7033 Object** TransitionArrayHeader() { 7034 return HeapObject::RawField(transition_array_, TransitionArray::kMapOffset); 7035 } 7036 7037 TransitionArray* transition_array_; 7038 }; 7039 7040 7041 // An iterator over all prototype transitions, reusing the map field of the 7042 // underlying array while it is running. 7043 class IntrusivePrototypeTransitionIterator { 7044 public: 7045 explicit IntrusivePrototypeTransitionIterator(HeapObject* proto_trans) 7046 : proto_trans_(proto_trans) { } 7047 7048 void Start() { 7049 ASSERT(!IsIterating()); 7050 *Header() = Smi::FromInt(0); 7051 } 7052 7053 bool IsIterating() { 7054 return (*Header())->IsSmi(); 7055 } 7056 7057 Map* Next() { 7058 ASSERT(IsIterating()); 7059 int transitionNumber = Smi::cast(*Header())->value(); 7060 if (transitionNumber < NumberOfTransitions()) { 7061 *Header() = Smi::FromInt(transitionNumber + 1); 7062 return GetTransition(transitionNumber); 7063 } 7064 *Header() = proto_trans_->GetHeap()->fixed_array_map(); 7065 return NULL; 7066 } 7067 7068 private: 7069 Object** Header() { 7070 return HeapObject::RawField(proto_trans_, FixedArray::kMapOffset); 7071 } 7072 7073 int NumberOfTransitions() { 7074 FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_); 7075 Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset); 7076 return Smi::cast(num)->value(); 7077 } 7078 7079 Map* GetTransition(int transitionNumber) { 7080 FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_); 7081 return Map::cast(proto_trans->get(IndexFor(transitionNumber))); 7082 } 7083 7084 int IndexFor(int transitionNumber) { 7085 return Map::kProtoTransitionHeaderSize + 7086 Map::kProtoTransitionMapOffset + 7087 transitionNumber * Map::kProtoTransitionElementsPerEntry; 7088 } 7089 7090 HeapObject* proto_trans_; 7091 }; 7092 7093 7094 // To traverse the transition tree iteratively, we have to store two kinds of 7095 // information in a map: The parent map in the traversal and which children of a 7096 // node have already been visited. To do this without additional memory, we 7097 // temporarily reuse two maps with known values: 7098 // 7099 // (1) The map of the map temporarily holds the parent, and is restored to the 7100 // meta map afterwards. 7101 // 7102 // (2) The info which children have already been visited depends on which part 7103 // of the map we currently iterate: 7104 // 7105 // (a) If we currently follow normal map transitions, we temporarily store 7106 // the current index in the map of the FixedArray of the desciptor 7107 // array's contents, and restore it to the fixed array map afterwards. 7108 // Note that a single descriptor can have 0, 1, or 2 transitions. 7109 // 7110 // (b) If we currently follow prototype transitions, we temporarily store 7111 // the current index in the map of the FixedArray holding the prototype 7112 // transitions, and restore it to the fixed array map afterwards. 7113 // 7114 // Note that the child iterator is just a concatenation of two iterators: One 7115 // iterating over map transitions and one iterating over prototype transisitons. 7116 class TraversableMap : public Map { 7117 public: 7118 // Record the parent in the traversal within this map. Note that this destroys 7119 // this map's map! 7120 void SetParent(TraversableMap* parent) { set_map_no_write_barrier(parent); } 7121 7122 // Reset the current map's map, returning the parent previously stored in it. 7123 TraversableMap* GetAndResetParent() { 7124 TraversableMap* old_parent = static_cast<TraversableMap*>(map()); 7125 set_map_no_write_barrier(GetHeap()->meta_map()); 7126 return old_parent; 7127 } 7128 7129 // Start iterating over this map's children, possibly destroying a FixedArray 7130 // map (see explanation above). 7131 void ChildIteratorStart() { 7132 if (HasTransitionArray()) { 7133 if (HasPrototypeTransitions()) { 7134 IntrusivePrototypeTransitionIterator(GetPrototypeTransitions()).Start(); 7135 } 7136 7137 IntrusiveMapTransitionIterator(transitions()).Start(); 7138 } 7139 } 7140 7141 // If we have an unvisited child map, return that one and advance. If we have 7142 // none, return NULL and reset any destroyed FixedArray maps. 7143 TraversableMap* ChildIteratorNext() { 7144 TransitionArray* transition_array = unchecked_transition_array(); 7145 if (!transition_array->map()->IsSmi() && 7146 !transition_array->IsTransitionArray()) { 7147 return NULL; 7148 } 7149 7150 if (transition_array->HasPrototypeTransitions()) { 7151 HeapObject* proto_transitions = 7152 transition_array->UncheckedPrototypeTransitions(); 7153 IntrusivePrototypeTransitionIterator proto_iterator(proto_transitions); 7154 if (proto_iterator.IsIterating()) { 7155 Map* next = proto_iterator.Next(); 7156 if (next != NULL) return static_cast<TraversableMap*>(next); 7157 } 7158 } 7159 7160 IntrusiveMapTransitionIterator transition_iterator(transition_array); 7161 if (transition_iterator.IsIterating()) { 7162 Map* next = transition_iterator.Next(); 7163 if (next != NULL) return static_cast<TraversableMap*>(next); 7164 } 7165 7166 return NULL; 7167 } 7168 }; 7169 7170 7171 // Traverse the transition tree in postorder without using the C++ stack by 7172 // doing pointer reversal. 7173 void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { 7174 TraversableMap* current = static_cast<TraversableMap*>(this); 7175 current->ChildIteratorStart(); 7176 while (true) { 7177 TraversableMap* child = current->ChildIteratorNext(); 7178 if (child != NULL) { 7179 child->ChildIteratorStart(); 7180 child->SetParent(current); 7181 current = child; 7182 } else { 7183 TraversableMap* parent = current->GetAndResetParent(); 7184 callback(current, data); 7185 if (current == this) break; 7186 current = parent; 7187 } 7188 } 7189 } 7190 7191 7192 MaybeObject* CodeCache::Update(Name* name, Code* code) { 7193 // The number of monomorphic stubs for normal load/store/call IC's can grow to 7194 // a large number and therefore they need to go into a hash table. They are 7195 // used to load global properties from cells. 7196 if (code->type() == Code::NORMAL) { 7197 // Make sure that a hash table is allocated for the normal load code cache. 7198 if (normal_type_cache()->IsUndefined()) { 7199 Object* result; 7200 { MaybeObject* maybe_result = 7201 CodeCacheHashTable::Allocate(GetHeap(), 7202 CodeCacheHashTable::kInitialSize); 7203 if (!maybe_result->ToObject(&result)) return maybe_result; 7204 } 7205 set_normal_type_cache(result); 7206 } 7207 return UpdateNormalTypeCache(name, code); 7208 } else { 7209 ASSERT(default_cache()->IsFixedArray()); 7210 return UpdateDefaultCache(name, code); 7211 } 7212 } 7213 7214 7215 MaybeObject* CodeCache::UpdateDefaultCache(Name* name, Code* code) { 7216 // When updating the default code cache we disregard the type encoded in the 7217 // flags. This allows call constant stubs to overwrite call field 7218 // stubs, etc. 7219 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags()); 7220 7221 // First check whether we can update existing code cache without 7222 // extending it. 7223 FixedArray* cache = default_cache(); 7224 int length = cache->length(); 7225 int deleted_index = -1; 7226 for (int i = 0; i < length; i += kCodeCacheEntrySize) { 7227 Object* key = cache->get(i); 7228 if (key->IsNull()) { 7229 if (deleted_index < 0) deleted_index = i; 7230 continue; 7231 } 7232 if (key->IsUndefined()) { 7233 if (deleted_index >= 0) i = deleted_index; 7234 cache->set(i + kCodeCacheEntryNameOffset, name); 7235 cache->set(i + kCodeCacheEntryCodeOffset, code); 7236 return this; 7237 } 7238 if (name->Equals(Name::cast(key))) { 7239 Code::Flags found = 7240 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags(); 7241 if (Code::RemoveTypeFromFlags(found) == flags) { 7242 cache->set(i + kCodeCacheEntryCodeOffset, code); 7243 return this; 7244 } 7245 } 7246 } 7247 7248 // Reached the end of the code cache. If there were deleted 7249 // elements, reuse the space for the first of them. 7250 if (deleted_index >= 0) { 7251 cache->set(deleted_index + kCodeCacheEntryNameOffset, name); 7252 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code); 7253 return this; 7254 } 7255 7256 // Extend the code cache with some new entries (at least one). Must be a 7257 // multiple of the entry size. 7258 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize; 7259 new_length = new_length - new_length % kCodeCacheEntrySize; 7260 ASSERT((new_length % kCodeCacheEntrySize) == 0); 7261 Object* result; 7262 { MaybeObject* maybe_result = cache->CopySize(new_length); 7263 if (!maybe_result->ToObject(&result)) return maybe_result; 7264 } 7265 7266 // Add the (name, code) pair to the new cache. 7267 cache = FixedArray::cast(result); 7268 cache->set(length + kCodeCacheEntryNameOffset, name); 7269 cache->set(length + kCodeCacheEntryCodeOffset, code); 7270 set_default_cache(cache); 7271 return this; 7272 } 7273 7274 7275 MaybeObject* CodeCache::UpdateNormalTypeCache(Name* name, Code* code) { 7276 // Adding a new entry can cause a new cache to be allocated. 7277 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); 7278 Object* new_cache; 7279 { MaybeObject* maybe_new_cache = cache->Put(name, code); 7280 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache; 7281 } 7282 set_normal_type_cache(new_cache); 7283 return this; 7284 } 7285 7286 7287 Object* CodeCache::Lookup(Name* name, Code::Flags flags) { 7288 if (Code::ExtractTypeFromFlags(flags) == Code::NORMAL) { 7289 return LookupNormalTypeCache(name, flags); 7290 } else { 7291 return LookupDefaultCache(name, flags); 7292 } 7293 } 7294 7295 7296 Object* CodeCache::LookupDefaultCache(Name* name, Code::Flags flags) { 7297 FixedArray* cache = default_cache(); 7298 int length = cache->length(); 7299 for (int i = 0; i < length; i += kCodeCacheEntrySize) { 7300 Object* key = cache->get(i + kCodeCacheEntryNameOffset); 7301 // Skip deleted elements. 7302 if (key->IsNull()) continue; 7303 if (key->IsUndefined()) return key; 7304 if (name->Equals(Name::cast(key))) { 7305 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset)); 7306 if (code->flags() == flags) { 7307 return code; 7308 } 7309 } 7310 } 7311 return GetHeap()->undefined_value(); 7312 } 7313 7314 7315 Object* CodeCache::LookupNormalTypeCache(Name* name, Code::Flags flags) { 7316 if (!normal_type_cache()->IsUndefined()) { 7317 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); 7318 return cache->Lookup(name, flags); 7319 } else { 7320 return GetHeap()->undefined_value(); 7321 } 7322 } 7323 7324 7325 int CodeCache::GetIndex(Object* name, Code* code) { 7326 if (code->type() == Code::NORMAL) { 7327 if (normal_type_cache()->IsUndefined()) return -1; 7328 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); 7329 return cache->GetIndex(Name::cast(name), code->flags()); 7330 } 7331 7332 FixedArray* array = default_cache(); 7333 int len = array->length(); 7334 for (int i = 0; i < len; i += kCodeCacheEntrySize) { 7335 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1; 7336 } 7337 return -1; 7338 } 7339 7340 7341 void CodeCache::RemoveByIndex(Object* name, Code* code, int index) { 7342 if (code->type() == Code::NORMAL) { 7343 ASSERT(!normal_type_cache()->IsUndefined()); 7344 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); 7345 ASSERT(cache->GetIndex(Name::cast(name), code->flags()) == index); 7346 cache->RemoveByIndex(index); 7347 } else { 7348 FixedArray* array = default_cache(); 7349 ASSERT(array->length() >= index && array->get(index)->IsCode()); 7350 // Use null instead of undefined for deleted elements to distinguish 7351 // deleted elements from unused elements. This distinction is used 7352 // when looking up in the cache and when updating the cache. 7353 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset); 7354 array->set_null(index - 1); // Name. 7355 array->set_null(index); // Code. 7356 } 7357 } 7358 7359 7360 // The key in the code cache hash table consists of the property name and the 7361 // code object. The actual match is on the name and the code flags. If a key 7362 // is created using the flags and not a code object it can only be used for 7363 // lookup not to create a new entry. 7364 class CodeCacheHashTableKey : public HashTableKey { 7365 public: 7366 CodeCacheHashTableKey(Name* name, Code::Flags flags) 7367 : name_(name), flags_(flags), code_(NULL) { } 7368 7369 CodeCacheHashTableKey(Name* name, Code* code) 7370 : name_(name), 7371 flags_(code->flags()), 7372 code_(code) { } 7373 7374 7375 bool IsMatch(Object* other) { 7376 if (!other->IsFixedArray()) return false; 7377 FixedArray* pair = FixedArray::cast(other); 7378 Name* name = Name::cast(pair->get(0)); 7379 Code::Flags flags = Code::cast(pair->get(1))->flags(); 7380 if (flags != flags_) { 7381 return false; 7382 } 7383 return name_->Equals(name); 7384 } 7385 7386 static uint32_t NameFlagsHashHelper(Name* name, Code::Flags flags) { 7387 return name->Hash() ^ flags; 7388 } 7389 7390 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); } 7391 7392 uint32_t HashForObject(Object* obj) { 7393 FixedArray* pair = FixedArray::cast(obj); 7394 Name* name = Name::cast(pair->get(0)); 7395 Code* code = Code::cast(pair->get(1)); 7396 return NameFlagsHashHelper(name, code->flags()); 7397 } 7398 7399 MUST_USE_RESULT MaybeObject* AsObject(Heap* heap) { 7400 ASSERT(code_ != NULL); 7401 Object* obj; 7402 { MaybeObject* maybe_obj = heap->AllocateFixedArray(2); 7403 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 7404 } 7405 FixedArray* pair = FixedArray::cast(obj); 7406 pair->set(0, name_); 7407 pair->set(1, code_); 7408 return pair; 7409 } 7410 7411 private: 7412 Name* name_; 7413 Code::Flags flags_; 7414 // TODO(jkummerow): We should be able to get by without this. 7415 Code* code_; 7416 }; 7417 7418 7419 Object* CodeCacheHashTable::Lookup(Name* name, Code::Flags flags) { 7420 CodeCacheHashTableKey key(name, flags); 7421 int entry = FindEntry(&key); 7422 if (entry == kNotFound) return GetHeap()->undefined_value(); 7423 return get(EntryToIndex(entry) + 1); 7424 } 7425 7426 7427 MaybeObject* CodeCacheHashTable::Put(Name* name, Code* code) { 7428 CodeCacheHashTableKey key(name, code); 7429 Object* obj; 7430 { MaybeObject* maybe_obj = EnsureCapacity(1, &key); 7431 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 7432 } 7433 7434 // Don't use |this|, as the table might have grown. 7435 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj); 7436 7437 int entry = cache->FindInsertionEntry(key.Hash()); 7438 Object* k; 7439 { MaybeObject* maybe_k = key.AsObject(GetHeap()); 7440 if (!maybe_k->ToObject(&k)) return maybe_k; 7441 } 7442 7443 cache->set(EntryToIndex(entry), k); 7444 cache->set(EntryToIndex(entry) + 1, code); 7445 cache->ElementAdded(); 7446 return cache; 7447 } 7448 7449 7450 int CodeCacheHashTable::GetIndex(Name* name, Code::Flags flags) { 7451 CodeCacheHashTableKey key(name, flags); 7452 int entry = FindEntry(&key); 7453 return (entry == kNotFound) ? -1 : entry; 7454 } 7455 7456 7457 void CodeCacheHashTable::RemoveByIndex(int index) { 7458 ASSERT(index >= 0); 7459 Heap* heap = GetHeap(); 7460 set(EntryToIndex(index), heap->the_hole_value()); 7461 set(EntryToIndex(index) + 1, heap->the_hole_value()); 7462 ElementRemoved(); 7463 } 7464 7465 7466 void PolymorphicCodeCache::Update(Handle<PolymorphicCodeCache> cache, 7467 MapHandleList* maps, 7468 Code::Flags flags, 7469 Handle<Code> code) { 7470 Isolate* isolate = cache->GetIsolate(); 7471 CALL_HEAP_FUNCTION_VOID(isolate, cache->Update(maps, flags, *code)); 7472 } 7473 7474 7475 MaybeObject* PolymorphicCodeCache::Update(MapHandleList* maps, 7476 Code::Flags flags, 7477 Code* code) { 7478 // Initialize cache if necessary. 7479 if (cache()->IsUndefined()) { 7480 Object* result; 7481 { MaybeObject* maybe_result = 7482 PolymorphicCodeCacheHashTable::Allocate( 7483 GetHeap(), 7484 PolymorphicCodeCacheHashTable::kInitialSize); 7485 if (!maybe_result->ToObject(&result)) return maybe_result; 7486 } 7487 set_cache(result); 7488 } else { 7489 // This entry shouldn't be contained in the cache yet. 7490 ASSERT(PolymorphicCodeCacheHashTable::cast(cache()) 7491 ->Lookup(maps, flags)->IsUndefined()); 7492 } 7493 PolymorphicCodeCacheHashTable* hash_table = 7494 PolymorphicCodeCacheHashTable::cast(cache()); 7495 Object* new_cache; 7496 { MaybeObject* maybe_new_cache = hash_table->Put(maps, flags, code); 7497 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache; 7498 } 7499 set_cache(new_cache); 7500 return this; 7501 } 7502 7503 7504 Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps, 7505 Code::Flags flags) { 7506 if (!cache()->IsUndefined()) { 7507 PolymorphicCodeCacheHashTable* hash_table = 7508 PolymorphicCodeCacheHashTable::cast(cache()); 7509 return Handle<Object>(hash_table->Lookup(maps, flags), GetIsolate()); 7510 } else { 7511 return GetIsolate()->factory()->undefined_value(); 7512 } 7513 } 7514 7515 7516 // Despite their name, object of this class are not stored in the actual 7517 // hash table; instead they're temporarily used for lookups. It is therefore 7518 // safe to have a weak (non-owning) pointer to a MapList as a member field. 7519 class PolymorphicCodeCacheHashTableKey : public HashTableKey { 7520 public: 7521 // Callers must ensure that |maps| outlives the newly constructed object. 7522 PolymorphicCodeCacheHashTableKey(MapHandleList* maps, int code_flags) 7523 : maps_(maps), 7524 code_flags_(code_flags) {} 7525 7526 bool IsMatch(Object* other) { 7527 MapHandleList other_maps(kDefaultListAllocationSize); 7528 int other_flags; 7529 FromObject(other, &other_flags, &other_maps); 7530 if (code_flags_ != other_flags) return false; 7531 if (maps_->length() != other_maps.length()) return false; 7532 // Compare just the hashes first because it's faster. 7533 int this_hash = MapsHashHelper(maps_, code_flags_); 7534 int other_hash = MapsHashHelper(&other_maps, other_flags); 7535 if (this_hash != other_hash) return false; 7536 7537 // Full comparison: for each map in maps_, look for an equivalent map in 7538 // other_maps. This implementation is slow, but probably good enough for 7539 // now because the lists are short (<= 4 elements currently). 7540 for (int i = 0; i < maps_->length(); ++i) { 7541 bool match_found = false; 7542 for (int j = 0; j < other_maps.length(); ++j) { 7543 if (*(maps_->at(i)) == *(other_maps.at(j))) { 7544 match_found = true; 7545 break; 7546 } 7547 } 7548 if (!match_found) return false; 7549 } 7550 return true; 7551 } 7552 7553 static uint32_t MapsHashHelper(MapHandleList* maps, int code_flags) { 7554 uint32_t hash = code_flags; 7555 for (int i = 0; i < maps->length(); ++i) { 7556 hash ^= maps->at(i)->Hash(); 7557 } 7558 return hash; 7559 } 7560 7561 uint32_t Hash() { 7562 return MapsHashHelper(maps_, code_flags_); 7563 } 7564 7565 uint32_t HashForObject(Object* obj) { 7566 MapHandleList other_maps(kDefaultListAllocationSize); 7567 int other_flags; 7568 FromObject(obj, &other_flags, &other_maps); 7569 return MapsHashHelper(&other_maps, other_flags); 7570 } 7571 7572 MUST_USE_RESULT MaybeObject* AsObject(Heap* heap) { 7573 Object* obj; 7574 // The maps in |maps_| must be copied to a newly allocated FixedArray, 7575 // both because the referenced MapList is short-lived, and because C++ 7576 // objects can't be stored in the heap anyway. 7577 { MaybeObject* maybe_obj = 7578 heap->AllocateUninitializedFixedArray(maps_->length() + 1); 7579 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 7580 } 7581 FixedArray* list = FixedArray::cast(obj); 7582 list->set(0, Smi::FromInt(code_flags_)); 7583 for (int i = 0; i < maps_->length(); ++i) { 7584 list->set(i + 1, *maps_->at(i)); 7585 } 7586 return list; 7587 } 7588 7589 private: 7590 static MapHandleList* FromObject(Object* obj, 7591 int* code_flags, 7592 MapHandleList* maps) { 7593 FixedArray* list = FixedArray::cast(obj); 7594 maps->Rewind(0); 7595 *code_flags = Smi::cast(list->get(0))->value(); 7596 for (int i = 1; i < list->length(); ++i) { 7597 maps->Add(Handle<Map>(Map::cast(list->get(i)))); 7598 } 7599 return maps; 7600 } 7601 7602 MapHandleList* maps_; // weak. 7603 int code_flags_; 7604 static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1; 7605 }; 7606 7607 7608 Object* PolymorphicCodeCacheHashTable::Lookup(MapHandleList* maps, 7609 int code_flags) { 7610 PolymorphicCodeCacheHashTableKey key(maps, code_flags); 7611 int entry = FindEntry(&key); 7612 if (entry == kNotFound) return GetHeap()->undefined_value(); 7613 return get(EntryToIndex(entry) + 1); 7614 } 7615 7616 7617 MaybeObject* PolymorphicCodeCacheHashTable::Put(MapHandleList* maps, 7618 int code_flags, 7619 Code* code) { 7620 PolymorphicCodeCacheHashTableKey key(maps, code_flags); 7621 Object* obj; 7622 { MaybeObject* maybe_obj = EnsureCapacity(1, &key); 7623 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 7624 } 7625 PolymorphicCodeCacheHashTable* cache = 7626 reinterpret_cast<PolymorphicCodeCacheHashTable*>(obj); 7627 int entry = cache->FindInsertionEntry(key.Hash()); 7628 { MaybeObject* maybe_obj = key.AsObject(GetHeap()); 7629 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 7630 } 7631 cache->set(EntryToIndex(entry), obj); 7632 cache->set(EntryToIndex(entry) + 1, code); 7633 cache->ElementAdded(); 7634 return cache; 7635 } 7636 7637 7638 MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) { 7639 ElementsAccessor* accessor = array->GetElementsAccessor(); 7640 MaybeObject* maybe_result = 7641 accessor->AddElementsToFixedArray(array, array, this); 7642 FixedArray* result; 7643 if (!maybe_result->To<FixedArray>(&result)) return maybe_result; 7644 #ifdef DEBUG 7645 if (FLAG_enable_slow_asserts) { 7646 for (int i = 0; i < result->length(); i++) { 7647 Object* current = result->get(i); 7648 ASSERT(current->IsNumber() || current->IsName()); 7649 } 7650 } 7651 #endif 7652 return result; 7653 } 7654 7655 7656 MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) { 7657 ElementsAccessor* accessor = ElementsAccessor::ForArray(other); 7658 MaybeObject* maybe_result = 7659 accessor->AddElementsToFixedArray(NULL, NULL, this, other); 7660 FixedArray* result; 7661 if (!maybe_result->To(&result)) return maybe_result; 7662 #ifdef DEBUG 7663 if (FLAG_enable_slow_asserts) { 7664 for (int i = 0; i < result->length(); i++) { 7665 Object* current = result->get(i); 7666 ASSERT(current->IsNumber() || current->IsName()); 7667 } 7668 } 7669 #endif 7670 return result; 7671 } 7672 7673 7674 MaybeObject* FixedArray::CopySize(int new_length) { 7675 Heap* heap = GetHeap(); 7676 if (new_length == 0) return heap->empty_fixed_array(); 7677 Object* obj; 7678 { MaybeObject* maybe_obj = heap->AllocateFixedArray(new_length); 7679 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 7680 } 7681 FixedArray* result = FixedArray::cast(obj); 7682 // Copy the content 7683 DisallowHeapAllocation no_gc; 7684 int len = length(); 7685 if (new_length < len) len = new_length; 7686 // We are taking the map from the old fixed array so the map is sure to 7687 // be an immortal immutable object. 7688 result->set_map_no_write_barrier(map()); 7689 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); 7690 for (int i = 0; i < len; i++) { 7691 result->set(i, get(i), mode); 7692 } 7693 return result; 7694 } 7695 7696 7697 void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) { 7698 DisallowHeapAllocation no_gc; 7699 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc); 7700 for (int index = 0; index < len; index++) { 7701 dest->set(dest_pos+index, get(pos+index), mode); 7702 } 7703 } 7704 7705 7706 #ifdef DEBUG 7707 bool FixedArray::IsEqualTo(FixedArray* other) { 7708 if (length() != other->length()) return false; 7709 for (int i = 0 ; i < length(); ++i) { 7710 if (get(i) != other->get(i)) return false; 7711 } 7712 return true; 7713 } 7714 #endif 7715 7716 7717 MaybeObject* DescriptorArray::Allocate(int number_of_descriptors, int slack) { 7718 Heap* heap = Isolate::Current()->heap(); 7719 // Do not use DescriptorArray::cast on incomplete object. 7720 int size = number_of_descriptors + slack; 7721 if (size == 0) return heap->empty_descriptor_array(); 7722 FixedArray* result; 7723 // Allocate the array of keys. 7724 MaybeObject* maybe_array = heap->AllocateFixedArray(LengthFor(size)); 7725 if (!maybe_array->To(&result)) return maybe_array; 7726 7727 result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors)); 7728 result->set(kEnumCacheIndex, Smi::FromInt(0)); 7729 return result; 7730 } 7731 7732 7733 void DescriptorArray::ClearEnumCache() { 7734 set(kEnumCacheIndex, Smi::FromInt(0)); 7735 } 7736 7737 7738 void DescriptorArray::SetEnumCache(FixedArray* bridge_storage, 7739 FixedArray* new_cache, 7740 Object* new_index_cache) { 7741 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength); 7742 ASSERT(new_index_cache->IsSmi() || new_index_cache->IsFixedArray()); 7743 ASSERT(!IsEmpty()); 7744 ASSERT(!HasEnumCache() || new_cache->length() > GetEnumCache()->length()); 7745 FixedArray::cast(bridge_storage)-> 7746 set(kEnumCacheBridgeCacheIndex, new_cache); 7747 FixedArray::cast(bridge_storage)-> 7748 set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache); 7749 set(kEnumCacheIndex, bridge_storage); 7750 } 7751 7752 7753 void DescriptorArray::CopyFrom(int dst_index, 7754 DescriptorArray* src, 7755 int src_index, 7756 const WhitenessWitness& witness) { 7757 Object* value = src->GetValue(src_index); 7758 PropertyDetails details = src->GetDetails(src_index); 7759 Descriptor desc(src->GetKey(src_index), value, details); 7760 Set(dst_index, &desc, witness); 7761 } 7762 7763 7764 // Generalize the |other| descriptor array by merging it into the (at least 7765 // partly) updated |this| descriptor array. 7766 // The method merges two descriptor array in three parts. Both descriptor arrays 7767 // are identical up to |verbatim|. They also overlap in keys up to |valid|. 7768 // Between |verbatim| and |valid|, the resulting descriptor type as well as the 7769 // representation are generalized from both |this| and |other|. Beyond |valid|, 7770 // the descriptors are copied verbatim from |other| up to |new_size|. 7771 // In case of incompatible types, the type and representation of |other| is 7772 // used. 7773 MaybeObject* DescriptorArray::Merge(int verbatim, 7774 int valid, 7775 int new_size, 7776 DescriptorArray* other) { 7777 ASSERT(verbatim <= valid); 7778 ASSERT(valid <= new_size); 7779 7780 DescriptorArray* result; 7781 // Allocate a new descriptor array large enough to hold the required 7782 // descriptors, with minimally the exact same size as this descriptor array. 7783 MaybeObject* maybe_descriptors = DescriptorArray::Allocate( 7784 new_size, Max(new_size, other->number_of_descriptors()) - new_size); 7785 if (!maybe_descriptors->To(&result)) return maybe_descriptors; 7786 ASSERT(result->length() > length() || 7787 result->NumberOfSlackDescriptors() > 0 || 7788 result->number_of_descriptors() == other->number_of_descriptors()); 7789 ASSERT(result->number_of_descriptors() == new_size); 7790 7791 DescriptorArray::WhitenessWitness witness(result); 7792 7793 int descriptor; 7794 7795 // 0 -> |verbatim| 7796 int current_offset = 0; 7797 for (descriptor = 0; descriptor < verbatim; descriptor++) { 7798 if (GetDetails(descriptor).type() == FIELD) current_offset++; 7799 result->CopyFrom(descriptor, this, descriptor, witness); 7800 } 7801 7802 // |verbatim| -> |valid| 7803 for (; descriptor < valid; descriptor++) { 7804 Name* key = GetKey(descriptor); 7805 PropertyDetails details = GetDetails(descriptor); 7806 PropertyDetails other_details = other->GetDetails(descriptor); 7807 7808 if (details.type() == FIELD || other_details.type() == FIELD || 7809 (details.type() == CONSTANT && 7810 other_details.type() == CONSTANT && 7811 GetValue(descriptor) != other->GetValue(descriptor))) { 7812 Representation representation = 7813 details.representation().generalize(other_details.representation()); 7814 FieldDescriptor d(key, 7815 current_offset++, 7816 other_details.attributes(), 7817 representation); 7818 result->Set(descriptor, &d, witness); 7819 } else { 7820 result->CopyFrom(descriptor, other, descriptor, witness); 7821 } 7822 } 7823 7824 // |valid| -> |new_size| 7825 for (; descriptor < new_size; descriptor++) { 7826 PropertyDetails details = other->GetDetails(descriptor); 7827 if (details.type() == FIELD) { 7828 Name* key = other->GetKey(descriptor); 7829 FieldDescriptor d(key, 7830 current_offset++, 7831 details.attributes(), 7832 details.representation()); 7833 result->Set(descriptor, &d, witness); 7834 } else { 7835 result->CopyFrom(descriptor, other, descriptor, witness); 7836 } 7837 } 7838 7839 result->Sort(); 7840 return result; 7841 } 7842 7843 7844 // Checks whether a merge of |other| into |this| would return a copy of |this|. 7845 bool DescriptorArray::IsMoreGeneralThan(int verbatim, 7846 int valid, 7847 int new_size, 7848 DescriptorArray* other) { 7849 ASSERT(verbatim <= valid); 7850 ASSERT(valid <= new_size); 7851 if (valid != new_size) return false; 7852 7853 for (int descriptor = verbatim; descriptor < valid; descriptor++) { 7854 PropertyDetails details = GetDetails(descriptor); 7855 PropertyDetails other_details = other->GetDetails(descriptor); 7856 if (!other_details.representation().fits_into(details.representation())) { 7857 return false; 7858 } 7859 if (details.type() == CONSTANT) { 7860 if (other_details.type() != CONSTANT) return false; 7861 if (GetValue(descriptor) != other->GetValue(descriptor)) return false; 7862 } 7863 } 7864 7865 return true; 7866 } 7867 7868 7869 // We need the whiteness witness since sort will reshuffle the entries in the 7870 // descriptor array. If the descriptor array were to be black, the shuffling 7871 // would move a slot that was already recorded as pointing into an evacuation 7872 // candidate. This would result in missing updates upon evacuation. 7873 void DescriptorArray::Sort() { 7874 // In-place heap sort. 7875 int len = number_of_descriptors(); 7876 // Reset sorting since the descriptor array might contain invalid pointers. 7877 for (int i = 0; i < len; ++i) SetSortedKey(i, i); 7878 // Bottom-up max-heap construction. 7879 // Index of the last node with children 7880 const int max_parent_index = (len / 2) - 1; 7881 for (int i = max_parent_index; i >= 0; --i) { 7882 int parent_index = i; 7883 const uint32_t parent_hash = GetSortedKey(i)->Hash(); 7884 while (parent_index <= max_parent_index) { 7885 int child_index = 2 * parent_index + 1; 7886 uint32_t child_hash = GetSortedKey(child_index)->Hash(); 7887 if (child_index + 1 < len) { 7888 uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash(); 7889 if (right_child_hash > child_hash) { 7890 child_index++; 7891 child_hash = right_child_hash; 7892 } 7893 } 7894 if (child_hash <= parent_hash) break; 7895 SwapSortedKeys(parent_index, child_index); 7896 // Now element at child_index could be < its children. 7897 parent_index = child_index; // parent_hash remains correct. 7898 } 7899 } 7900 7901 // Extract elements and create sorted array. 7902 for (int i = len - 1; i > 0; --i) { 7903 // Put max element at the back of the array. 7904 SwapSortedKeys(0, i); 7905 // Shift down the new top element. 7906 int parent_index = 0; 7907 const uint32_t parent_hash = GetSortedKey(parent_index)->Hash(); 7908 const int max_parent_index = (i / 2) - 1; 7909 while (parent_index <= max_parent_index) { 7910 int child_index = parent_index * 2 + 1; 7911 uint32_t child_hash = GetSortedKey(child_index)->Hash(); 7912 if (child_index + 1 < i) { 7913 uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash(); 7914 if (right_child_hash > child_hash) { 7915 child_index++; 7916 child_hash = right_child_hash; 7917 } 7918 } 7919 if (child_hash <= parent_hash) break; 7920 SwapSortedKeys(parent_index, child_index); 7921 parent_index = child_index; 7922 } 7923 } 7924 ASSERT(IsSortedNoDuplicates()); 7925 } 7926 7927 7928 Handle<AccessorPair> AccessorPair::Copy(Handle<AccessorPair> pair) { 7929 Handle<AccessorPair> copy = pair->GetIsolate()->factory()->NewAccessorPair(); 7930 copy->set_getter(pair->getter()); 7931 copy->set_setter(pair->setter()); 7932 return copy; 7933 } 7934 7935 7936 Object* AccessorPair::GetComponent(AccessorComponent component) { 7937 Object* accessor = get(component); 7938 return accessor->IsTheHole() ? GetHeap()->undefined_value() : accessor; 7939 } 7940 7941 7942 MaybeObject* DeoptimizationInputData::Allocate(int deopt_entry_count, 7943 PretenureFlag pretenure) { 7944 ASSERT(deopt_entry_count > 0); 7945 return HEAP->AllocateFixedArray(LengthFor(deopt_entry_count), 7946 pretenure); 7947 } 7948 7949 7950 MaybeObject* DeoptimizationOutputData::Allocate(int number_of_deopt_points, 7951 PretenureFlag pretenure) { 7952 if (number_of_deopt_points == 0) return HEAP->empty_fixed_array(); 7953 return HEAP->AllocateFixedArray(LengthOfFixedArray(number_of_deopt_points), 7954 pretenure); 7955 } 7956 7957 7958 #ifdef DEBUG 7959 bool DescriptorArray::IsEqualTo(DescriptorArray* other) { 7960 if (IsEmpty()) return other->IsEmpty(); 7961 if (other->IsEmpty()) return false; 7962 if (length() != other->length()) return false; 7963 for (int i = 0; i < length(); ++i) { 7964 if (get(i) != other->get(i)) return false; 7965 } 7966 return true; 7967 } 7968 #endif 7969 7970 7971 bool String::LooksValid() { 7972 if (!Isolate::Current()->heap()->Contains(this)) return false; 7973 return true; 7974 } 7975 7976 7977 String::FlatContent String::GetFlatContent() { 7978 ASSERT(!AllowHeapAllocation::IsAllowed()); 7979 int length = this->length(); 7980 StringShape shape(this); 7981 String* string = this; 7982 int offset = 0; 7983 if (shape.representation_tag() == kConsStringTag) { 7984 ConsString* cons = ConsString::cast(string); 7985 if (cons->second()->length() != 0) { 7986 return FlatContent(); 7987 } 7988 string = cons->first(); 7989 shape = StringShape(string); 7990 } 7991 if (shape.representation_tag() == kSlicedStringTag) { 7992 SlicedString* slice = SlicedString::cast(string); 7993 offset = slice->offset(); 7994 string = slice->parent(); 7995 shape = StringShape(string); 7996 ASSERT(shape.representation_tag() != kConsStringTag && 7997 shape.representation_tag() != kSlicedStringTag); 7998 } 7999 if (shape.encoding_tag() == kOneByteStringTag) { 8000 const uint8_t* start; 8001 if (shape.representation_tag() == kSeqStringTag) { 8002 start = SeqOneByteString::cast(string)->GetChars(); 8003 } else { 8004 start = ExternalAsciiString::cast(string)->GetChars(); 8005 } 8006 return FlatContent(Vector<const uint8_t>(start + offset, length)); 8007 } else { 8008 ASSERT(shape.encoding_tag() == kTwoByteStringTag); 8009 const uc16* start; 8010 if (shape.representation_tag() == kSeqStringTag) { 8011 start = SeqTwoByteString::cast(string)->GetChars(); 8012 } else { 8013 start = ExternalTwoByteString::cast(string)->GetChars(); 8014 } 8015 return FlatContent(Vector<const uc16>(start + offset, length)); 8016 } 8017 } 8018 8019 8020 SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, 8021 RobustnessFlag robust_flag, 8022 int offset, 8023 int length, 8024 int* length_return) { 8025 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { 8026 return SmartArrayPointer<char>(NULL); 8027 } 8028 Heap* heap = GetHeap(); 8029 8030 // Negative length means the to the end of the string. 8031 if (length < 0) length = kMaxInt - offset; 8032 8033 // Compute the size of the UTF-8 string. Start at the specified offset. 8034 Access<ConsStringIteratorOp> op( 8035 heap->isolate()->objects_string_iterator()); 8036 StringCharacterStream stream(this, op.value(), offset); 8037 int character_position = offset; 8038 int utf8_bytes = 0; 8039 int last = unibrow::Utf16::kNoPreviousCharacter; 8040 while (stream.HasMore() && character_position++ < offset + length) { 8041 uint16_t character = stream.GetNext(); 8042 utf8_bytes += unibrow::Utf8::Length(character, last); 8043 last = character; 8044 } 8045 8046 if (length_return) { 8047 *length_return = utf8_bytes; 8048 } 8049 8050 char* result = NewArray<char>(utf8_bytes + 1); 8051 8052 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset. 8053 stream.Reset(this, offset); 8054 character_position = offset; 8055 int utf8_byte_position = 0; 8056 last = unibrow::Utf16::kNoPreviousCharacter; 8057 while (stream.HasMore() && character_position++ < offset + length) { 8058 uint16_t character = stream.GetNext(); 8059 if (allow_nulls == DISALLOW_NULLS && character == 0) { 8060 character = ' '; 8061 } 8062 utf8_byte_position += 8063 unibrow::Utf8::Encode(result + utf8_byte_position, character, last); 8064 last = character; 8065 } 8066 result[utf8_byte_position] = 0; 8067 return SmartArrayPointer<char>(result); 8068 } 8069 8070 8071 SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, 8072 RobustnessFlag robust_flag, 8073 int* length_return) { 8074 return ToCString(allow_nulls, robust_flag, 0, -1, length_return); 8075 } 8076 8077 8078 const uc16* String::GetTwoByteData() { 8079 return GetTwoByteData(0); 8080 } 8081 8082 8083 const uc16* String::GetTwoByteData(unsigned start) { 8084 ASSERT(!IsOneByteRepresentationUnderneath()); 8085 switch (StringShape(this).representation_tag()) { 8086 case kSeqStringTag: 8087 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start); 8088 case kExternalStringTag: 8089 return ExternalTwoByteString::cast(this)-> 8090 ExternalTwoByteStringGetData(start); 8091 case kSlicedStringTag: { 8092 SlicedString* slice = SlicedString::cast(this); 8093 return slice->parent()->GetTwoByteData(start + slice->offset()); 8094 } 8095 case kConsStringTag: 8096 UNREACHABLE(); 8097 return NULL; 8098 } 8099 UNREACHABLE(); 8100 return NULL; 8101 } 8102 8103 8104 SmartArrayPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) { 8105 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { 8106 return SmartArrayPointer<uc16>(); 8107 } 8108 Heap* heap = GetHeap(); 8109 8110 Access<ConsStringIteratorOp> op( 8111 heap->isolate()->objects_string_iterator()); 8112 StringCharacterStream stream(this, op.value()); 8113 8114 uc16* result = NewArray<uc16>(length() + 1); 8115 8116 int i = 0; 8117 while (stream.HasMore()) { 8118 uint16_t character = stream.GetNext(); 8119 result[i++] = character; 8120 } 8121 result[i] = 0; 8122 return SmartArrayPointer<uc16>(result); 8123 } 8124 8125 8126 const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) { 8127 return reinterpret_cast<uc16*>( 8128 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start; 8129 } 8130 8131 8132 void Relocatable::PostGarbageCollectionProcessing() { 8133 Isolate* isolate = Isolate::Current(); 8134 Relocatable* current = isolate->relocatable_top(); 8135 while (current != NULL) { 8136 current->PostGarbageCollection(); 8137 current = current->prev_; 8138 } 8139 } 8140 8141 8142 // Reserve space for statics needing saving and restoring. 8143 int Relocatable::ArchiveSpacePerThread() { 8144 return sizeof(Isolate::Current()->relocatable_top()); 8145 } 8146 8147 8148 // Archive statics that are thread local. 8149 char* Relocatable::ArchiveState(Isolate* isolate, char* to) { 8150 *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top(); 8151 isolate->set_relocatable_top(NULL); 8152 return to + ArchiveSpacePerThread(); 8153 } 8154 8155 8156 // Restore statics that are thread local. 8157 char* Relocatable::RestoreState(Isolate* isolate, char* from) { 8158 isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from)); 8159 return from + ArchiveSpacePerThread(); 8160 } 8161 8162 8163 char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) { 8164 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage); 8165 Iterate(v, top); 8166 return thread_storage + ArchiveSpacePerThread(); 8167 } 8168 8169 8170 void Relocatable::Iterate(ObjectVisitor* v) { 8171 Isolate* isolate = Isolate::Current(); 8172 Iterate(v, isolate->relocatable_top()); 8173 } 8174 8175 8176 void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) { 8177 Relocatable* current = top; 8178 while (current != NULL) { 8179 current->IterateInstance(v); 8180 current = current->prev_; 8181 } 8182 } 8183 8184 8185 FlatStringReader::FlatStringReader(Isolate* isolate, Handle<String> str) 8186 : Relocatable(isolate), 8187 str_(str.location()), 8188 length_(str->length()) { 8189 PostGarbageCollection(); 8190 } 8191 8192 8193 FlatStringReader::FlatStringReader(Isolate* isolate, Vector<const char> input) 8194 : Relocatable(isolate), 8195 str_(0), 8196 is_ascii_(true), 8197 length_(input.length()), 8198 start_(input.start()) { } 8199 8200 8201 void FlatStringReader::PostGarbageCollection() { 8202 if (str_ == NULL) return; 8203 Handle<String> str(str_); 8204 ASSERT(str->IsFlat()); 8205 DisallowHeapAllocation no_gc; 8206 // This does not actually prevent the vector from being relocated later. 8207 String::FlatContent content = str->GetFlatContent(); 8208 ASSERT(content.IsFlat()); 8209 is_ascii_ = content.IsAscii(); 8210 if (is_ascii_) { 8211 start_ = content.ToOneByteVector().start(); 8212 } else { 8213 start_ = content.ToUC16Vector().start(); 8214 } 8215 } 8216 8217 8218 String* ConsStringIteratorOp::Operate(String* string, 8219 unsigned* offset_out, 8220 int32_t* type_out, 8221 unsigned* length_out) { 8222 ASSERT(string->IsConsString()); 8223 ConsString* cons_string = ConsString::cast(string); 8224 // Set up search data. 8225 root_ = cons_string; 8226 consumed_ = *offset_out; 8227 // Now search. 8228 return Search(offset_out, type_out, length_out); 8229 } 8230 8231 8232 String* ConsStringIteratorOp::Search(unsigned* offset_out, 8233 int32_t* type_out, 8234 unsigned* length_out) { 8235 ConsString* cons_string = root_; 8236 // Reset the stack, pushing the root string. 8237 depth_ = 1; 8238 maximum_depth_ = 1; 8239 frames_[0] = cons_string; 8240 const unsigned consumed = consumed_; 8241 unsigned offset = 0; 8242 while (true) { 8243 // Loop until the string is found which contains the target offset. 8244 String* string = cons_string->first(); 8245 unsigned length = string->length(); 8246 int32_t type; 8247 if (consumed < offset + length) { 8248 // Target offset is in the left branch. 8249 // Keep going if we're still in a ConString. 8250 type = string->map()->instance_type(); 8251 if ((type & kStringRepresentationMask) == kConsStringTag) { 8252 cons_string = ConsString::cast(string); 8253 PushLeft(cons_string); 8254 continue; 8255 } 8256 // Tell the stack we're done decending. 8257 AdjustMaximumDepth(); 8258 } else { 8259 // Descend right. 8260 // Update progress through the string. 8261 offset += length; 8262 // Keep going if we're still in a ConString. 8263 string = cons_string->second(); 8264 type = string->map()->instance_type(); 8265 if ((type & kStringRepresentationMask) == kConsStringTag) { 8266 cons_string = ConsString::cast(string); 8267 PushRight(cons_string); 8268 // TODO(dcarney) Add back root optimization. 8269 continue; 8270 } 8271 // Need this to be updated for the current string. 8272 length = string->length(); 8273 // Account for the possibility of an empty right leaf. 8274 // This happens only if we have asked for an offset outside the string. 8275 if (length == 0) { 8276 // Reset depth so future operations will return null immediately. 8277 Reset(); 8278 return NULL; 8279 } 8280 // Tell the stack we're done decending. 8281 AdjustMaximumDepth(); 8282 // Pop stack so next iteration is in correct place. 8283 Pop(); 8284 } 8285 ASSERT(length != 0); 8286 // Adjust return values and exit. 8287 consumed_ = offset + length; 8288 *offset_out = consumed - offset; 8289 *type_out = type; 8290 *length_out = length; 8291 return string; 8292 } 8293 UNREACHABLE(); 8294 return NULL; 8295 } 8296 8297 8298 String* ConsStringIteratorOp::NextLeaf(bool* blew_stack, 8299 int32_t* type_out, 8300 unsigned* length_out) { 8301 while (true) { 8302 // Tree traversal complete. 8303 if (depth_ == 0) { 8304 *blew_stack = false; 8305 return NULL; 8306 } 8307 // We've lost track of higher nodes. 8308 if (maximum_depth_ - depth_ == kStackSize) { 8309 *blew_stack = true; 8310 return NULL; 8311 } 8312 // Go right. 8313 ConsString* cons_string = frames_[OffsetForDepth(depth_ - 1)]; 8314 String* string = cons_string->second(); 8315 int32_t type = string->map()->instance_type(); 8316 if ((type & kStringRepresentationMask) != kConsStringTag) { 8317 // Pop stack so next iteration is in correct place. 8318 Pop(); 8319 unsigned length = static_cast<unsigned>(string->length()); 8320 // Could be a flattened ConsString. 8321 if (length == 0) continue; 8322 *length_out = length; 8323 *type_out = type; 8324 consumed_ += length; 8325 return string; 8326 } 8327 cons_string = ConsString::cast(string); 8328 // TODO(dcarney) Add back root optimization. 8329 PushRight(cons_string); 8330 // Need to traverse all the way left. 8331 while (true) { 8332 // Continue left. 8333 string = cons_string->first(); 8334 type = string->map()->instance_type(); 8335 if ((type & kStringRepresentationMask) != kConsStringTag) { 8336 AdjustMaximumDepth(); 8337 unsigned length = static_cast<unsigned>(string->length()); 8338 ASSERT(length != 0); 8339 *length_out = length; 8340 *type_out = type; 8341 consumed_ += length; 8342 return string; 8343 } 8344 cons_string = ConsString::cast(string); 8345 PushLeft(cons_string); 8346 } 8347 } 8348 UNREACHABLE(); 8349 return NULL; 8350 } 8351 8352 8353 uint16_t ConsString::ConsStringGet(int index) { 8354 ASSERT(index >= 0 && index < this->length()); 8355 8356 // Check for a flattened cons string 8357 if (second()->length() == 0) { 8358 String* left = first(); 8359 return left->Get(index); 8360 } 8361 8362 String* string = String::cast(this); 8363 8364 while (true) { 8365 if (StringShape(string).IsCons()) { 8366 ConsString* cons_string = ConsString::cast(string); 8367 String* left = cons_string->first(); 8368 if (left->length() > index) { 8369 string = left; 8370 } else { 8371 index -= left->length(); 8372 string = cons_string->second(); 8373 } 8374 } else { 8375 return string->Get(index); 8376 } 8377 } 8378 8379 UNREACHABLE(); 8380 return 0; 8381 } 8382 8383 8384 uint16_t SlicedString::SlicedStringGet(int index) { 8385 return parent()->Get(offset() + index); 8386 } 8387 8388 8389 template <typename sinkchar> 8390 void String::WriteToFlat(String* src, 8391 sinkchar* sink, 8392 int f, 8393 int t) { 8394 String* source = src; 8395 int from = f; 8396 int to = t; 8397 while (true) { 8398 ASSERT(0 <= from && from <= to && to <= source->length()); 8399 switch (StringShape(source).full_representation_tag()) { 8400 case kOneByteStringTag | kExternalStringTag: { 8401 CopyChars(sink, 8402 ExternalAsciiString::cast(source)->GetChars() + from, 8403 to - from); 8404 return; 8405 } 8406 case kTwoByteStringTag | kExternalStringTag: { 8407 const uc16* data = 8408 ExternalTwoByteString::cast(source)->GetChars(); 8409 CopyChars(sink, 8410 data + from, 8411 to - from); 8412 return; 8413 } 8414 case kOneByteStringTag | kSeqStringTag: { 8415 CopyChars(sink, 8416 SeqOneByteString::cast(source)->GetChars() + from, 8417 to - from); 8418 return; 8419 } 8420 case kTwoByteStringTag | kSeqStringTag: { 8421 CopyChars(sink, 8422 SeqTwoByteString::cast(source)->GetChars() + from, 8423 to - from); 8424 return; 8425 } 8426 case kOneByteStringTag | kConsStringTag: 8427 case kTwoByteStringTag | kConsStringTag: { 8428 ConsString* cons_string = ConsString::cast(source); 8429 String* first = cons_string->first(); 8430 int boundary = first->length(); 8431 if (to - boundary >= boundary - from) { 8432 // Right hand side is longer. Recurse over left. 8433 if (from < boundary) { 8434 WriteToFlat(first, sink, from, boundary); 8435 sink += boundary - from; 8436 from = 0; 8437 } else { 8438 from -= boundary; 8439 } 8440 to -= boundary; 8441 source = cons_string->second(); 8442 } else { 8443 // Left hand side is longer. Recurse over right. 8444 if (to > boundary) { 8445 String* second = cons_string->second(); 8446 // When repeatedly appending to a string, we get a cons string that 8447 // is unbalanced to the left, a list, essentially. We inline the 8448 // common case of sequential ascii right child. 8449 if (to - boundary == 1) { 8450 sink[boundary - from] = static_cast<sinkchar>(second->Get(0)); 8451 } else if (second->IsSeqOneByteString()) { 8452 CopyChars(sink + boundary - from, 8453 SeqOneByteString::cast(second)->GetChars(), 8454 to - boundary); 8455 } else { 8456 WriteToFlat(second, 8457 sink + boundary - from, 8458 0, 8459 to - boundary); 8460 } 8461 to = boundary; 8462 } 8463 source = first; 8464 } 8465 break; 8466 } 8467 case kOneByteStringTag | kSlicedStringTag: 8468 case kTwoByteStringTag | kSlicedStringTag: { 8469 SlicedString* slice = SlicedString::cast(