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(source); 8470 unsigned offset = slice->offset(); 8471 WriteToFlat(slice->parent(), sink, from + offset, to + offset); 8472 return; 8473 } 8474 } 8475 } 8476 } 8477 8478 8479 // Compares the contents of two strings by reading and comparing 8480 // int-sized blocks of characters. 8481 template <typename Char> 8482 static inline bool CompareRawStringContents(const Char* const a, 8483 const Char* const b, 8484 int length) { 8485 int i = 0; 8486 #ifndef V8_HOST_CAN_READ_UNALIGNED 8487 // If this architecture isn't comfortable reading unaligned ints 8488 // then we have to check that the strings are aligned before 8489 // comparing them blockwise. 8490 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT 8491 uint32_t pa_addr = reinterpret_cast<uint32_t>(a); 8492 uint32_t pb_addr = reinterpret_cast<uint32_t>(b); 8493 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) { 8494 #endif 8495 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT 8496 int endpoint = length - kStepSize; 8497 // Compare blocks until we reach near the end of the string. 8498 for (; i <= endpoint; i += kStepSize) { 8499 uint32_t wa = *reinterpret_cast<const uint32_t*>(a + i); 8500 uint32_t wb = *reinterpret_cast<const uint32_t*>(b + i); 8501 if (wa != wb) { 8502 return false; 8503 } 8504 } 8505 #ifndef V8_HOST_CAN_READ_UNALIGNED 8506 } 8507 #endif 8508 // Compare the remaining characters that didn't fit into a block. 8509 for (; i < length; i++) { 8510 if (a[i] != b[i]) { 8511 return false; 8512 } 8513 } 8514 return true; 8515 } 8516 8517 8518 template<typename Chars1, typename Chars2> 8519 class RawStringComparator : public AllStatic { 8520 public: 8521 static inline bool compare(const Chars1* a, const Chars2* b, int len) { 8522 ASSERT(sizeof(Chars1) != sizeof(Chars2)); 8523 for (int i = 0; i < len; i++) { 8524 if (a[i] != b[i]) { 8525 return false; 8526 } 8527 } 8528 return true; 8529 } 8530 }; 8531 8532 8533 template<> 8534 class RawStringComparator<uint16_t, uint16_t> { 8535 public: 8536 static inline bool compare(const uint16_t* a, const uint16_t* b, int len) { 8537 return CompareRawStringContents(a, b, len); 8538 } 8539 }; 8540 8541 8542 template<> 8543 class RawStringComparator<uint8_t, uint8_t> { 8544 public: 8545 static inline bool compare(const uint8_t* a, const uint8_t* b, int len) { 8546 return CompareRawStringContents(a, b, len); 8547 } 8548 }; 8549 8550 8551 class StringComparator { 8552 class State { 8553 public: 8554 explicit inline State(ConsStringIteratorOp* op) 8555 : op_(op), is_one_byte_(true), length_(0), buffer8_(NULL) {} 8556 8557 inline void Init(String* string, unsigned len) { 8558 op_->Reset(); 8559 int32_t type = string->map()->instance_type(); 8560 String::Visit(string, 0, *this, *op_, type, len); 8561 } 8562 8563 inline void VisitOneByteString(const uint8_t* chars, unsigned length) { 8564 is_one_byte_ = true; 8565 buffer8_ = chars; 8566 length_ = length; 8567 } 8568 8569 inline void VisitTwoByteString(const uint16_t* chars, unsigned length) { 8570 is_one_byte_ = false; 8571 buffer16_ = chars; 8572 length_ = length; 8573 } 8574 8575 void Advance(unsigned consumed) { 8576 ASSERT(consumed <= length_); 8577 // Still in buffer. 8578 if (length_ != consumed) { 8579 if (is_one_byte_) { 8580 buffer8_ += consumed; 8581 } else { 8582 buffer16_ += consumed; 8583 } 8584 length_ -= consumed; 8585 return; 8586 } 8587 // Advance state. 8588 ASSERT(op_->HasMore()); 8589 int32_t type = 0; 8590 unsigned length = 0; 8591 String* next = op_->ContinueOperation(&type, &length); 8592 ASSERT(next != NULL); 8593 ConsStringNullOp null_op; 8594 String::Visit(next, 0, *this, null_op, type, length); 8595 } 8596 8597 ConsStringIteratorOp* const op_; 8598 bool is_one_byte_; 8599 unsigned length_; 8600 union { 8601 const uint8_t* buffer8_; 8602 const uint16_t* buffer16_; 8603 }; 8604 8605 private: 8606 DISALLOW_IMPLICIT_CONSTRUCTORS(State); 8607 }; 8608 8609 public: 8610 inline StringComparator(ConsStringIteratorOp* op_1, 8611 ConsStringIteratorOp* op_2) 8612 : state_1_(op_1), 8613 state_2_(op_2) { 8614 } 8615 8616 template<typename Chars1, typename Chars2> 8617 static inline bool Equals(State* state_1, State* state_2, unsigned to_check) { 8618 const Chars1* a = reinterpret_cast<const Chars1*>(state_1->buffer8_); 8619 const Chars2* b = reinterpret_cast<const Chars2*>(state_2->buffer8_); 8620 return RawStringComparator<Chars1, Chars2>::compare(a, b, to_check); 8621 } 8622 8623 bool Equals(unsigned length, String* string_1, String* string_2) { 8624 ASSERT(length != 0); 8625 state_1_.Init(string_1, length); 8626 state_2_.Init(string_2, length); 8627 while (true) { 8628 unsigned to_check = Min(state_1_.length_, state_2_.length_); 8629 ASSERT(to_check > 0 && to_check <= length); 8630 bool is_equal; 8631 if (state_1_.is_one_byte_) { 8632 if (state_2_.is_one_byte_) { 8633 is_equal = Equals<uint8_t, uint8_t>(&state_1_, &state_2_, to_check); 8634 } else { 8635 is_equal = Equals<uint8_t, uint16_t>(&state_1_, &state_2_, to_check); 8636 } 8637 } else { 8638 if (state_2_.is_one_byte_) { 8639 is_equal = Equals<uint16_t, uint8_t>(&state_1_, &state_2_, to_check); 8640 } else { 8641 is_equal = Equals<uint16_t, uint16_t>(&state_1_, &state_2_, to_check); 8642 } 8643 } 8644 // Looping done. 8645 if (!is_equal) return false; 8646 length -= to_check; 8647 // Exit condition. Strings are equal. 8648 if (length == 0) return true; 8649 state_1_.Advance(to_check); 8650 state_2_.Advance(to_check); 8651 } 8652 } 8653 8654 private: 8655 State state_1_; 8656 State state_2_; 8657 DISALLOW_IMPLICIT_CONSTRUCTORS(StringComparator); 8658 }; 8659 8660 8661 bool String::SlowEquals(String* other) { 8662 // Fast check: negative check with lengths. 8663 int len = length(); 8664 if (len != other->length()) return false; 8665 if (len == 0) return true; 8666 8667 // Fast check: if hash code is computed for both strings 8668 // a fast negative check can be performed. 8669 if (HasHashCode() && other->HasHashCode()) { 8670 #ifdef DEBUG 8671 if (FLAG_enable_slow_asserts) { 8672 if (Hash() != other->Hash()) { 8673 bool found_difference = false; 8674 for (int i = 0; i < len; i++) { 8675 if (Get(i) != other->Get(i)) { 8676 found_difference = true; 8677 break; 8678 } 8679 } 8680 ASSERT(found_difference); 8681 } 8682 } 8683 #endif 8684 if (Hash() != other->Hash()) return false; 8685 } 8686 8687 // We know the strings are both non-empty. Compare the first chars 8688 // before we try to flatten the strings. 8689 if (this->Get(0) != other->Get(0)) return false; 8690 8691 String* lhs = this->TryFlattenGetString(); 8692 String* rhs = other->TryFlattenGetString(); 8693 8694 // TODO(dcarney): Compare all types of flat strings with a Visitor. 8695 if (StringShape(lhs).IsSequentialAscii() && 8696 StringShape(rhs).IsSequentialAscii()) { 8697 const uint8_t* str1 = SeqOneByteString::cast(lhs)->GetChars(); 8698 const uint8_t* str2 = SeqOneByteString::cast(rhs)->GetChars(); 8699 return CompareRawStringContents(str1, str2, len); 8700 } 8701 8702 Isolate* isolate = GetIsolate(); 8703 StringComparator comparator(isolate->objects_string_compare_iterator_a(), 8704 isolate->objects_string_compare_iterator_b()); 8705 8706 return comparator.Equals(static_cast<unsigned>(len), lhs, rhs); 8707 } 8708 8709 8710 bool String::MarkAsUndetectable() { 8711 if (StringShape(this).IsInternalized()) return false; 8712 8713 Map* map = this->map(); 8714 Heap* heap = GetHeap(); 8715 if (map == heap->string_map()) { 8716 this->set_map(heap->undetectable_string_map()); 8717 return true; 8718 } else if (map == heap->ascii_string_map()) { 8719 this->set_map(heap->undetectable_ascii_string_map()); 8720 return true; 8721 } 8722 // Rest cannot be marked as undetectable 8723 return false; 8724 } 8725 8726 8727 bool String::IsUtf8EqualTo(Vector<const char> str, bool allow_prefix_match) { 8728 int slen = length(); 8729 // Can't check exact length equality, but we can check bounds. 8730 int str_len = str.length(); 8731 if (!allow_prefix_match && 8732 (str_len < slen || 8733 str_len > slen*static_cast<int>(unibrow::Utf8::kMaxEncodedSize))) { 8734 return false; 8735 } 8736 int i; 8737 unsigned remaining_in_str = static_cast<unsigned>(str_len); 8738 const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(str.start()); 8739 for (i = 0; i < slen && remaining_in_str > 0; i++) { 8740 unsigned cursor = 0; 8741 uint32_t r = unibrow::Utf8::ValueOf(utf8_data, remaining_in_str, &cursor); 8742 ASSERT(cursor > 0 && cursor <= remaining_in_str); 8743 if (r > unibrow::Utf16::kMaxNonSurrogateCharCode) { 8744 if (i > slen - 1) return false; 8745 if (Get(i++) != unibrow::Utf16::LeadSurrogate(r)) return false; 8746 if (Get(i) != unibrow::Utf16::TrailSurrogate(r)) return false; 8747 } else { 8748 if (Get(i) != r) return false; 8749 } 8750 utf8_data += cursor; 8751 remaining_in_str -= cursor; 8752 } 8753 return (allow_prefix_match || i == slen) && remaining_in_str == 0; 8754 } 8755 8756 8757 bool String::IsOneByteEqualTo(Vector<const uint8_t> str) { 8758 int slen = length(); 8759 if (str.length() != slen) return false; 8760 DisallowHeapAllocation no_gc; 8761 FlatContent content = GetFlatContent(); 8762 if (content.IsAscii()) { 8763 return CompareChars(content.ToOneByteVector().start(), 8764 str.start(), slen) == 0; 8765 } 8766 for (int i = 0; i < slen; i++) { 8767 if (Get(i) != static_cast<uint16_t>(str[i])) return false; 8768 } 8769 return true; 8770 } 8771 8772 8773 bool String::IsTwoByteEqualTo(Vector<const uc16> str) { 8774 int slen = length(); 8775 if (str.length() != slen) return false; 8776 DisallowHeapAllocation no_gc; 8777 FlatContent content = GetFlatContent(); 8778 if (content.IsTwoByte()) { 8779 return CompareChars(content.ToUC16Vector().start(), str.start(), slen) == 0; 8780 } 8781 for (int i = 0; i < slen; i++) { 8782 if (Get(i) != str[i]) return false; 8783 } 8784 return true; 8785 } 8786 8787 8788 class IteratingStringHasher: public StringHasher { 8789 public: 8790 static inline uint32_t Hash(String* string, uint32_t seed) { 8791 const unsigned len = static_cast<unsigned>(string->length()); 8792 IteratingStringHasher hasher(len, seed); 8793 if (hasher.has_trivial_hash()) { 8794 return hasher.GetHashField(); 8795 } 8796 int32_t type = string->map()->instance_type(); 8797 ConsStringNullOp null_op; 8798 String::Visit(string, 0, hasher, null_op, type, len); 8799 // Flat strings terminate immediately. 8800 if (hasher.consumed_ == len) { 8801 ASSERT(!string->IsConsString()); 8802 return hasher.GetHashField(); 8803 } 8804 ASSERT(string->IsConsString()); 8805 // This is a ConsString, iterate across it. 8806 ConsStringIteratorOp op; 8807 unsigned offset = 0; 8808 unsigned leaf_length = len; 8809 string = op.Operate(string, &offset, &type, &leaf_length); 8810 while (true) { 8811 ASSERT(hasher.consumed_ < len); 8812 String::Visit(string, 0, hasher, null_op, type, leaf_length); 8813 if (hasher.consumed_ == len) break; 8814 string = op.ContinueOperation(&type, &leaf_length); 8815 // This should be taken care of by the length check. 8816 ASSERT(string != NULL); 8817 } 8818 return hasher.GetHashField(); 8819 } 8820 inline void VisitOneByteString(const uint8_t* chars, unsigned length) { 8821 AddCharacters(chars, static_cast<int>(length)); 8822 consumed_ += length; 8823 } 8824 inline void VisitTwoByteString(const uint16_t* chars, unsigned length) { 8825 AddCharacters(chars, static_cast<int>(length)); 8826 consumed_ += length; 8827 } 8828 8829 private: 8830 inline IteratingStringHasher(int len, uint32_t seed) 8831 : StringHasher(len, seed), 8832 consumed_(0) {} 8833 unsigned consumed_; 8834 DISALLOW_COPY_AND_ASSIGN(IteratingStringHasher); 8835 }; 8836 8837 8838 uint32_t String::ComputeAndSetHash() { 8839 // Should only be called if hash code has not yet been computed. 8840 ASSERT(!HasHashCode()); 8841 8842 // Store the hash code in the object. 8843 uint32_t field = IteratingStringHasher::Hash(this, GetHeap()->HashSeed()); 8844 set_hash_field(field); 8845 8846 // Check the hash code is there. 8847 ASSERT(HasHashCode()); 8848 uint32_t result = field >> kHashShift; 8849 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. 8850 return result; 8851 } 8852 8853 8854 bool String::ComputeArrayIndex(uint32_t* index) { 8855 int length = this->length(); 8856 if (length == 0 || length > kMaxArrayIndexSize) return false; 8857 ConsStringIteratorOp op; 8858 StringCharacterStream stream(this, &op); 8859 uint16_t ch = stream.GetNext(); 8860 8861 // If the string begins with a '0' character, it must only consist 8862 // of it to be a legal array index. 8863 if (ch == '0') { 8864 *index = 0; 8865 return length == 1; 8866 } 8867 8868 // Convert string to uint32 array index; character by character. 8869 int d = ch - '0'; 8870 if (d < 0 || d > 9) return false; 8871 uint32_t result = d; 8872 while (stream.HasMore()) { 8873 d = stream.GetNext() - '0'; 8874 if (d < 0 || d > 9) return false; 8875 // Check that the new result is below the 32 bit limit. 8876 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false; 8877 result = (result * 10) + d; 8878 } 8879 8880 *index = result; 8881 return true; 8882 } 8883 8884 8885 bool String::SlowAsArrayIndex(uint32_t* index) { 8886 if (length() <= kMaxCachedArrayIndexLength) { 8887 Hash(); // force computation of hash code 8888 uint32_t field = hash_field(); 8889 if ((field & kIsNotArrayIndexMask) != 0) return false; 8890 // Isolate the array index form the full hash field. 8891 *index = (kArrayIndexHashMask & field) >> kHashShift; 8892 return true; 8893 } else { 8894 return ComputeArrayIndex(index); 8895 } 8896 } 8897 8898 8899 Handle<String> SeqString::Truncate(Handle<SeqString> string, int new_length) { 8900 int new_size, old_size; 8901 int old_length = string->length(); 8902 if (old_length <= new_length) return string; 8903 8904 if (string->IsSeqOneByteString()) { 8905 old_size = SeqOneByteString::SizeFor(old_length); 8906 new_size = SeqOneByteString::SizeFor(new_length); 8907 } else { 8908 ASSERT(string->IsSeqTwoByteString()); 8909 old_size = SeqTwoByteString::SizeFor(old_length); 8910 new_size = SeqTwoByteString::SizeFor(new_length); 8911 } 8912 8913 int delta = old_size - new_size; 8914 string->set_length(new_length); 8915 8916 Address start_of_string = string->address(); 8917 ASSERT_OBJECT_ALIGNED(start_of_string); 8918 ASSERT_OBJECT_ALIGNED(start_of_string + new_size); 8919 8920 Heap* heap = string->GetHeap(); 8921 NewSpace* newspace = heap->new_space(); 8922 if (newspace->Contains(start_of_string) && 8923 newspace->top() == start_of_string + old_size) { 8924 // Last allocated object in new space. Simply lower allocation top. 8925 *(newspace->allocation_top_address()) = start_of_string + new_size; 8926 } else { 8927 // Sizes are pointer size aligned, so that we can use filler objects 8928 // that are a multiple of pointer size. 8929 heap->CreateFillerObjectAt(start_of_string + new_size, delta); 8930 } 8931 if (Marking::IsBlack(Marking::MarkBitFrom(start_of_string))) { 8932 MemoryChunk::IncrementLiveBytesFromMutator(start_of_string, -delta); 8933 } 8934 8935 8936 if (new_length == 0) return heap->isolate()->factory()->empty_string(); 8937 return string; 8938 } 8939 8940 8941 AllocationMemento* AllocationMemento::FindForJSObject(JSObject* object) { 8942 // Currently, AllocationMemento objects are only allocated immediately 8943 // after JSArrays in NewSpace, and detecting whether a JSArray has one 8944 // involves carefully checking the object immediately after the JSArray 8945 // (if there is one) to see if it's an AllocationMemento. 8946 if (FLAG_track_allocation_sites && object->GetHeap()->InNewSpace(object)) { 8947 Address ptr_end = (reinterpret_cast<Address>(object) - kHeapObjectTag) + 8948 object->Size(); 8949 if ((ptr_end + AllocationMemento::kSize) <= 8950 object->GetHeap()->NewSpaceTop()) { 8951 // There is room in newspace for allocation info. Do we have some? 8952 Map** possible_allocation_memento_map = 8953 reinterpret_cast<Map**>(ptr_end); 8954 if (*possible_allocation_memento_map == 8955 object->GetHeap()->allocation_memento_map()) { 8956 AllocationMemento* memento = AllocationMemento::cast( 8957 reinterpret_cast<Object*>(ptr_end + 1)); 8958 return memento; 8959 } 8960 } 8961 } 8962 return NULL; 8963 } 8964 8965 8966 uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { 8967 // For array indexes mix the length into the hash as an array index could 8968 // be zero. 8969 ASSERT(length > 0); 8970 ASSERT(length <= String::kMaxArrayIndexSize); 8971 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < 8972 (1 << String::kArrayIndexValueBits)); 8973 8974 value <<= String::kHashShift; 8975 value |= length << String::kArrayIndexHashLengthShift; 8976 8977 ASSERT((value & String::kIsNotArrayIndexMask) == 0); 8978 ASSERT((length > String::kMaxCachedArrayIndexLength) || 8979 (value & String::kContainsCachedArrayIndexMask) == 0); 8980 return value; 8981 } 8982 8983 8984 uint32_t StringHasher::GetHashField() { 8985 if (length_ <= String::kMaxHashCalcLength) { 8986 if (is_array_index_) { 8987 return MakeArrayIndexHash(array_index_, length_); 8988 } 8989 return (GetHashCore(raw_running_hash_) << String::kHashShift) | 8990 String::kIsNotArrayIndexMask; 8991 } else { 8992 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask; 8993 } 8994 } 8995 8996 8997 uint32_t StringHasher::ComputeUtf8Hash(Vector<const char> chars, 8998 uint32_t seed, 8999 int* utf16_length_out) { 9000 int vector_length = chars.length(); 9001 // Handle some edge cases 9002 if (vector_length <= 1) { 9003 ASSERT(vector_length == 0 || 9004 static_cast<uint8_t>(chars.start()[0]) <= 9005 unibrow::Utf8::kMaxOneByteChar); 9006 *utf16_length_out = vector_length; 9007 return HashSequentialString(chars.start(), vector_length, seed); 9008 } 9009 // Start with a fake length which won't affect computation. 9010 // It will be updated later. 9011 StringHasher hasher(String::kMaxArrayIndexSize, seed); 9012 unsigned remaining = static_cast<unsigned>(vector_length); 9013 const uint8_t* stream = reinterpret_cast<const uint8_t*>(chars.start()); 9014 int utf16_length = 0; 9015 bool is_index = true; 9016 ASSERT(hasher.is_array_index_); 9017 while (remaining > 0) { 9018 unsigned consumed = 0; 9019 uint32_t c = unibrow::Utf8::ValueOf(stream, remaining, &consumed); 9020 ASSERT(consumed > 0 && consumed <= remaining); 9021 stream += consumed; 9022 remaining -= consumed; 9023 bool is_two_characters = c > unibrow::Utf16::kMaxNonSurrogateCharCode; 9024 utf16_length += is_two_characters ? 2 : 1; 9025 // No need to keep hashing. But we do need to calculate utf16_length. 9026 if (utf16_length > String::kMaxHashCalcLength) continue; 9027 if (is_two_characters) { 9028 uint16_t c1 = unibrow::Utf16::LeadSurrogate(c); 9029 uint16_t c2 = unibrow::Utf16::TrailSurrogate(c); 9030 hasher.AddCharacter(c1); 9031 hasher.AddCharacter(c2); 9032 if (is_index) is_index = hasher.UpdateIndex(c1); 9033 if (is_index) is_index = hasher.UpdateIndex(c2); 9034 } else { 9035 hasher.AddCharacter(c); 9036 if (is_index) is_index = hasher.UpdateIndex(c); 9037 } 9038 } 9039 *utf16_length_out = static_cast<int>(utf16_length); 9040 // Must set length here so that hash computation is correct. 9041 hasher.length_ = utf16_length; 9042 return hasher.GetHashField(); 9043 } 9044 9045 9046 MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) { 9047 Heap* heap = GetHeap(); 9048 if (start == 0 && end == length()) return this; 9049 MaybeObject* result = heap->AllocateSubString(this, start, end, pretenure); 9050 return result; 9051 } 9052 9053 9054 void String::PrintOn(FILE* file) { 9055 int length = this->length(); 9056 for (int i = 0; i < length; i++) { 9057 PrintF(file, "%c", Get(i)); 9058 } 9059 } 9060 9061 9062 static void TrimEnumCache(Heap* heap, Map* map, DescriptorArray* descriptors) { 9063 int live_enum = map->EnumLength(); 9064 if (live_enum == Map::kInvalidEnumCache) { 9065 live_enum = map->NumberOfDescribedProperties(OWN_DESCRIPTORS, DONT_ENUM); 9066 } 9067 if (live_enum == 0) return descriptors->ClearEnumCache(); 9068 9069 FixedArray* enum_cache = descriptors->GetEnumCache(); 9070 9071 int to_trim = enum_cache->length() - live_enum; 9072 if (to_trim <= 0) return; 9073 RightTrimFixedArray<FROM_GC>(heap, descriptors->GetEnumCache(), to_trim); 9074 9075 if (!descriptors->HasEnumIndicesCache()) return; 9076 FixedArray* enum_indices_cache = descriptors->GetEnumIndicesCache(); 9077 RightTrimFixedArray<FROM_GC>(heap, enum_indices_cache, to_trim); 9078 } 9079 9080 9081 static void TrimDescriptorArray(Heap* heap, 9082 Map* map, 9083 DescriptorArray* descriptors, 9084 int number_of_own_descriptors) { 9085 int number_of_descriptors = descriptors->number_of_descriptors_storage(); 9086 int to_trim = number_of_descriptors - number_of_own_descriptors; 9087 if (to_trim == 0) return; 9088 9089 RightTrimFixedArray<FROM_GC>( 9090 heap, descriptors, to_trim * DescriptorArray::kDescriptorSize); 9091 descriptors->SetNumberOfDescriptors(number_of_own_descriptors); 9092 9093 if (descriptors->HasEnumCache()) TrimEnumCache(heap, map, descriptors); 9094 descriptors->Sort(); 9095 } 9096 9097 9098 // Clear a possible back pointer in case the transition leads to a dead map. 9099 // Return true in case a back pointer has been cleared and false otherwise. 9100 static bool ClearBackPointer(Heap* heap, Map* target) { 9101 if (Marking::MarkBitFrom(target).Get()) return false; 9102 target->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER); 9103 return true; 9104 } 9105 9106 9107 // TODO(mstarzinger): This method should be moved into MarkCompactCollector, 9108 // because it cannot be called from outside the GC and we already have methods 9109 // depending on the transitions layout in the GC anyways. 9110 void Map::ClearNonLiveTransitions(Heap* heap) { 9111 // If there are no transitions to be cleared, return. 9112 // TODO(verwaest) Should be an assert, otherwise back pointers are not 9113 // properly cleared. 9114 if (!HasTransitionArray()) return; 9115 9116 TransitionArray* t = transitions(); 9117 MarkCompactCollector* collector = heap->mark_compact_collector(); 9118 9119 int transition_index = 0; 9120 9121 DescriptorArray* descriptors = instance_descriptors(); 9122 bool descriptors_owner_died = false; 9123 9124 // Compact all live descriptors to the left. 9125 for (int i = 0; i < t->number_of_transitions(); ++i) { 9126 Map* target = t->GetTarget(i); 9127 if (ClearBackPointer(heap, target)) { 9128 if (target->instance_descriptors() == descriptors) { 9129 descriptors_owner_died = true; 9130 } 9131 } else { 9132 if (i != transition_index) { 9133 Name* key = t->GetKey(i); 9134 t->SetKey(transition_index, key); 9135 Object** key_slot = t->GetKeySlot(transition_index); 9136 collector->RecordSlot(key_slot, key_slot, key); 9137 // Target slots do not need to be recorded since maps are not compacted. 9138 t->SetTarget(transition_index, t->GetTarget(i)); 9139 } 9140 transition_index++; 9141 } 9142 } 9143 9144 // If there are no transitions to be cleared, return. 9145 // TODO(verwaest) Should be an assert, otherwise back pointers are not 9146 // properly cleared. 9147 if (transition_index == t->number_of_transitions()) return; 9148 9149 int number_of_own_descriptors = NumberOfOwnDescriptors(); 9150 9151 if (descriptors_owner_died) { 9152 if (number_of_own_descriptors > 0) { 9153 TrimDescriptorArray(heap, this, descriptors, number_of_own_descriptors); 9154 ASSERT(descriptors->number_of_descriptors() == number_of_own_descriptors); 9155 } else { 9156 ASSERT(descriptors == GetHeap()->empty_descriptor_array()); 9157 } 9158 } 9159 9160 int trim = t->number_of_transitions() - transition_index; 9161 if (trim > 0) { 9162 RightTrimFixedArray<FROM_GC>(heap, t, t->IsSimpleTransition() 9163 ? trim : trim * TransitionArray::kTransitionSize); 9164 } 9165 } 9166 9167 9168 int Map::Hash() { 9169 // For performance reasons we only hash the 3 most variable fields of a map: 9170 // constructor, prototype and bit_field2. 9171 9172 // Shift away the tag. 9173 int hash = (static_cast<uint32_t>( 9174 reinterpret_cast<uintptr_t>(constructor())) >> 2); 9175 9176 // XOR-ing the prototype and constructor directly yields too many zero bits 9177 // when the two pointers are close (which is fairly common). 9178 // To avoid this we shift the prototype 4 bits relatively to the constructor. 9179 hash ^= (static_cast<uint32_t>( 9180 reinterpret_cast<uintptr_t>(prototype())) << 2); 9181 9182 return hash ^ (hash >> 16) ^ bit_field2(); 9183 } 9184 9185 9186 static bool CheckEquivalent(Map* first, Map* second) { 9187 return 9188 first->constructor() == second->constructor() && 9189 first->prototype() == second->prototype() && 9190 first->instance_type() == second->instance_type() && 9191 first->bit_field() == second->bit_field() && 9192 first->bit_field2() == second->bit_field2() && 9193 first->is_observed() == second->is_observed() && 9194 first->function_with_prototype() == second->function_with_prototype(); 9195 } 9196 9197 9198 bool Map::EquivalentToForTransition(Map* other) { 9199 return CheckEquivalent(this, other); 9200 } 9201 9202 9203 bool Map::EquivalentToForNormalization(Map* other, 9204 PropertyNormalizationMode mode) { 9205 int properties = mode == CLEAR_INOBJECT_PROPERTIES 9206 ? 0 : other->inobject_properties(); 9207 return CheckEquivalent(this, other) && inobject_properties() == properties; 9208 } 9209 9210 9211 void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) { 9212 // Iterate over all fields in the body but take care in dealing with 9213 // the code entry. 9214 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset); 9215 v->VisitCodeEntry(this->address() + kCodeEntryOffset); 9216 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size); 9217 } 9218 9219 9220 void JSFunction::MarkForLazyRecompilation() { 9221 ASSERT(is_compiled() || GetIsolate()->DebuggerHasBreakPoints()); 9222 ASSERT(!IsOptimized()); 9223 ASSERT(shared()->allows_lazy_compilation() || 9224 code()->optimizable()); 9225 ASSERT(!shared()->is_generator()); 9226 set_code_no_write_barrier( 9227 GetIsolate()->builtins()->builtin(Builtins::kLazyRecompile)); 9228 // No write barrier required, since the builtin is part of the root set. 9229 } 9230 9231 9232 void JSFunction::MarkForParallelRecompilation() { 9233 ASSERT(is_compiled() || GetIsolate()->DebuggerHasBreakPoints()); 9234 ASSERT(!IsOptimized()); 9235 ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); 9236 ASSERT(!shared()->is_generator()); 9237 ASSERT(FLAG_parallel_recompilation); 9238 if (FLAG_trace_parallel_recompilation) { 9239 PrintF(" ** Marking "); 9240 PrintName(); 9241 PrintF(" for parallel recompilation.\n"); 9242 } 9243 set_code_no_write_barrier( 9244 GetIsolate()->builtins()->builtin(Builtins::kParallelRecompile)); 9245 // No write barrier required, since the builtin is part of the root set. 9246 } 9247 9248 9249 void JSFunction::MarkForInstallingRecompiledCode() { 9250 // The debugger could have switched the builtin to lazy compile. 9251 // In that case, simply carry on. It will be dealt with later. 9252 ASSERT(!IsOptimized()); 9253 ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); 9254 ASSERT(FLAG_parallel_recompilation); 9255 set_code_no_write_barrier( 9256 GetIsolate()->builtins()->builtin(Builtins::kInstallRecompiledCode)); 9257 // No write barrier required, since the builtin is part of the root set. 9258 } 9259 9260 9261 void JSFunction::MarkInRecompileQueue() { 9262 // We can only arrive here via the parallel-recompilation builtin. If 9263 // break points were set, the code would point to the lazy-compile builtin. 9264 ASSERT(!GetIsolate()->DebuggerHasBreakPoints()); 9265 ASSERT(IsMarkedForParallelRecompilation() && !IsOptimized()); 9266 ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); 9267 ASSERT(FLAG_parallel_recompilation); 9268 if (FLAG_trace_parallel_recompilation) { 9269 PrintF(" ** Queueing "); 9270 PrintName(); 9271 PrintF(" for parallel recompilation.\n"); 9272 } 9273 set_code_no_write_barrier( 9274 GetIsolate()->builtins()->builtin(Builtins::kInRecompileQueue)); 9275 // No write barrier required, since the builtin is part of the root set. 9276 } 9277 9278 9279 static bool CompileLazyHelper(CompilationInfo* info, 9280 ClearExceptionFlag flag) { 9281 // Compile the source information to a code object. 9282 ASSERT(info->IsOptimizing() || !info->shared_info()->is_compiled()); 9283 ASSERT(!info->isolate()->has_pending_exception()); 9284 bool result = Compiler::CompileLazy(info); 9285 ASSERT(result != Isolate::Current()->has_pending_exception()); 9286 if (!result && flag == CLEAR_EXCEPTION) { 9287 info->isolate()->clear_pending_exception(); 9288 } 9289 return result; 9290 } 9291 9292 9293 bool SharedFunctionInfo::CompileLazy(Handle<SharedFunctionInfo> shared, 9294 ClearExceptionFlag flag) { 9295 ASSERT(shared->allows_lazy_compilation_without_context()); 9296 CompilationInfoWithZone info(shared); 9297 return CompileLazyHelper(&info, flag); 9298 } 9299 9300 9301 void SharedFunctionInfo::AddToOptimizedCodeMap( 9302 Handle<SharedFunctionInfo> shared, 9303 Handle<Context> native_context, 9304 Handle<Code> code, 9305 Handle<FixedArray> literals) { 9306 CALL_HEAP_FUNCTION_VOID( 9307 shared->GetIsolate(), 9308 shared->AddToOptimizedCodeMap(*native_context, *code, *literals)); 9309 } 9310 9311 9312 MaybeObject* SharedFunctionInfo::AddToOptimizedCodeMap(Context* native_context, 9313 Code* code, 9314 FixedArray* literals) { 9315 ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); 9316 ASSERT(native_context->IsNativeContext()); 9317 STATIC_ASSERT(kEntryLength == 3); 9318 Heap* heap = GetHeap(); 9319 FixedArray* new_code_map; 9320 Object* value = optimized_code_map(); 9321 if (value->IsSmi()) { 9322 // No optimized code map. 9323 ASSERT_EQ(0, Smi::cast(value)->value()); 9324 // Crate 3 entries per context {context, code, literals}. 9325 MaybeObject* maybe = heap->AllocateFixedArray(kInitialLength); 9326 if (!maybe->To(&new_code_map)) return maybe; 9327 new_code_map->set(kEntriesStart + 0, native_context); 9328 new_code_map->set(kEntriesStart + 1, code); 9329 new_code_map->set(kEntriesStart + 2, literals); 9330 } else { 9331 // Copy old map and append one new entry. 9332 FixedArray* old_code_map = FixedArray::cast(value); 9333 ASSERT_EQ(-1, SearchOptimizedCodeMap(native_context)); 9334 int old_length = old_code_map->length(); 9335 int new_length = old_length + kEntryLength; 9336 MaybeObject* maybe = old_code_map->CopySize(new_length); 9337 if (!maybe->To(&new_code_map)) return maybe; 9338 new_code_map->set(old_length + 0, native_context); 9339 new_code_map->set(old_length + 1, code); 9340 new_code_map->set(old_length + 2, literals); 9341 // Zap the old map for the sake of the heap verifier. 9342 if (Heap::ShouldZapGarbage()) { 9343 Object** data = old_code_map->data_start(); 9344 MemsetPointer(data, heap->the_hole_value(), old_length); 9345 } 9346 } 9347 #ifdef DEBUG 9348 for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) { 9349 ASSERT(new_code_map->get(i)->IsNativeContext()); 9350 ASSERT(new_code_map->get(i + 1)->IsCode()); 9351 ASSERT(Code::cast(new_code_map->get(i + 1))->kind() == 9352 Code::OPTIMIZED_FUNCTION); 9353 ASSERT(new_code_map->get(i + 2)->IsFixedArray()); 9354 } 9355 #endif 9356 set_optimized_code_map(new_code_map); 9357 return new_code_map; 9358 } 9359 9360 9361 void SharedFunctionInfo::InstallFromOptimizedCodeMap(JSFunction* function, 9362 int index) { 9363 ASSERT(index > kEntriesStart); 9364 FixedArray* code_map = FixedArray::cast(optimized_code_map()); 9365 if (!bound()) { 9366 FixedArray* cached_literals = FixedArray::cast(code_map->get(index + 1)); 9367 ASSERT(cached_literals != NULL); 9368 function->set_literals(cached_literals); 9369 } 9370 Code* code = Code::cast(code_map->get(index)); 9371 ASSERT(code != NULL); 9372 ASSERT(function->context()->native_context() == code_map->get(index - 1)); 9373 function->ReplaceCode(code); 9374 } 9375 9376 9377 void SharedFunctionInfo::ClearOptimizedCodeMap() { 9378 FixedArray* code_map = FixedArray::cast(optimized_code_map()); 9379 9380 // If the next map link slot is already used then the function was 9381 // enqueued with code flushing and we remove it now. 9382 if (!code_map->get(kNextMapIndex)->IsUndefined()) { 9383 CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher(); 9384 flusher->EvictOptimizedCodeMap(this); 9385 } 9386 9387 ASSERT(code_map->get(kNextMapIndex)->IsUndefined()); 9388 set_optimized_code_map(Smi::FromInt(0)); 9389 } 9390 9391 9392 void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, 9393 const char* reason) { 9394 if (optimized_code_map()->IsSmi()) return; 9395 9396 int i; 9397 bool removed_entry = false; 9398 FixedArray* code_map = FixedArray::cast(optimized_code_map()); 9399 for (i = kEntriesStart; i < code_map->length(); i += kEntryLength) { 9400 ASSERT(code_map->get(i)->IsNativeContext()); 9401 if (Code::cast(code_map->get(i + 1)) == optimized_code) { 9402 if (FLAG_trace_opt) { 9403 PrintF("[evicting entry from optimizing code map (%s) for ", reason); 9404 ShortPrint(); 9405 PrintF("]\n"); 9406 } 9407 removed_entry = true; 9408 break; 9409 } 9410 } 9411 while (i < (code_map->length() - kEntryLength)) { 9412 code_map->set(i, code_map->get(i + kEntryLength)); 9413 code_map->set(i + 1, code_map->get(i + 1 + kEntryLength)); 9414 code_map->set(i + 2, code_map->get(i + 2 + kEntryLength)); 9415 i += kEntryLength; 9416 } 9417 if (removed_entry) { 9418 // Always trim even when array is cleared because of heap verifier. 9419 RightTrimFixedArray<FROM_MUTATOR>(GetHeap(), code_map, kEntryLength); 9420 if (code_map->length() == kEntriesStart) { 9421 ClearOptimizedCodeMap(); 9422 } 9423 } 9424 } 9425 9426 9427 void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) { 9428 FixedArray* code_map = FixedArray::cast(optimized_code_map()); 9429 ASSERT(shrink_by % kEntryLength == 0); 9430 ASSERT(shrink_by <= code_map->length() - kEntriesStart); 9431 // Always trim even when array is cleared because of heap verifier. 9432 RightTrimFixedArray<FROM_GC>(GetHeap(), code_map, shrink_by); 9433 if (code_map->length() == kEntriesStart) { 9434 ClearOptimizedCodeMap(); 9435 } 9436 } 9437 9438 9439 bool JSFunction::CompileLazy(Handle<JSFunction> function, 9440 ClearExceptionFlag flag) { 9441 bool result = true; 9442 if (function->shared()->is_compiled()) { 9443 function->ReplaceCode(function->shared()->code()); 9444 } else { 9445 ASSERT(function->shared()->allows_lazy_compilation()); 9446 CompilationInfoWithZone info(function); 9447 result = CompileLazyHelper(&info, flag); 9448 ASSERT(!result || function->is_compiled()); 9449 } 9450 return result; 9451 } 9452 9453 9454 bool JSFunction::CompileOptimized(Handle<JSFunction> function, 9455 BailoutId osr_ast_id, 9456 ClearExceptionFlag flag) { 9457 CompilationInfoWithZone info(function); 9458 info.SetOptimizing(osr_ast_id); 9459 return CompileLazyHelper(&info, flag); 9460 } 9461 9462 9463 bool JSFunction::EnsureCompiled(Handle<JSFunction> function, 9464 ClearExceptionFlag flag) { 9465 return function->is_compiled() || CompileLazy(function, flag); 9466 } 9467 9468 9469 bool JSFunction::IsInlineable() { 9470 if (IsBuiltin()) return false; 9471 SharedFunctionInfo* shared_info = shared(); 9472 // Check that the function has a script associated with it. 9473 if (!shared_info->script()->IsScript()) return false; 9474 if (shared_info->optimization_disabled()) return false; 9475 Code* code = shared_info->code(); 9476 if (code->kind() == Code::OPTIMIZED_FUNCTION) return true; 9477 // If we never ran this (unlikely) then lets try to optimize it. 9478 if (code->kind() != Code::FUNCTION) return true; 9479 return code->optimizable(); 9480 } 9481 9482 9483 void JSObject::OptimizeAsPrototype(Handle<JSObject> object) { 9484 CALL_HEAP_FUNCTION_VOID(object->GetIsolate(), object->OptimizeAsPrototype()); 9485 } 9486 9487 9488 MaybeObject* JSObject::OptimizeAsPrototype() { 9489 if (IsGlobalObject()) return this; 9490 9491 // Make sure prototypes are fast objects and their maps have the bit set 9492 // so they remain fast. 9493 if (!HasFastProperties()) { 9494 MaybeObject* new_proto = TransformToFastProperties(0); 9495 if (new_proto->IsFailure()) return new_proto; 9496 ASSERT(new_proto == this); 9497 } 9498 return this; 9499 } 9500 9501 9502 static MUST_USE_RESULT MaybeObject* CacheInitialJSArrayMaps( 9503 Context* native_context, Map* initial_map) { 9504 // Replace all of the cached initial array maps in the native context with 9505 // the appropriate transitioned elements kind maps. 9506 Heap* heap = native_context->GetHeap(); 9507 MaybeObject* maybe_maps = 9508 heap->AllocateFixedArrayWithHoles(kElementsKindCount, TENURED); 9509 FixedArray* maps; 9510 if (!maybe_maps->To(&maps)) return maybe_maps; 9511 9512 Map* current_map = initial_map; 9513 ElementsKind kind = current_map->elements_kind(); 9514 ASSERT(kind == GetInitialFastElementsKind()); 9515 maps->set(kind, current_map); 9516 for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1; 9517 i < kFastElementsKindCount; ++i) { 9518 Map* new_map; 9519 ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i); 9520 if (current_map->HasElementsTransition()) { 9521 new_map = current_map->elements_transition_map(); 9522 ASSERT(new_map->elements_kind() == next_kind); 9523 } else { 9524 MaybeObject* maybe_new_map = 9525 current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION); 9526 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 9527 } 9528 maps->set(next_kind, new_map); 9529 current_map = new_map; 9530 } 9531 native_context->set_js_array_maps(maps); 9532 return initial_map; 9533 } 9534 9535 9536 Handle<Object> CacheInitialJSArrayMaps(Handle<Context> native_context, 9537 Handle<Map> initial_map) { 9538 CALL_HEAP_FUNCTION(native_context->GetIsolate(), 9539 CacheInitialJSArrayMaps(*native_context, *initial_map), 9540 Object); 9541 } 9542 9543 9544 void JSFunction::SetInstancePrototype(Handle<JSFunction> function, 9545 Handle<Object> value) { 9546 ASSERT(value->IsJSReceiver()); 9547 9548 // First some logic for the map of the prototype to make sure it is in fast 9549 // mode. 9550 if (value->IsJSObject()) { 9551 JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value)); 9552 } 9553 9554 // Now some logic for the maps of the objects that are created by using this 9555 // function as a constructor. 9556 if (function->has_initial_map()) { 9557 // If the function has allocated the initial map replace it with a 9558 // copy containing the new prototype. Also complete any in-object 9559 // slack tracking that is in progress at this point because it is 9560 // still tracking the old copy. 9561 if (function->shared()->IsInobjectSlackTrackingInProgress()) { 9562 function->shared()->CompleteInobjectSlackTracking(); 9563 } 9564 Handle<Map> new_map = Map::Copy(handle(function->initial_map())); 9565 new_map->set_prototype(*value); 9566 9567 // If the function is used as the global Array function, cache the 9568 // initial map (and transitioned versions) in the native context. 9569 Context* native_context = function->context()->native_context(); 9570 Object* array_function = native_context->get(Context::ARRAY_FUNCTION_INDEX); 9571 if (array_function->IsJSFunction() && 9572 *function == JSFunction::cast(array_function)) { 9573 CacheInitialJSArrayMaps(handle(native_context), new_map); 9574 } 9575 9576 function->set_initial_map(*new_map); 9577 } else { 9578 // Put the value in the initial map field until an initial map is 9579 // needed. At that point, a new initial map is created and the 9580 // prototype is put into the initial map where it belongs. 9581 function->set_prototype_or_initial_map(*value); 9582 } 9583 function->GetHeap()->ClearInstanceofCache(); 9584 } 9585 9586 9587 void JSFunction::SetPrototype(Handle<JSFunction> function, 9588 Handle<Object> value) { 9589 ASSERT(function->should_have_prototype()); 9590 Handle<Object> construct_prototype = value; 9591 9592 // If the value is not a JSReceiver, store the value in the map's 9593 // constructor field so it can be accessed. Also, set the prototype 9594 // used for constructing objects to the original object prototype. 9595 // See ECMA-262 13.2.2. 9596 if (!value->IsJSReceiver()) { 9597 // Copy the map so this does not affect unrelated functions. 9598 // Remove map transitions because they point to maps with a 9599 // different prototype. 9600 Handle<Map> new_map = Map::Copy(handle(function->map())); 9601 9602 function->set_map(*new_map); 9603 new_map->set_constructor(*value); 9604 new_map->set_non_instance_prototype(true); 9605 Isolate* isolate = new_map->GetIsolate(); 9606 construct_prototype = handle( 9607 isolate->context()->native_context()->initial_object_prototype(), 9608 isolate); 9609 } else { 9610 function->map()->set_non_instance_prototype(false); 9611 } 9612 9613 return SetInstancePrototype(function, construct_prototype); 9614 } 9615 9616 9617 void JSFunction::RemovePrototype() { 9618 Context* native_context = context()->native_context(); 9619 Map* no_prototype_map = shared()->is_classic_mode() 9620 ? native_context->function_without_prototype_map() 9621 : native_context->strict_mode_function_without_prototype_map(); 9622 9623 if (map() == no_prototype_map) return; 9624 9625 ASSERT(map() == (shared()->is_classic_mode() 9626 ? native_context->function_map() 9627 : native_context->strict_mode_function_map())); 9628 9629 set_map(no_prototype_map); 9630 set_prototype_or_initial_map(no_prototype_map->GetHeap()->the_hole_value()); 9631 } 9632 9633 9634 void JSFunction::SetInstanceClassName(String* name) { 9635 shared()->set_instance_class_name(name); 9636 } 9637 9638 9639 void JSFunction::PrintName(FILE* out) { 9640 SmartArrayPointer<char> name = shared()->DebugName()->ToCString(); 9641 PrintF(out, "%s", *name); 9642 } 9643 9644 9645 Context* JSFunction::NativeContextFromLiterals(FixedArray* literals) { 9646 return Context::cast(literals->get(JSFunction::kLiteralNativeContextIndex)); 9647 } 9648 9649 9650 bool JSFunction::PassesHydrogenFilter() { 9651 String* name = shared()->DebugName(); 9652 // The filter string is a pattern that matches functions in this way: 9653 // "*" all; the default 9654 // "-" all but the top-level function 9655 // "-name" all but the function "name" 9656 // "" only the top-level function 9657 // "name" only the function "name" 9658 // "name*" only functions starting with "name" 9659 if (*FLAG_hydrogen_filter != '*') { 9660 Vector<const char> filter = CStrVector(FLAG_hydrogen_filter); 9661 if (filter.length() == 0) return name->length() == 0; 9662 if (filter[0] != '-' && name->IsUtf8EqualTo(filter)) return true; 9663 if (filter[0] == '-' && 9664 !name->IsUtf8EqualTo(filter.SubVector(1, filter.length()))) { 9665 return true; 9666 } 9667 if (filter[filter.length() - 1] == '*' && 9668 name->IsUtf8EqualTo(filter.SubVector(0, filter.length() - 1), true)) { 9669 return true; 9670 } 9671 return false; 9672 } 9673 9674 return true; 9675 } 9676 9677 9678 MaybeObject* Oddball::Initialize(const char* to_string, 9679 Object* to_number, 9680 byte kind) { 9681 String* internalized_to_string; 9682 { MaybeObject* maybe_string = 9683 Isolate::Current()->heap()->InternalizeUtf8String( 9684 CStrVector(to_string)); 9685 if (!maybe_string->To(&internalized_to_string)) return maybe_string; 9686 } 9687 set_to_string(internalized_to_string); 9688 set_to_number(to_number); 9689 set_kind(kind); 9690 return this; 9691 } 9692 9693 9694 String* SharedFunctionInfo::DebugName() { 9695 Object* n = name(); 9696 if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name(); 9697 return String::cast(n); 9698 } 9699 9700 9701 bool SharedFunctionInfo::HasSourceCode() { 9702 return !script()->IsUndefined() && 9703 !reinterpret_cast<Script*>(script())->source()->IsUndefined(); 9704 } 9705 9706 9707 Handle<Object> SharedFunctionInfo::GetSourceCode() { 9708 if (!HasSourceCode()) return GetIsolate()->factory()->undefined_value(); 9709 Handle<String> source(String::cast(Script::cast(script())->source())); 9710 return SubString(source, start_position(), end_position()); 9711 } 9712 9713 9714 int SharedFunctionInfo::SourceSize() { 9715 return end_position() - start_position(); 9716 } 9717 9718 9719 int SharedFunctionInfo::CalculateInstanceSize() { 9720 int instance_size = 9721 JSObject::kHeaderSize + 9722 expected_nof_properties() * kPointerSize; 9723 if (instance_size > JSObject::kMaxInstanceSize) { 9724 instance_size = JSObject::kMaxInstanceSize; 9725 } 9726 return instance_size; 9727 } 9728 9729 9730 int SharedFunctionInfo::CalculateInObjectProperties() { 9731 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize; 9732 } 9733 9734 9735 // Support function for printing the source code to a StringStream 9736 // without any allocation in the heap. 9737 void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator, 9738 int max_length) { 9739 // For some native functions there is no source. 9740 if (!HasSourceCode()) { 9741 accumulator->Add("<No Source>"); 9742 return; 9743 } 9744 9745 // Get the source for the script which this function came from. 9746 // Don't use String::cast because we don't want more assertion errors while 9747 // we are already creating a stack dump. 9748 String* script_source = 9749 reinterpret_cast<String*>(Script::cast(script())->source()); 9750 9751 if (!script_source->LooksValid()) { 9752 accumulator->Add("<Invalid Source>"); 9753 return; 9754 } 9755 9756 if (!is_toplevel()) { 9757 accumulator->Add("function "); 9758 Object* name = this->name(); 9759 if (name->IsString() && String::cast(name)->length() > 0) { 9760 accumulator->PrintName(name); 9761 } 9762 } 9763 9764 int len = end_position() - start_position(); 9765 if (len <= max_length || max_length < 0) { 9766 accumulator->Put(script_source, start_position(), end_position()); 9767 } else { 9768 accumulator->Put(script_source, 9769 start_position(), 9770 start_position() + max_length); 9771 accumulator->Add("...\n"); 9772 } 9773 } 9774 9775 9776 static bool IsCodeEquivalent(Code* code, Code* recompiled) { 9777 if (code->instruction_size() != recompiled->instruction_size()) return false; 9778 ByteArray* code_relocation = code->relocation_info(); 9779 ByteArray* recompiled_relocation = recompiled->relocation_info(); 9780 int length = code_relocation->length(); 9781 if (length != recompiled_relocation->length()) return false; 9782 int compare = memcmp(code_relocation->GetDataStartAddress(), 9783 recompiled_relocation->GetDataStartAddress(), 9784 length); 9785 return compare == 0; 9786 } 9787 9788 9789 void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) { 9790 ASSERT(!has_deoptimization_support()); 9791 DisallowHeapAllocation no_allocation; 9792 Code* code = this->code(); 9793 if (IsCodeEquivalent(code, recompiled)) { 9794 // Copy the deoptimization data from the recompiled code. 9795 code->set_deoptimization_data(recompiled->deoptimization_data()); 9796 code->set_has_deoptimization_support(true); 9797 } else { 9798 // TODO(3025757): In case the recompiled isn't equivalent to the 9799 // old code, we have to replace it. We should try to avoid this 9800 // altogether because it flushes valuable type feedback by 9801 // effectively resetting all IC state. 9802 ReplaceCode(recompiled); 9803 } 9804 ASSERT(has_deoptimization_support()); 9805 } 9806 9807 9808 void SharedFunctionInfo::DisableOptimization(BailoutReason reason) { 9809 // Disable optimization for the shared function info and mark the 9810 // code as non-optimizable. The marker on the shared function info 9811 // is there because we flush non-optimized code thereby loosing the 9812 // non-optimizable information for the code. When the code is 9813 // regenerated and set on the shared function info it is marked as 9814 // non-optimizable if optimization is disabled for the shared 9815 // function info. 9816 set_optimization_disabled(true); 9817 // Code should be the lazy compilation stub or else unoptimized. If the 9818 // latter, disable optimization for the code too. 9819 ASSERT(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN); 9820 if (code()->kind() == Code::FUNCTION) { 9821 code()->set_optimizable(false); 9822 } 9823 if (FLAG_trace_opt) { 9824 PrintF("[disabled optimization for "); 9825 ShortPrint(); 9826 PrintF(", reason: %s]\n", GetBailoutReason(reason)); 9827 } 9828 } 9829 9830 9831 bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) { 9832 ASSERT(!id.IsNone()); 9833 Code* unoptimized = code(); 9834 DeoptimizationOutputData* data = 9835 DeoptimizationOutputData::cast(unoptimized->deoptimization_data()); 9836 unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this); 9837 USE(ignore); 9838 return true; // Return true if there was no ASSERT. 9839 } 9840 9841 9842 void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) { 9843 ASSERT(!IsInobjectSlackTrackingInProgress()); 9844 9845 if (!FLAG_clever_optimizations) return; 9846 9847 // Only initiate the tracking the first time. 9848 if (live_objects_may_exist()) return; 9849 set_live_objects_may_exist(true); 9850 9851 // No tracking during the snapshot construction phase. 9852 if (Serializer::enabled()) return; 9853 9854 if (map->unused_property_fields() == 0) return; 9855 9856 // Nonzero counter is a leftover from the previous attempt interrupted 9857 // by GC, keep it. 9858 if (construction_count() == 0) { 9859 set_construction_count(kGenerousAllocationCount); 9860 } 9861 set_initial_map(map); 9862 Builtins* builtins = map->GetHeap()->isolate()->builtins(); 9863 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric), 9864 construct_stub()); 9865 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown)); 9866 } 9867 9868 9869 // Called from GC, hence reinterpret_cast and unchecked accessors. 9870 void SharedFunctionInfo::DetachInitialMap() { 9871 Map* map = reinterpret_cast<Map*>(initial_map()); 9872 9873 // Make the map remember to restore the link if it survives the GC. 9874 map->set_bit_field2( 9875 map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo)); 9876 9877 // Undo state changes made by StartInobjectTracking (except the 9878 // construction_count). This way if the initial map does not survive the GC 9879 // then StartInobjectTracking will be called again the next time the 9880 // constructor is called. The countdown will continue and (possibly after 9881 // several more GCs) CompleteInobjectSlackTracking will eventually be called. 9882 Heap* heap = map->GetHeap(); 9883 set_initial_map(heap->undefined_value()); 9884 Builtins* builtins = heap->isolate()->builtins(); 9885 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), 9886 *RawField(this, kConstructStubOffset)); 9887 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric)); 9888 // It is safe to clear the flag: it will be set again if the map is live. 9889 set_live_objects_may_exist(false); 9890 } 9891 9892 9893 // Called from GC, hence reinterpret_cast and unchecked accessors. 9894 void SharedFunctionInfo::AttachInitialMap(Map* map) { 9895 map->set_bit_field2( 9896 map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo)); 9897 9898 // Resume inobject slack tracking. 9899 set_initial_map(map); 9900 Builtins* builtins = map->GetHeap()->isolate()->builtins(); 9901 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric), 9902 *RawField(this, kConstructStubOffset)); 9903 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown)); 9904 // The map survived the gc, so there may be objects referencing it. 9905 set_live_objects_may_exist(true); 9906 } 9907 9908 9909 void SharedFunctionInfo::ResetForNewContext(int new_ic_age) { 9910 code()->ClearInlineCaches(); 9911 set_ic_age(new_ic_age); 9912 if (code()->kind() == Code::FUNCTION) { 9913 code()->set_profiler_ticks(0); 9914 if (optimization_disabled() && 9915 opt_count() >= FLAG_max_opt_count) { 9916 // Re-enable optimizations if they were disabled due to opt_count limit. 9917 set_optimization_disabled(false); 9918 code()->set_optimizable(true); 9919 } 9920 set_opt_count(0); 9921 set_deopt_count(0); 9922 } 9923 } 9924 9925 9926 static void GetMinInobjectSlack(Map* map, void* data) { 9927 int slack = map->unused_property_fields(); 9928 if (*reinterpret_cast<int*>(data) > slack) { 9929 *reinterpret_cast<int*>(data) = slack; 9930 } 9931 } 9932 9933 9934 static void ShrinkInstanceSize(Map* map, void* data) { 9935 int slack = *reinterpret_cast<int*>(data); 9936 map->set_inobject_properties(map->inobject_properties() - slack); 9937 map->set_unused_property_fields(map->unused_property_fields() - slack); 9938 map->set_instance_size(map->instance_size() - slack * kPointerSize); 9939 9940 // Visitor id might depend on the instance size, recalculate it. 9941 map->set_visitor_id(StaticVisitorBase::GetVisitorId(map)); 9942 } 9943 9944 9945 void SharedFunctionInfo::CompleteInobjectSlackTracking() { 9946 ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress()); 9947 Map* map = Map::cast(initial_map()); 9948 9949 Heap* heap = map->GetHeap(); 9950 set_initial_map(heap->undefined_value()); 9951 Builtins* builtins = heap->isolate()->builtins(); 9952 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), 9953 construct_stub()); 9954 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric)); 9955 9956 int slack = map->unused_property_fields(); 9957 map->TraverseTransitionTree(&GetMinInobjectSlack, &slack); 9958 if (slack != 0) { 9959 // Resize the initial map and all maps in its transition tree. 9960 map->TraverseTransitionTree(&ShrinkInstanceSize, &slack); 9961 9962 // Give the correct expected_nof_properties to initial maps created later. 9963 ASSERT(expected_nof_properties() >= slack); 9964 set_expected_nof_properties(expected_nof_properties() - slack); 9965 } 9966 } 9967 9968 9969 int SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context) { 9970 ASSERT(native_context->IsNativeContext()); 9971 if (!FLAG_cache_optimized_code) return -1; 9972 Object* value = optimized_code_map(); 9973 if (!value->IsSmi()) { 9974 FixedArray* optimized_code_map = FixedArray::cast(value); 9975 int length = optimized_code_map->length(); 9976 for (int i = kEntriesStart; i < length; i += kEntryLength) { 9977 if (optimized_code_map->get(i) == native_context) { 9978 return i + 1; 9979 } 9980 } 9981 if (FLAG_trace_opt) { 9982 PrintF("[didn't find optimized code in optimized code map for "); 9983 ShortPrint(); 9984 PrintF("]\n"); 9985 } 9986 } 9987 return -1; 9988 } 9989 9990 9991 #define DECLARE_TAG(ignore1, name, ignore2) name, 9992 const char* const VisitorSynchronization::kTags[ 9993 VisitorSynchronization::kNumberOfSyncTags] = { 9994 VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) 9995 }; 9996 #undef DECLARE_TAG 9997 9998 9999 #define DECLARE_TAG(ignore1, ignore2, name) name, 10000 const char* const VisitorSynchronization::kTagNames[ 10001 VisitorSynchronization::kNumberOfSyncTags] = { 10002 VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) 10003 }; 10004 #undef DECLARE_TAG 10005 10006 10007 void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { 10008 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); 10009 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); 10010 Object* old_target = target; 10011 VisitPointer(&target); 10012 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. 10013 } 10014 10015 10016 void ObjectVisitor::VisitCodeAgeSequence(RelocInfo* rinfo) { 10017 ASSERT(RelocInfo::IsCodeAgeSequence(rinfo->rmode())); 10018 Object* stub = rinfo->code_age_stub(); 10019 if (stub) { 10020 VisitPointer(&stub); 10021 } 10022 } 10023 10024 10025 void ObjectVisitor::VisitCodeEntry(Address entry_address) { 10026 Object* code = Code::GetObjectFromEntryAddress(entry_address); 10027 Object* old_code = code; 10028 VisitPointer(&code); 10029 if (code != old_code) { 10030 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry(); 10031 } 10032 } 10033 10034 10035 void ObjectVisitor::VisitCell(RelocInfo* rinfo) { 10036 ASSERT(rinfo->rmode() == RelocInfo::CELL); 10037 Object* cell = rinfo->target_cell(); 10038 Object* old_cell = cell; 10039 VisitPointer(&cell); 10040 if (cell != old_cell) { 10041 rinfo->set_target_cell(reinterpret_cast<Cell*>(cell)); 10042 } 10043 } 10044 10045 10046 void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) { 10047 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) && 10048 rinfo->IsPatchedReturnSequence()) || 10049 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && 10050 rinfo->IsPatchedDebugBreakSlotSequence())); 10051 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address()); 10052 Object* old_target = target; 10053 VisitPointer(&target); 10054 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. 10055 } 10056 10057 10058 void ObjectVisitor::VisitEmbeddedPointer(RelocInfo* rinfo) { 10059 ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); 10060 VisitPointer(rinfo->target_object_address()); 10061 } 10062 10063 10064 void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) { 10065 Address* p = rinfo->target_reference_address(); 10066 VisitExternalReferences(p, p + 1); 10067 } 10068 10069 10070 void Code::InvalidateRelocation() { 10071 set_relocation_info(GetHeap()->empty_byte_array()); 10072 } 10073 10074 10075 void Code::Relocate(intptr_t delta) { 10076 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) { 10077 it.rinfo()->apply(delta); 10078 } 10079 CPU::FlushICache(instruction_start(), instruction_size()); 10080 } 10081 10082 10083 void Code::CopyFrom(const CodeDesc& desc) { 10084 ASSERT(Marking::Color(this) == Marking::WHITE_OBJECT); 10085 10086 // copy code 10087 CopyBytes(instruction_start(), desc.buffer, 10088 static_cast<size_t>(desc.instr_size)); 10089 10090 // copy reloc info 10091 CopyBytes(relocation_start(), 10092 desc.buffer + desc.buffer_size - desc.reloc_size, 10093 static_cast<size_t>(desc.reloc_size)); 10094 10095 // unbox handles and relocate 10096 intptr_t delta = instruction_start() - desc.buffer; 10097 int mode_mask = RelocInfo::kCodeTargetMask | 10098 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | 10099 RelocInfo::ModeMask(RelocInfo::CELL) | 10100 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) | 10101 RelocInfo::kApplyMask; 10102 // Needed to find target_object and runtime_entry on X64 10103 Assembler* origin = desc.origin; 10104 AllowDeferredHandleDereference embedding_raw_address; 10105 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) { 10106 RelocInfo::Mode mode = it.rinfo()->rmode(); 10107 if (mode == RelocInfo::EMBEDDED_OBJECT) { 10108 Handle<Object> p = it.rinfo()->target_object_handle(origin); 10109 it.rinfo()->set_target_object(*p, SKIP_WRITE_BARRIER); 10110 } else if (mode == RelocInfo::CELL) { 10111 Handle<Cell> cell = it.rinfo()->target_cell_handle(); 10112 it.rinfo()->set_target_cell(*cell, SKIP_WRITE_BARRIER); 10113 } else if (RelocInfo::IsCodeTarget(mode)) { 10114 // rewrite code handles in inline cache targets to direct 10115 // pointers to the first instruction in the code object 10116 Handle<Object> p = it.rinfo()->target_object_handle(origin); 10117 Code* code = Code::cast(*p); 10118 it.rinfo()->set_target_address(code->instruction_start(), 10119 SKIP_WRITE_BARRIER); 10120 } else if (RelocInfo::IsRuntimeEntry(mode)) { 10121 Address p = it.rinfo()->target_runtime_entry(origin); 10122 it.rinfo()->set_target_runtime_entry(p, SKIP_WRITE_BARRIER); 10123 } else { 10124 it.rinfo()->apply(delta); 10125 } 10126 } 10127 CPU::FlushICache(instruction_start(), instruction_size()); 10128 } 10129 10130 10131 // Locate the source position which is closest to the address in the code. This 10132 // is using the source position information embedded in the relocation info. 10133 // The position returned is relative to the beginning of the script where the 10134 // source for this function is found. 10135 int Code::SourcePosition(Address pc) { 10136 int distance = kMaxInt; 10137 int position = RelocInfo::kNoPosition; // Initially no position found. 10138 // Run through all the relocation info to find the best matching source 10139 // position. All the code needs to be considered as the sequence of the 10140 // instructions in the code does not necessarily follow the same order as the 10141 // source. 10142 RelocIterator it(this, RelocInfo::kPositionMask); 10143 while (!it.done()) { 10144 // Only look at positions after the current pc. 10145 if (it.rinfo()->pc() < pc) { 10146 // Get position and distance. 10147 10148 int dist = static_cast<int>(pc - it.rinfo()->pc()); 10149 int pos = static_cast<int>(it.rinfo()->data()); 10150 // If this position is closer than the current candidate or if it has the 10151 // same distance as the current candidate and the position is higher then 10152 // this position is the new candidate. 10153 if ((dist < distance) || 10154 (dist == distance && pos > position)) { 10155 position = pos; 10156 distance = dist; 10157 } 10158 } 10159 it.next(); 10160 } 10161 return position; 10162 } 10163 10164 10165 // Same as Code::SourcePosition above except it only looks for statement 10166 // positions. 10167 int Code::SourceStatementPosition(Address pc) { 10168 // First find the position as close as possible using all position 10169 // information. 10170 int position = SourcePosition(pc); 10171 // Now find the closest statement position before the position. 10172 int statement_position = 0; 10173 RelocIterator it(this, RelocInfo::kPositionMask); 10174 while (!it.done()) { 10175 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) { 10176 int p = static_cast<int>(it.rinfo()->data()); 10177 if (statement_position < p && p <= position) { 10178 statement_position = p; 10179 } 10180 } 10181 it.next(); 10182 } 10183 return statement_position; 10184 } 10185 10186 10187 SafepointEntry Code::GetSafepointEntry(Address pc) { 10188 SafepointTable table(this); 10189 return table.FindEntry(pc); 10190 } 10191 10192 10193 Object* Code::FindNthObject(int n, Map* match_map) { 10194 ASSERT(is_inline_cache_stub()); 10195 DisallowHeapAllocation no_allocation; 10196 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); 10197 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10198 RelocInfo* info = it.rinfo(); 10199 Object* object = info->target_object(); 10200 if (object->IsHeapObject()) { 10201 if (HeapObject::cast(object)->map() == match_map) { 10202 if (--n == 0) return object; 10203 } 10204 } 10205 } 10206 return NULL; 10207 } 10208 10209 10210 Map* Code::FindFirstMap() { 10211 Object* result = FindNthObject(1, GetHeap()->meta_map()); 10212 return (result != NULL) ? Map::cast(result) : NULL; 10213 } 10214 10215 10216 void Code::ReplaceNthObject(int n, 10217 Map* match_map, 10218 Object* replace_with) { 10219 ASSERT(is_inline_cache_stub()); 10220 DisallowHeapAllocation no_allocation; 10221 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); 10222 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10223 RelocInfo* info = it.rinfo(); 10224 Object* object = info->target_object(); 10225 if (object->IsHeapObject()) { 10226 if (HeapObject::cast(object)->map() == match_map) { 10227 if (--n == 0) { 10228 info->set_target_object(replace_with); 10229 return; 10230 } 10231 } 10232 } 10233 } 10234 UNREACHABLE(); 10235 } 10236 10237 10238 void Code::FindAllMaps(MapHandleList* maps) { 10239 ASSERT(is_inline_cache_stub()); 10240 DisallowHeapAllocation no_allocation; 10241 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); 10242 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10243 RelocInfo* info = it.rinfo(); 10244 Object* object = info->target_object(); 10245 if (object->IsMap()) maps->Add(Handle<Map>(Map::cast(object))); 10246 } 10247 } 10248 10249 10250 void Code::ReplaceFirstMap(Map* replace_with) { 10251 ReplaceNthObject(1, GetHeap()->meta_map(), replace_with); 10252 } 10253 10254 10255 Code* Code::FindFirstCode() { 10256 ASSERT(is_inline_cache_stub()); 10257 DisallowHeapAllocation no_allocation; 10258 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); 10259 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10260 RelocInfo* info = it.rinfo(); 10261 return Code::GetCodeFromTargetAddress(info->target_address()); 10262 } 10263 return NULL; 10264 } 10265 10266 10267 void Code::FindAllCode(CodeHandleList* code_list, int length) { 10268 ASSERT(is_inline_cache_stub()); 10269 DisallowHeapAllocation no_allocation; 10270 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); 10271 int i = 0; 10272 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10273 if (i++ == length) return; 10274 RelocInfo* info = it.rinfo(); 10275 Code* code = Code::GetCodeFromTargetAddress(info->target_address()); 10276 ASSERT(code->kind() == Code::STUB); 10277 code_list->Add(Handle<Code>(code)); 10278 } 10279 UNREACHABLE(); 10280 } 10281 10282 10283 Name* Code::FindFirstName() { 10284 ASSERT(is_inline_cache_stub()); 10285 DisallowHeapAllocation no_allocation; 10286 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); 10287 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10288 RelocInfo* info = it.rinfo(); 10289 Object* object = info->target_object(); 10290 if (object->IsName()) return Name::cast(object); 10291 } 10292 return NULL; 10293 } 10294 10295 10296 void Code::ReplaceNthCell(int n, Cell* replace_with) { 10297 ASSERT(is_inline_cache_stub()); 10298 DisallowHeapAllocation no_allocation; 10299 int mask = RelocInfo::ModeMask(RelocInfo::CELL); 10300 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10301 RelocInfo* info = it.rinfo(); 10302 if (--n == 0) { 10303 info->set_target_cell(replace_with); 10304 return; 10305 } 10306 } 10307 UNREACHABLE(); 10308 } 10309 10310 10311 void Code::ClearInlineCaches() { 10312 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | 10313 RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) | 10314 RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID) | 10315 RelocInfo::ModeMask(RelocInfo::CODE_TARGET_CONTEXT); 10316 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10317 RelocInfo* info = it.rinfo(); 10318 Code* target(Code::GetCodeFromTargetAddress(info->target_address())); 10319 if (target->is_inline_cache_stub()) { 10320 IC::Clear(info->pc()); 10321 } 10322 } 10323 } 10324 10325 10326 void Code::ClearTypeFeedbackCells(Heap* heap) { 10327 if (kind() != FUNCTION) return; 10328 Object* raw_info = type_feedback_info(); 10329 if (raw_info->IsTypeFeedbackInfo()) { 10330 TypeFeedbackCells* type_feedback_cells = 10331 TypeFeedbackInfo::cast(raw_info)->type_feedback_cells(); 10332 for (int i = 0; i < type_feedback_cells->CellCount(); i++) { 10333 Cell* cell = type_feedback_cells->GetCell(i); 10334 // Don't clear AllocationSites 10335 Object* value = cell->value(); 10336 if (value == NULL || !value->IsAllocationSite()) { 10337 cell->set_value(TypeFeedbackCells::RawUninitializedSentinel(heap)); 10338 } 10339 } 10340 } 10341 } 10342 10343 10344 bool Code::allowed_in_shared_map_code_cache() { 10345 return is_keyed_load_stub() || is_keyed_store_stub() || 10346 (is_compare_ic_stub() && 10347 ICCompareStub::CompareState(stub_info()) == CompareIC::KNOWN_OBJECT); 10348 } 10349 10350 10351 void Code::MakeCodeAgeSequenceYoung(byte* sequence) { 10352 PatchPlatformCodeAge(sequence, kNoAge, NO_MARKING_PARITY); 10353 } 10354 10355 10356 void Code::MakeOlder(MarkingParity current_parity) { 10357 byte* sequence = FindCodeAgeSequence(); 10358 if (sequence != NULL) { 10359 Age age; 10360 MarkingParity code_parity; 10361 GetCodeAgeAndParity(sequence, &age, &code_parity); 10362 if (age != kLastCodeAge && code_parity != current_parity) { 10363 PatchPlatformCodeAge(sequence, static_cast<Age>(age + 1), 10364 current_parity); 10365 } 10366 } 10367 } 10368 10369 10370 bool Code::IsOld() { 10371 byte* sequence = FindCodeAgeSequence(); 10372 if (sequence == NULL) return false; 10373 Age age; 10374 MarkingParity parity; 10375 GetCodeAgeAndParity(sequence, &age, &parity); 10376 return age >= kSexagenarianCodeAge; 10377 } 10378 10379 10380 byte* Code::FindCodeAgeSequence() { 10381 return FLAG_age_code && 10382 prologue_offset() != kPrologueOffsetNotSet && 10383 (kind() == OPTIMIZED_FUNCTION || 10384 (kind() == FUNCTION && !has_debug_break_slots())) 10385 ? instruction_start() + prologue_offset() 10386 : NULL; 10387 } 10388 10389 10390 int Code::GetAge() { 10391 byte* sequence = FindCodeAgeSequence(); 10392 if (sequence == NULL) { 10393 return Code::kNoAge; 10394 } 10395 Age age; 10396 MarkingParity parity; 10397 GetCodeAgeAndParity(sequence, &age, &parity); 10398 return age; 10399 } 10400 10401 10402 void Code::GetCodeAgeAndParity(Code* code, Age* age, 10403 MarkingParity* parity) { 10404 Isolate* isolate = Isolate::Current(); 10405 Builtins* builtins = isolate->builtins(); 10406 Code* stub = NULL; 10407 #define HANDLE_CODE_AGE(AGE) \ 10408 stub = *builtins->Make##AGE##CodeYoungAgainEvenMarking(); \ 10409 if (code == stub) { \ 10410 *age = k##AGE##CodeAge; \ 10411 *parity = EVEN_MARKING_PARITY; \ 10412 return; \ 10413 } \ 10414 stub = *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ 10415 if (code == stub) { \ 10416 *age = k##AGE##CodeAge; \ 10417 *parity = ODD_MARKING_PARITY; \ 10418 return; \ 10419 } 10420 CODE_AGE_LIST(HANDLE_CODE_AGE) 10421 #undef HANDLE_CODE_AGE 10422 UNREACHABLE(); 10423 } 10424 10425 10426 Code* Code::GetCodeAgeStub(Age age, MarkingParity parity) { 10427 Isolate* isolate = Isolate::Current(); 10428 Builtins* builtins = isolate->builtins(); 10429 switch (age) { 10430 #define HANDLE_CODE_AGE(AGE) \ 10431 case k##AGE##CodeAge: { \ 10432 Code* stub = parity == EVEN_MARKING_PARITY \ 10433 ? *builtins->Make##AGE##CodeYoungAgainEvenMarking() \ 10434 : *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ 10435 return stub; \ 10436 } 10437 CODE_AGE_LIST(HANDLE_CODE_AGE) 10438 #undef HANDLE_CODE_AGE 10439 default: 10440 UNREACHABLE(); 10441 break; 10442 } 10443 return NULL; 10444 } 10445 10446 10447 void Code::PrintDeoptLocation(int bailout_id) { 10448 const char* last_comment = NULL; 10449 int mask = RelocInfo::ModeMask(RelocInfo::COMMENT) 10450 | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); 10451 for (RelocIterator it(this, mask); !it.done(); it.next()) { 10452 RelocInfo* info = it.rinfo(); 10453 if (info->rmode() == RelocInfo::COMMENT) { 10454 last_comment = reinterpret_cast<const char*>(info->data()); 10455 } else if (last_comment != NULL) { 10456 if ((bailout_id == Deoptimizer::GetDeoptimizationId( 10457 GetIsolate(), info->target_address(), Deoptimizer::EAGER)) || 10458 (bailout_id == Deoptimizer::GetDeoptimizationId( 10459 GetIsolate(), info->target_address(), Deoptimizer::SOFT))) { 10460 CHECK(RelocInfo::IsRuntimeEntry(info->rmode())); 10461 PrintF(" %s\n", last_comment); 10462 return; 10463 } 10464 } 10465 } 10466 } 10467 10468 10469 bool Code::CanDeoptAt(Address pc) { 10470 DeoptimizationInputData* deopt_data = 10471 DeoptimizationInputData::cast(deoptimization_data()); 10472 Address code_start_address = instruction_start(); 10473 for (int i = 0; i < deopt_data->DeoptCount(); i++) { 10474 if (deopt_data->Pc(i)->value() == -1) continue; 10475 Address address = code_start_address + deopt_data->Pc(i)->value(); 10476 if (address == pc) return true; 10477 } 10478 return false; 10479 } 10480 10481 10482 // Identify kind of code. 10483 const char* Code::Kind2String(Kind kind) { 10484 switch (kind) { 10485 #define CASE(name) case name: return #name; 10486 CODE_KIND_LIST(CASE) 10487 #undef CASE 10488 case NUMBER_OF_KINDS: break; 10489 } 10490 UNREACHABLE(); 10491 return NULL; 10492 } 10493 10494 10495 #ifdef ENABLE_DISASSEMBLER 10496 10497 void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { 10498 disasm::NameConverter converter; 10499 int deopt_count = DeoptCount(); 10500 PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count); 10501 if (0 == deopt_count) return; 10502 10503 PrintF(out, "%6s %6s %6s %6s %12s\n", "index", "ast id", "argc", "pc", 10504 FLAG_print_code_verbose ? "commands" : ""); 10505 for (int i = 0; i < deopt_count; i++) { 10506 PrintF(out, "%6d %6d %6d %6d", 10507 i, 10508 AstId(i).ToInt(), 10509 ArgumentsStackHeight(i)->value(), 10510 Pc(i)->value()); 10511 10512 if (!FLAG_print_code_verbose) { 10513 PrintF(out, "\n"); 10514 continue; 10515 } 10516 // Print details of the frame translation. 10517 int translation_index = TranslationIndex(i)->value(); 10518 TranslationIterator iterator(TranslationByteArray(), translation_index); 10519 Translation::Opcode opcode = 10520 static_cast<Translation::Opcode>(iterator.Next()); 10521 ASSERT(Translation::BEGIN == opcode); 10522 int frame_count = iterator.Next(); 10523 int jsframe_count = iterator.Next(); 10524 PrintF(out, " %s {frame count=%d, js frame count=%d}\n", 10525 Translation::StringFor(opcode), 10526 frame_count, 10527 jsframe_count); 10528 10529 while (iterator.HasNext() && 10530 Translation::BEGIN != 10531 (opcode = static_cast<Translation::Opcode>(iterator.Next()))) { 10532 PrintF(out, "%24s %s ", "", Translation::StringFor(opcode)); 10533 10534 switch (opcode) { 10535 case Translation::BEGIN: 10536 UNREACHABLE(); 10537 break; 10538 10539 case Translation::JS_FRAME: { 10540 int ast_id = iterator.Next(); 10541 int function_id = iterator.Next(); 10542 unsigned height = iterator.Next(); 10543 PrintF(out, "{ast_id=%d, function=", ast_id); 10544 if (function_id != Translation::kSelfLiteralId) { 10545 Object* function = LiteralArray()->get(function_id); 10546 JSFunction::cast(function)->PrintName(out); 10547 } else { 10548 PrintF(out, "<self>"); 10549 } 10550 PrintF(out, ", height=%u}", height); 10551 break; 10552 } 10553 10554 case Translation::COMPILED_STUB_FRAME: { 10555 Code::Kind stub_kind = static_cast<Code::Kind>(iterator.Next()); 10556 PrintF(out, "{kind=%d}", stub_kind); 10557 break; 10558 } 10559 10560 case Translation::ARGUMENTS_ADAPTOR_FRAME: 10561 case Translation::CONSTRUCT_STUB_FRAME: { 10562 int function_id = iterator.Next(); 10563 JSFunction* function = 10564 JSFunction::cast(LiteralArray()->get(function_id)); 10565 unsigned height = iterator.Next(); 10566 PrintF(out, "{function="); 10567 function->PrintName(out); 10568 PrintF(out, ", height=%u}", height); 10569 break; 10570 } 10571 10572 case Translation::GETTER_STUB_FRAME: 10573 case Translation::SETTER_STUB_FRAME: { 10574 int function_id = iterator.Next(); 10575 JSFunction* function = 10576 JSFunction::cast(LiteralArray()->get(function_id)); 10577 PrintF(out, "{function="); 10578 function->PrintName(out); 10579 PrintF(out, "}"); 10580 break; 10581 } 10582 10583 case Translation::REGISTER: { 10584 int reg_code = iterator.Next(); 10585 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code)); 10586 break; 10587 } 10588 10589 case Translation::INT32_REGISTER: { 10590 int reg_code = iterator.Next(); 10591 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code)); 10592 break; 10593 } 10594 10595 case Translation::UINT32_REGISTER: { 10596 int reg_code = iterator.Next(); 10597 PrintF(out, "{input=%s (unsigned)}", 10598 converter.NameOfCPURegister(reg_code)); 10599 break; 10600 } 10601 10602 case Translation::DOUBLE_REGISTER: { 10603 int reg_code = iterator.Next(); 10604 PrintF(out, "{input=%s}", 10605 DoubleRegister::AllocationIndexToString(reg_code)); 10606 break; 10607 } 10608 10609 case Translation::STACK_SLOT: { 10610 int input_slot_index = iterator.Next(); 10611 PrintF(out, "{input=%d}", input_slot_index); 10612 break; 10613 } 10614 10615 case Translation::INT32_STACK_SLOT: { 10616 int input_slot_index = iterator.Next(); 10617 PrintF(out, "{input=%d}", input_slot_index); 10618 break; 10619 } 10620 10621 case Translation::UINT32_STACK_SLOT: { 10622 int input_slot_index = iterator.Next(); 10623 PrintF(out, "{input=%d (unsigned)}", input_slot_index); 10624 break; 10625 } 10626 10627 case Translation::DOUBLE_STACK_SLOT: { 10628 int input_slot_index = iterator.Next(); 10629 PrintF(out, "{input=%d}", input_slot_index); 10630 break; 10631 } 10632 10633 case Translation::LITERAL: { 10634 unsigned literal_index = iterator.Next(); 10635 PrintF(out, "{literal_id=%u}", literal_index); 10636 break; 10637 } 10638 10639 case Translation::DUPLICATED_OBJECT: { 10640 int object_index = iterator.Next(); 10641 PrintF(out, "{object_index=%d}", object_index); 10642 break; 10643 } 10644 10645 case Translation::ARGUMENTS_OBJECT: 10646 case Translation::CAPTURED_OBJECT: { 10647 int args_length = iterator.Next(); 10648 PrintF(out, "{length=%d}", args_length); 10649 break; 10650 } 10651 } 10652 PrintF(out, "\n"); 10653 } 10654 } 10655 } 10656 10657 10658 void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) { 10659 PrintF(out, "Deoptimization Output Data (deopt points = %d)\n", 10660 this->DeoptPoints()); 10661 if (this->DeoptPoints() == 0) return; 10662 10663 PrintF("%6s %8s %s\n", "ast id", "pc", "state"); 10664 for (int i = 0; i < this->DeoptPoints(); i++) { 10665 int pc_and_state = this->PcAndState(i)->value(); 10666 PrintF("%6d %8d %s\n", 10667 this->AstId(i).ToInt(), 10668 FullCodeGenerator::PcField::decode(pc_and_state), 10669 FullCodeGenerator::State2String( 10670 FullCodeGenerator::StateField::decode(pc_and_state))); 10671 } 10672 } 10673 10674 10675 const char* Code::ICState2String(InlineCacheState state) { 10676 switch (state) { 10677 case UNINITIALIZED: return "UNINITIALIZED"; 10678 case PREMONOMORPHIC: return "PREMONOMORPHIC"; 10679 case MONOMORPHIC: return "MONOMORPHIC"; 10680 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE"; 10681 case POLYMORPHIC: return "POLYMORPHIC"; 10682 case MEGAMORPHIC: return "MEGAMORPHIC"; 10683 case GENERIC: return "GENERIC"; 10684 case DEBUG_STUB: return "DEBUG_STUB"; 10685 } 10686 UNREACHABLE(); 10687 return NULL; 10688 } 10689 10690 10691 const char* Code::StubType2String(StubType type) { 10692 switch (type) { 10693 case NORMAL: return "NORMAL"; 10694 case FIELD: return "FIELD"; 10695 case CONSTANT: return "CONSTANT"; 10696 case CALLBACKS: return "CALLBACKS"; 10697 case INTERCEPTOR: return "INTERCEPTOR"; 10698 case MAP_TRANSITION: return "MAP_TRANSITION"; 10699 case NONEXISTENT: return "NONEXISTENT"; 10700 } 10701 UNREACHABLE(); // keep the compiler happy 10702 return NULL; 10703 } 10704 10705 10706 void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) { 10707 PrintF(out, "extra_ic_state = "); 10708 const char* name = NULL; 10709 switch (kind) { 10710 case CALL_IC: 10711 if (extra == STRING_INDEX_OUT_OF_BOUNDS) { 10712 name = "STRING_INDEX_OUT_OF_BOUNDS"; 10713 } 10714 break; 10715 case STORE_IC: 10716 case KEYED_STORE_IC: 10717 if (extra == kStrictMode) { 10718 name = "STRICT"; 10719 } 10720 break; 10721 default: 10722 break; 10723 } 10724 if (name != NULL) { 10725 PrintF(out, "%s\n", name); 10726 } else { 10727 PrintF(out, "%d\n", extra); 10728 } 10729 } 10730 10731 10732 void Code::Disassemble(const char* name, FILE* out) { 10733 PrintF(out, "kind = %s\n", Kind2String(kind())); 10734 if (is_inline_cache_stub()) { 10735 PrintF(out, "ic_state = %s\n", ICState2String(ic_state())); 10736 PrintExtraICState(out, kind(), needs_extended_extra_ic_state(kind()) ? 10737 extended_extra_ic_state() : extra_ic_state()); 10738 if (ic_state() == MONOMORPHIC) { 10739 PrintF(out, "type = %s\n", StubType2String(type())); 10740 } 10741 if (is_call_stub() || is_keyed_call_stub()) { 10742 PrintF(out, "argc = %d\n", arguments_count()); 10743 } 10744 if (is_compare_ic_stub()) { 10745 ASSERT(major_key() == CodeStub::CompareIC); 10746 CompareIC::State left_state, right_state, handler_state; 10747 Token::Value op; 10748 ICCompareStub::DecodeMinorKey(stub_info(), &left_state, &right_state, 10749 &handler_state, &op); 10750 PrintF(out, "compare_state = %s*%s -> %s\n", 10751 CompareIC::GetStateName(left_state), 10752 CompareIC::GetStateName(right_state), 10753 CompareIC::GetStateName(handler_state)); 10754 PrintF(out, "compare_operation = %s\n", Token::Name(op)); 10755 } 10756 } 10757 if ((name != NULL) && (name[0] != '\0')) { 10758 PrintF(out, "name = %s\n", name); 10759 } 10760 if (kind() == OPTIMIZED_FUNCTION) { 10761 PrintF(out, "stack_slots = %d\n", stack_slots()); 10762 } 10763 10764 PrintF(out, "Instructions (size = %d)\n", instruction_size()); 10765 Disassembler::Decode(out, this); 10766 PrintF(out, "\n"); 10767 10768 if (kind() == FUNCTION) { 10769 DeoptimizationOutputData* data = 10770 DeoptimizationOutputData::cast(this->deoptimization_data()); 10771 data->DeoptimizationOutputDataPrint(out); 10772 } else if (kind() == OPTIMIZED_FUNCTION) { 10773 DeoptimizationInputData* data = 10774 DeoptimizationInputData::cast(this->deoptimization_data()); 10775 data->DeoptimizationInputDataPrint(out); 10776 } 10777 PrintF("\n"); 10778 10779 if (is_crankshafted()) { 10780 SafepointTable table(this); 10781 PrintF(out, "Safepoints (size = %u)\n", table.size()); 10782 for (unsigned i = 0; i < table.length(); i++) { 10783 unsigned pc_offset = table.GetPcOffset(i); 10784 PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset); 10785 table.PrintEntry(i); 10786 PrintF(out, " (sp -> fp)"); 10787 SafepointEntry entry = table.GetEntry(i); 10788 if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) { 10789 PrintF(out, " %6d", entry.deoptimization_index()); 10790 } else { 10791 PrintF(out, " <none>"); 10792 } 10793 if (entry.argument_count() > 0) { 10794 PrintF(out, " argc: %d", entry.argument_count()); 10795 } 10796 PrintF(out, "\n"); 10797 } 10798 PrintF(out, "\n"); 10799 } else if (kind() == FUNCTION) { 10800 unsigned offset = back_edge_table_offset(); 10801 // If there is no back edge table, the "table start" will be at or after 10802 // (due to alignment) the end of the instruction stream. 10803 if (static_cast<int>(offset) < instruction_size()) { 10804 FullCodeGenerator::BackEdgeTableIterator back_edges(this); 10805 10806 PrintF(out, "Back edges (size = %u)\n", back_edges.table_length()); 10807 PrintF(out, "ast_id pc_offset loop_depth\n"); 10808 10809 for ( ; !back_edges.Done(); back_edges.Next()) { 10810 PrintF(out, "%6d %9u %10u\n", back_edges.ast_id().ToInt(), 10811 back_edges.pc_offset(), 10812 back_edges.loop_depth()); 10813 } 10814 10815 PrintF(out, "\n"); 10816 } 10817 #ifdef OBJECT_PRINT 10818 if (!type_feedback_info()->IsUndefined()) { 10819 TypeFeedbackInfo::cast(type_feedback_info())->TypeFeedbackInfoPrint(out); 10820 PrintF(out, "\n"); 10821 } 10822 #endif 10823 } 10824 10825 PrintF("RelocInfo (size = %d)\n", relocation_size()); 10826 for (RelocIterator it(this); !it.done(); it.next()) { 10827 it.rinfo()->Print(GetIsolate(), out); 10828 } 10829 PrintF(out, "\n"); 10830 } 10831 #endif // ENABLE_DISASSEMBLER 10832 10833 10834 MaybeObject* JSObject::SetFastElementsCapacityAndLength( 10835 int capacity, 10836 int length, 10837 SetFastElementsCapacitySmiMode smi_mode) { 10838 Heap* heap = GetHeap(); 10839 // We should never end in here with a pixel or external array. 10840 ASSERT(!HasExternalArrayElements()); 10841 ASSERT(!map()->is_observed()); 10842 10843 // Allocate a new fast elements backing store. 10844 FixedArray* new_elements; 10845 MaybeObject* maybe = heap->AllocateUninitializedFixedArray(capacity); 10846 if (!maybe->To(&new_elements)) return maybe; 10847 10848 ElementsKind elements_kind = GetElementsKind(); 10849 ElementsKind new_elements_kind; 10850 // The resized array has FAST_*_SMI_ELEMENTS if the capacity mode forces it, 10851 // or if it's allowed and the old elements array contained only SMIs. 10852 bool has_fast_smi_elements = 10853 (smi_mode == kForceSmiElements) || 10854 ((smi_mode == kAllowSmiElements) && HasFastSmiElements()); 10855 if (has_fast_smi_elements) { 10856 if (IsHoleyElementsKind(elements_kind)) { 10857 new_elements_kind = FAST_HOLEY_SMI_ELEMENTS; 10858 } else { 10859 new_elements_kind = FAST_SMI_ELEMENTS; 10860 } 10861 } else { 10862 if (IsHoleyElementsKind(elements_kind)) { 10863 new_elements_kind = FAST_HOLEY_ELEMENTS; 10864 } else { 10865 new_elements_kind = FAST_ELEMENTS; 10866 } 10867 } 10868 FixedArrayBase* old_elements = elements(); 10869 ElementsAccessor* accessor = ElementsAccessor::ForKind(new_elements_kind); 10870 MaybeObject* maybe_obj = 10871 accessor->CopyElements(this, new_elements, elements_kind); 10872 if (maybe_obj->IsFailure()) return maybe_obj; 10873 10874 if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { 10875 Map* new_map = map(); 10876 if (new_elements_kind != elements_kind) { 10877 MaybeObject* maybe = 10878 GetElementsTransitionMap(GetIsolate(), new_elements_kind); 10879 if (!maybe->To(&new_map)) return maybe; 10880 } 10881 ValidateElements(); 10882 set_map_and_elements(new_map, new_elements); 10883 } else { 10884 FixedArray* parameter_map = FixedArray::cast(old_elements); 10885 parameter_map->set(1, new_elements); 10886 } 10887 10888 if (FLAG_trace_elements_transitions) { 10889 PrintElementsTransition(stdout, elements_kind, old_elements, 10890 GetElementsKind(), new_elements); 10891 } 10892 10893 if (IsJSArray()) { 10894 JSArray::cast(this)->set_length(Smi::FromInt(length)); 10895 } 10896 return new_elements; 10897 } 10898 10899 10900 MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( 10901 int capacity, 10902 int length) { 10903 Heap* heap = GetHeap(); 10904 // We should never end in here with a pixel or external array. 10905 ASSERT(!HasExternalArrayElements()); 10906 ASSERT(!map()->is_observed()); 10907 10908 FixedArrayBase* elems; 10909 { MaybeObject* maybe_obj = 10910 heap->AllocateUninitializedFixedDoubleArray(capacity); 10911 if (!maybe_obj->To(&elems)) return maybe_obj; 10912 } 10913 10914 ElementsKind elements_kind = GetElementsKind(); 10915 ElementsKind new_elements_kind = elements_kind; 10916 if (IsHoleyElementsKind(elements_kind)) { 10917 new_elements_kind = FAST_HOLEY_DOUBLE_ELEMENTS; 10918 } else { 10919 new_elements_kind = FAST_DOUBLE_ELEMENTS; 10920 } 10921 10922 Map* new_map; 10923 { MaybeObject* maybe_obj = 10924 GetElementsTransitionMap(heap->isolate(), new_elements_kind); 10925 if (!maybe_obj->To(&new_map)) return maybe_obj; 10926 } 10927 10928 FixedArrayBase* old_elements = elements(); 10929 ElementsAccessor* accessor = ElementsAccessor::ForKind(FAST_DOUBLE_ELEMENTS); 10930 { MaybeObject* maybe_obj = 10931 accessor->CopyElements(this, elems, elements_kind); 10932 if (maybe_obj->IsFailure()) return maybe_obj; 10933 } 10934 if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { 10935 ValidateElements(); 10936 set_map_and_elements(new_map, elems); 10937 } else { 10938 FixedArray* parameter_map = FixedArray::cast(old_elements); 10939 parameter_map->set(1, elems); 10940 } 10941 10942 if (FLAG_trace_elements_transitions) { 10943 PrintElementsTransition(stdout, elements_kind, old_elements, 10944 GetElementsKind(), elems); 10945 } 10946 10947 if (IsJSArray()) { 10948 JSArray::cast(this)->set_length(Smi::FromInt(length)); 10949 } 10950 10951 return this; 10952 } 10953 10954 10955 MaybeObject* JSArray::Initialize(int capacity, int length) { 10956 ASSERT(capacity >= 0); 10957 return GetHeap()->AllocateJSArrayStorage(this, length, capacity, 10958 INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); 10959 } 10960 10961 10962 void JSArray::Expand(int required_size) { 10963 GetIsolate()->factory()->SetElementsCapacityAndLength( 10964 Handle<JSArray>(this), required_size, required_size); 10965 } 10966 10967 10968 // Returns false if the passed-in index is marked non-configurable, 10969 // which will cause the ES5 truncation operation to halt, and thus 10970 // no further old values need be collected. 10971 static bool GetOldValue(Isolate* isolate, 10972 Handle<JSObject> object, 10973 uint32_t index, 10974 List<Handle<Object> >* old_values, 10975 List<uint32_t>* indices) { 10976 PropertyAttributes attributes = object->GetLocalElementAttribute(index); 10977 ASSERT(attributes != ABSENT); 10978 if (attributes == DONT_DELETE) return false; 10979 old_values->Add(object->GetLocalElementAccessorPair(index) == NULL 10980 ? Object::GetElement(object, index) 10981 : Handle<Object>::cast(isolate->factory()->the_hole_value())); 10982 indices->Add(index); 10983 return true; 10984 } 10985 10986 static void EnqueueSpliceRecord(Handle<JSArray> object, 10987 uint32_t index, 10988 Handle<JSArray> deleted, 10989 uint32_t add_count) { 10990 Isolate* isolate = object->GetIsolate(); 10991 HandleScope scope(isolate); 10992 Handle<Object> index_object = isolate->factory()->NewNumberFromUint(index); 10993 Handle<Object> add_count_object = 10994 isolate->factory()->NewNumberFromUint(add_count); 10995 10996 Handle<Object> args[] = 10997 { object, index_object, deleted, add_count_object }; 10998 10999 bool threw; 11000 Execution::Call(Handle<JSFunction>(isolate->observers_enqueue_splice()), 11001 isolate->factory()->undefined_value(), ARRAY_SIZE(args), args, 11002 &threw); 11003 ASSERT(!threw); 11004 } 11005 11006 11007 static void BeginPerformSplice(Handle<JSArray> object) { 11008 Isolate* isolate = object->GetIsolate(); 11009 HandleScope scope(isolate); 11010 Handle<Object> args[] = { object }; 11011 11012 bool threw; 11013 Execution::Call(Handle<JSFunction>(isolate->observers_begin_perform_splice()), 11014 isolate->factory()->undefined_value(), ARRAY_SIZE(args), args, 11015 &threw); 11016 ASSERT(!threw); 11017 } 11018 11019 11020 static void EndPerformSplice(Handle<JSArray> object) { 11021 Isolate* isolate = object->GetIsolate(); 11022 HandleScope scope(isolate); 11023 Handle<Object> args[] = { object }; 11024 11025 bool threw; 11026 Execution::Call(Handle<JSFunction>(isolate->observers_end_perform_splice()), 11027 isolate->factory()->undefined_value(), ARRAY_SIZE(args), args, 11028 &threw); 11029 ASSERT(!threw); 11030 } 11031 11032 11033 MaybeObject* JSArray::SetElementsLength(Object* len) { 11034 // We should never end in here with a pixel or external array. 11035 ASSERT(AllowsSetElementsLength()); 11036 if (!(FLAG_harmony_observation && map()->is_observed())) 11037 return GetElementsAccessor()->SetLength(this, len); 11038 11039 Isolate* isolate = GetIsolate(); 11040 HandleScope scope(isolate); 11041 Handle<JSArray> self(this); 11042 List<uint32_t> indices; 11043 List<Handle<Object> > old_values; 11044 Handle<Object> old_length_handle(self->length(), isolate); 11045 Handle<Object> new_length_handle(len, isolate); 11046 uint32_t old_length = 0; 11047 CHECK(old_length_handle->ToArrayIndex(&old_length)); 11048 uint32_t new_length = 0; 11049 if (!new_length_handle->ToArrayIndex(&new_length)) 11050 return Failure::InternalError(); 11051 11052 // Observed arrays should always be in dictionary mode; 11053 // if they were in fast mode, the below is slower than necessary 11054 // as it iterates over the array backing store multiple times. 11055 ASSERT(self->HasDictionaryElements()); 11056 static const PropertyAttributes kNoAttrFilter = NONE; 11057 int num_elements = self->NumberOfLocalElements(kNoAttrFilter); 11058 if (num_elements > 0) { 11059 if (old_length == static_cast<uint32_t>(num_elements)) { 11060 // Simple case for arrays without holes. 11061 for (uint32_t i = old_length - 1; i + 1 > new_length; --i) { 11062 if (!GetOldValue(isolate, self, i, &old_values, &indices)) break; 11063 } 11064 } else { 11065 // For sparse arrays, only iterate over existing elements. 11066 Handle<FixedArray> keys = isolate->factory()->NewFixedArray(num_elements); 11067 self->GetLocalElementKeys(*keys, kNoAttrFilter); 11068 while (num_elements-- > 0) { 11069 uint32_t index = NumberToUint32(keys->get(num_elements)); 11070 if (index < new_length) break; 11071 if (!GetOldValue(isolate, self, index, &old_values, &indices)) break; 11072 } 11073 } 11074 } 11075 11076 MaybeObject* result = 11077 self->GetElementsAccessor()->SetLength(*self, *new_length_handle); 11078 Handle<Object> hresult; 11079 if (!result->ToHandle(&hresult, isolate)) return result; 11080 11081 CHECK(self->length()->ToArrayIndex(&new_length)); 11082 if (old_length == new_length) return *hresult; 11083 11084 BeginPerformSplice(self); 11085 11086 for (int i = 0; i < indices.length(); ++i) { 11087 JSObject::EnqueueChangeRecord( 11088 self, "deleted", isolate->factory()->Uint32ToString(indices[i]), 11089 old_values[i]); 11090 } 11091 JSObject::EnqueueChangeRecord( 11092 self, "updated", isolate->factory()->length_string(), 11093 old_length_handle); 11094 11095 EndPerformSplice(self); 11096 11097 uint32_t index = Min(old_length, new_length); 11098 uint32_t add_count = new_length > old_length ? new_length - old_length : 0; 11099 uint32_t delete_count = new_length < old_length ? old_length - new_length : 0; 11100 Handle<JSArray> deleted = isolate->factory()->NewJSArray(0); 11101 if (delete_count > 0) { 11102 for (int i = indices.length() - 1; i >= 0; i--) { 11103 JSObject::SetElement(deleted, indices[i] - index, old_values[i], NONE, 11104 kNonStrictMode); 11105 } 11106 11107 SetProperty(deleted, isolate->factory()->length_string(), 11108 isolate->factory()->NewNumberFromUint(delete_count), 11109 NONE, kNonStrictMode); 11110 } 11111 11112 EnqueueSpliceRecord(self, index, deleted, add_count); 11113 11114 return *hresult; 11115 } 11116 11117 11118 Handle<Map> Map::GetPrototypeTransition(Handle<Map> map, 11119 Handle<Object> prototype) { 11120 FixedArray* cache = map->GetPrototypeTransitions(); 11121 int number_of_transitions = map->NumberOfProtoTransitions(); 11122 const int proto_offset = 11123 kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset; 11124 const int map_offset = kProtoTransitionHeaderSize + kProtoTransitionMapOffset; 11125 const int step = kProtoTransitionElementsPerEntry; 11126 for (int i = 0; i < number_of_transitions; i++) { 11127 if (cache->get(proto_offset + i * step) == *prototype) { 11128 Object* result = cache->get(map_offset + i * step); 11129 return Handle<Map>(Map::cast(result)); 11130 } 11131 } 11132 return Handle<Map>(); 11133 } 11134 11135 11136 Handle<Map> Map::PutPrototypeTransition(Handle<Map> map, 11137 Handle<Object> prototype, 11138 Handle<Map> target_map) { 11139 ASSERT(target_map->IsMap()); 11140 ASSERT(HeapObject::cast(*prototype)->map()->IsMap()); 11141 // Don't cache prototype transition if this map is shared. 11142 if (map->is_shared() || !FLAG_cache_prototype_transitions) return map; 11143 11144 const int step = kProtoTransitionElementsPerEntry; 11145 const int header = kProtoTransitionHeaderSize; 11146 11147 Handle<FixedArray> cache(map->GetPrototypeTransitions()); 11148 int capacity = (cache->length() - header) / step; 11149 int transitions = map->NumberOfProtoTransitions() + 1; 11150 11151 if (transitions > capacity) { 11152 if (capacity > kMaxCachedPrototypeTransitions) return map; 11153 11154 // Grow array by factor 2 over and above what we need. 11155 Factory* factory = map->GetIsolate()->factory(); 11156 cache = factory->CopySizeFixedArray(cache, transitions * 2 * step + header); 11157 11158 CALL_AND_RETRY_OR_DIE(map->GetIsolate(), 11159 map->SetPrototypeTransitions(*cache), 11160 break, 11161 return Handle<Map>()); 11162 } 11163 11164 // Reload number of transitions as GC might shrink them. 11165 int last = map->NumberOfProtoTransitions(); 11166 int entry = header + last * step; 11167 11168 cache->set(entry + kProtoTransitionPrototypeOffset, *prototype); 11169 cache->set(entry + kProtoTransitionMapOffset, *target_map); 11170 map->SetNumberOfProtoTransitions(transitions); 11171 11172 return map; 11173 } 11174 11175 11176 void Map::ZapTransitions() { 11177 TransitionArray* transition_array = transitions(); 11178 // TODO(mstarzinger): Temporarily use a slower version instead of the faster 11179 // MemsetPointer to investigate a crasher. Switch back to MemsetPointer. 11180 Object** data = transition_array->data_start(); 11181 Object* the_hole = GetHeap()->the_hole_value(); 11182 int length = transition_array->length(); 11183 for (int i = 0; i < length; i++) { 11184 data[i] = the_hole; 11185 } 11186 } 11187 11188 11189 void Map::ZapPrototypeTransitions() { 11190 FixedArray* proto_transitions = GetPrototypeTransitions(); 11191 MemsetPointer(proto_transitions->data_start(), 11192 GetHeap()->the_hole_value(), 11193 proto_transitions->length()); 11194 } 11195 11196 11197 void Map::AddDependentCompilationInfo(DependentCode::DependencyGroup group, 11198 CompilationInfo* info) { 11199 Handle<DependentCode> dep(dependent_code()); 11200 Handle<DependentCode> codes = 11201 DependentCode::Insert(dep, group, info->object_wrapper()); 11202 if (*codes != dependent_code()) set_dependent_code(*codes); 11203 info->dependencies(group)->Add(Handle<HeapObject>(this), info->zone()); 11204 } 11205 11206 11207 void Map::AddDependentCode(DependentCode::DependencyGroup group, 11208 Handle<Code> code) { 11209 Handle<DependentCode> codes = DependentCode::Insert( 11210 Handle<DependentCode>(dependent_code()), group, code); 11211 if (*codes != dependent_code()) set_dependent_code(*codes); 11212 } 11213 11214 11215 DependentCode::GroupStartIndexes::GroupStartIndexes(DependentCode* entries) { 11216 Recompute(entries); 11217 } 11218 11219 11220 void DependentCode::GroupStartIndexes::Recompute(DependentCode* entries) { 11221 start_indexes_[0] = 0; 11222 for (int g = 1; g <= kGroupCount; g++) { 11223 int count = entries->number_of_entries(static_cast<DependencyGroup>(g - 1)); 11224 start_indexes_[g] = start_indexes_[g - 1] + count; 11225 } 11226 } 11227 11228 11229 DependentCode* DependentCode::ForObject(Handle<HeapObject> object, 11230 DependencyGroup group) { 11231 AllowDeferredHandleDereference dependencies_are_safe; 11232 if (group == DependentCode::kPropertyCellChangedGroup) { 11233 return Handle<PropertyCell>::cast(object)->dependent_code(); 11234 } 11235 return Handle<Map>::cast(object)->dependent_code(); 11236 } 11237 11238 11239 Handle<DependentCode> DependentCode::Insert(Handle<DependentCode> entries, 11240 DependencyGroup group, 11241 Handle<Object> object) { 11242 GroupStartIndexes starts(*entries); 11243 int start = starts.at(group); 11244 int end = starts.at(group + 1); 11245 int number_of_entries = starts.number_of_entries(); 11246 if (start < end && entries->object_at(end - 1) == *object) { 11247 // Do not append the compilation info if it is already in the array. 11248 // It is sufficient to just check only the last element because 11249 // we process embedded maps of an optimized code in one batch. 11250 return entries; 11251 } 11252 if (entries->length() < kCodesStartIndex + number_of_entries + 1) { 11253 Factory* factory = entries->GetIsolate()->factory(); 11254 int capacity = kCodesStartIndex + number_of_entries + 1; 11255 if (capacity > 5) capacity = capacity * 5 / 4; 11256 Handle<DependentCode> new_entries = Handle<DependentCode>::cast( 11257 factory->CopySizeFixedArray(entries, capacity)); 11258 // The number of codes can change after GC. 11259 starts.Recompute(*entries); 11260 start = starts.at(group); 11261 end = starts.at(group + 1); 11262 number_of_entries = starts.number_of_entries(); 11263 for (int i = 0; i < number_of_entries; i++) { 11264 entries->clear_at(i); 11265 } 11266 // If the old fixed array was empty, we need to reset counters of the 11267 // new array. 11268 if (number_of_entries == 0) { 11269 for (int g = 0; g < kGroupCount; g++) { 11270 new_entries->set_number_of_entries(static_cast<DependencyGroup>(g), 0); 11271 } 11272 } 11273 entries = new_entries; 11274 } 11275 entries->ExtendGroup(group); 11276 entries->set_object_at(end, *object); 11277 entries->set_number_of_entries(group, end + 1 - start); 11278 return entries; 11279 } 11280 11281 11282 void DependentCode::UpdateToFinishedCode(DependencyGroup group, 11283 CompilationInfo* info, 11284 Code* code) { 11285 DisallowHeapAllocation no_gc; 11286 AllowDeferredHandleDereference get_object_wrapper; 11287 Foreign* info_wrapper = *info->object_wrapper(); 11288 GroupStartIndexes starts(this); 11289 int start = starts.at(group); 11290 int end = starts.at(group + 1); 11291 for (int i = start; i < end; i++) { 11292 if (object_at(i) == info_wrapper) { 11293 set_object_at(i, code); 11294 break; 11295 } 11296 } 11297 11298 #ifdef DEBUG 11299 for (int i = start; i < end; i++) { 11300 ASSERT(is_code_at(i) || compilation_info_at(i) != info); 11301 } 11302 #endif 11303 } 11304 11305 11306 void DependentCode::RemoveCompilationInfo(DependentCode::DependencyGroup group, 11307 CompilationInfo* info) { 11308 DisallowHeapAllocation no_allocation; 11309 AllowDeferredHandleDereference get_object_wrapper; 11310 Foreign* info_wrapper = *info->object_wrapper(); 11311 GroupStartIndexes starts(this); 11312 int start = starts.at(group); 11313 int end = starts.at(group + 1); 11314 // Find compilation info wrapper. 11315 int info_pos = -1; 11316 for (int i = start; i < end; i++) { 11317 if (object_at(i) == info_wrapper) { 11318 info_pos = i; 11319 break; 11320 } 11321 } 11322 if (info_pos == -1) return; // Not found. 11323 int gap = info_pos; 11324 // Use the last of each group to fill the gap in the previous group. 11325 for (int i = group; i < kGroupCount; i++) { 11326 int last_of_group = starts.at(i + 1) - 1; 11327 ASSERT(last_of_group >= gap); 11328 if (last_of_group == gap) continue; 11329 copy(last_of_group, gap); 11330 gap = last_of_group; 11331 } 11332 ASSERT(gap == starts.number_of_entries() - 1); 11333 clear_at(gap); // Clear last gap. 11334 set_number_of_entries(group, end - start - 1); 11335 11336 #ifdef DEBUG 11337 for (int i = start; i < end - 1; i++) { 11338 ASSERT(is_code_at(i) || compilation_info_at(i) != info); 11339 } 11340 #endif 11341 } 11342 11343 11344 bool DependentCode::Contains(DependencyGroup group, Code* code) { 11345 GroupStartIndexes starts(this); 11346 int number_of_entries = starts.number_of_entries(); 11347 for (int i = 0; i < number_of_entries; i++) { 11348 if (object_at(i) == code) return true; 11349 } 11350 return false; 11351 } 11352 11353 11354 void DependentCode::DeoptimizeDependentCodeGroup( 11355 Isolate* isolate, 11356 DependentCode::DependencyGroup group) { 11357 ASSERT(AllowCodeDependencyChange::IsAllowed()); 11358 DisallowHeapAllocation no_allocation_scope; 11359 DependentCode::GroupStartIndexes starts(this); 11360 int start = starts.at(group); 11361 int end = starts.at(group + 1); 11362 int code_entries = starts.number_of_entries(); 11363 if (start == end) return; 11364 11365 // Collect all the code to deoptimize. 11366 Zone zone(isolate); 11367 ZoneList<Code*> codes(end - start, &zone); 11368 for (int i = start; i < end; i++) { 11369 if (is_code_at(i)) { 11370 Code* code = code_at(i); 11371 if (!code->marked_for_deoptimization()) codes.Add(code, &zone); 11372 } else { 11373 CompilationInfo* info = compilation_info_at(i); 11374 info->AbortDueToDependencyChange(); 11375 } 11376 } 11377 // Compact the array by moving all subsequent groups to fill in the new holes. 11378 for (int src = end, dst = start; src < code_entries; src++, dst++) { 11379 copy(src, dst); 11380 } 11381 // Now the holes are at the end of the array, zap them for heap-verifier. 11382 int removed = end - start; 11383 for (int i = code_entries - removed; i < code_entries; i++) { 11384 clear_at(i); 11385 } 11386 set_number_of_entries(group, 0); 11387 Deoptimizer::DeoptimizeCodeList(isolate, &codes); 11388 } 11389 11390 11391 Handle<Object> JSObject::SetPrototype(Handle<JSObject> object, 11392 Handle<Object> value, 11393 bool skip_hidden_prototypes) { 11394 #ifdef DEBUG 11395 int size = object->Size(); 11396 #endif 11397 11398 Isolate* isolate = object->GetIsolate(); 11399 Heap* heap = isolate->heap(); 11400 // Silently ignore the change if value is not a JSObject or null. 11401 // SpiderMonkey behaves this way. 11402 if (!value->IsJSReceiver() && !value->IsNull()) return value; 11403 11404 // From 8.6.2 Object Internal Methods 11405 // ... 11406 // In addition, if [[Extensible]] is false the value of the [[Class]] and 11407 // [[Prototype]] internal properties of the object may not be modified. 11408 // ... 11409 // Implementation specific extensions that modify [[Class]], [[Prototype]] 11410 // or [[Extensible]] must not violate the invariants defined in the preceding 11411 // paragraph. 11412 if (!object->map()->is_extensible()) { 11413 Handle<Object> args[] = { object }; 11414 Handle<Object> error = isolate->factory()->NewTypeError( 11415 "non_extensible_proto", HandleVector(args, ARRAY_SIZE(args))); 11416 isolate->Throw(*error); 11417 return Handle<Object>(); 11418 } 11419 11420 // Before we can set the prototype we need to be sure 11421 // prototype cycles are prevented. 11422 // It is sufficient to validate that the receiver is not in the new prototype 11423 // chain. 11424 for (Object* pt = *value; 11425 pt != heap->null_value(); 11426 pt = pt->GetPrototype(isolate)) { 11427 if (JSReceiver::cast(pt) == *object) { 11428 // Cycle detected. 11429 Handle<Object> error = isolate->factory()->NewError( 11430 "cyclic_proto", HandleVector<Object>(NULL, 0)); 11431 isolate->Throw(*error); 11432 return Handle<Object>(); 11433 } 11434 } 11435 11436 Handle<JSObject> real_receiver = object; 11437 11438 if (skip_hidden_prototypes) { 11439 // Find the first object in the chain whose prototype object is not 11440 // hidden and set the new prototype on that object. 11441 Object* current_proto = real_receiver->GetPrototype(); 11442 while (current_proto->IsJSObject() && 11443 JSObject::cast(current_proto)->map()->is_hidden_prototype()) { 11444 real_receiver = handle(JSObject::cast(current_proto), isolate); 11445 current_proto = current_proto->GetPrototype(isolate); 11446 } 11447 } 11448 11449 // Set the new prototype of the object. 11450 Handle<Map> map(real_receiver->map()); 11451 11452 // Nothing to do if prototype is already set. 11453 if (map->prototype() == *value) return value; 11454 11455 if (value->IsJSObject()) { 11456 JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value)); 11457 } 11458 11459 Handle<Map> new_map = Map::GetPrototypeTransition(map, value); 11460 if (new_map.is_null()) { 11461 new_map = Map::Copy(map); 11462 Map::PutPrototypeTransition(map, value, new_map); 11463 new_map->set_prototype(*value); 11464 } 11465 ASSERT(new_map->prototype() == *value); 11466 real_receiver->set_map(*new_map); 11467 11468 heap->ClearInstanceofCache(); 11469 ASSERT(size == object->Size()); 11470 return value; 11471 } 11472 11473 11474 MaybeObject* JSObject::EnsureCanContainElements(Arguments* args, 11475 uint32_t first_arg, 11476 uint32_t arg_count, 11477 EnsureElementsMode mode) { 11478 // Elements in |Arguments| are ordered backwards (because they're on the 11479 // stack), but the method that's called here iterates over them in forward 11480 // direction. 11481 return EnsureCanContainElements( 11482 args->arguments() - first_arg - (arg_count - 1), 11483 arg_count, mode); 11484 } 11485 11486 11487 PropertyType JSObject::GetLocalPropertyType(Name* name) { 11488 uint32_t index = 0; 11489 if (name->AsArrayIndex(&index)) { 11490 return GetLocalElementType(index); 11491 } 11492 LookupResult lookup(GetIsolate()); 11493 LocalLookup(name, &lookup, true); 11494 return lookup.type(); 11495 } 11496 11497 11498 PropertyType JSObject::GetLocalElementType(uint32_t index) { 11499 return GetElementsAccessor()->GetType(this, this, index); 11500 } 11501 11502 11503 AccessorPair* JSObject::GetLocalPropertyAccessorPair(Name* name) { 11504 uint32_t index = 0; 11505 if (name->AsArrayIndex(&index)) { 11506 return GetLocalElementAccessorPair(index); 11507 } 11508 11509 LookupResult lookup(GetIsolate()); 11510 LocalLookupRealNamedProperty(name, &lookup); 11511 11512 if (lookup.IsPropertyCallbacks() && 11513 lookup.GetCallbackObject()->IsAccessorPair()) { 11514 return AccessorPair::cast(lookup.GetCallbackObject()); 11515 } 11516 return NULL; 11517 } 11518 11519 11520 AccessorPair* JSObject::GetLocalElementAccessorPair(uint32_t index) { 11521 if (IsJSGlobalProxy()) { 11522 Object* proto = GetPrototype(); 11523 if (proto->IsNull()) return NULL; 11524 ASSERT(proto->IsJSGlobalObject()); 11525 return JSObject::cast(proto)->GetLocalElementAccessorPair(index); 11526 } 11527 11528 // Check for lookup interceptor. 11529 if (HasIndexedInterceptor()) return NULL; 11530 11531 return GetElementsAccessor()->GetAccessorPair(this, this, index); 11532 } 11533 11534 11535 MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index, 11536 Object* value, 11537 PropertyAttributes attributes, 11538 StrictModeFlag strict_mode, 11539 bool check_prototype, 11540 SetPropertyMode set_mode) { 11541 Isolate* isolate = GetIsolate(); 11542 // Make sure that the top context does not change when doing 11543 // callbacks or interceptor calls. 11544 AssertNoContextChange ncc; 11545 HandleScope scope(isolate); 11546 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 11547 Handle<JSObject> this_handle(this); 11548 Handle<Object> value_handle(value, isolate); 11549 if (!interceptor->setter()->IsUndefined()) { 11550 v8::IndexedPropertySetter setter = 11551 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter()); 11552 LOG(isolate, 11553 ApiIndexedPropertyAccess("interceptor-indexed-set", this, index)); 11554 PropertyCallbackArguments args(isolate, interceptor->data(), this, this); 11555 v8::Handle<v8::Value> result = 11556 args.Call(setter, index, v8::Utils::ToLocal(value_handle)); 11557 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 11558 if (!result.IsEmpty()) return *value_handle; 11559 } 11560 MaybeObject* raw_result = 11561 this_handle->SetElementWithoutInterceptor(index, 11562 *value_handle, 11563 attributes, 11564 strict_mode, 11565 check_prototype, 11566 set_mode); 11567 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 11568 return raw_result; 11569 } 11570 11571 11572 MaybeObject* JSObject::GetElementWithCallback(Object* receiver, 11573 Object* structure, 11574 uint32_t index, 11575 Object* holder) { 11576 Isolate* isolate = GetIsolate(); 11577 ASSERT(!structure->IsForeign()); 11578 11579 // api style callbacks. 11580 if (structure->IsExecutableAccessorInfo()) { 11581 Handle<ExecutableAccessorInfo> data( 11582 ExecutableAccessorInfo::cast(structure)); 11583 Object* fun_obj = data->getter(); 11584 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); 11585 if (call_fun == NULL) return isolate->heap()->undefined_value(); 11586 HandleScope scope(isolate); 11587 Handle<JSObject> self(JSObject::cast(receiver)); 11588 Handle<JSObject> holder_handle(JSObject::cast(holder)); 11589 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 11590 Handle<String> key = isolate->factory()->NumberToString(number); 11591 LOG(isolate, ApiNamedPropertyAccess("load", *self, *key)); 11592 PropertyCallbackArguments 11593 args(isolate, data->data(), *self, *holder_handle); 11594 v8::Handle<v8::Value> result = args.Call(call_fun, v8::Utils::ToLocal(key)); 11595 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 11596 if (result.IsEmpty()) return isolate->heap()->undefined_value(); 11597 Handle<Object> result_internal = v8::Utils::OpenHandle(*result); 11598 result_internal->VerifyApiCallResultType(); 11599 return *result_internal; 11600 } 11601 11602 // __defineGetter__ callback 11603 if (structure->IsAccessorPair()) { 11604 Object* getter = AccessorPair::cast(structure)->getter(); 11605 if (getter->IsSpecFunction()) { 11606 // TODO(rossberg): nicer would be to cast to some JSCallable here... 11607 return GetPropertyWithDefinedGetter(receiver, JSReceiver::cast(getter)); 11608 } 11609 // Getter is not a function. 11610 return isolate->heap()->undefined_value(); 11611 } 11612 11613 if (structure->IsDeclaredAccessorInfo()) { 11614 return GetDeclaredAccessorProperty(receiver, 11615 DeclaredAccessorInfo::cast(structure), 11616 isolate); 11617 } 11618 11619 UNREACHABLE(); 11620 return NULL; 11621 } 11622 11623 11624 MaybeObject* JSObject::SetElementWithCallback(Object* structure, 11625 uint32_t index, 11626 Object* value, 11627 JSObject* holder, 11628 StrictModeFlag strict_mode) { 11629 Isolate* isolate = GetIsolate(); 11630 HandleScope scope(isolate); 11631 11632 // We should never get here to initialize a const with the hole 11633 // value since a const declaration would conflict with the setter. 11634 ASSERT(!value->IsTheHole()); 11635 Handle<Object> value_handle(value, isolate); 11636 11637 // To accommodate both the old and the new api we switch on the 11638 // data structure used to store the callbacks. Eventually foreign 11639 // callbacks should be phased out. 11640 ASSERT(!structure->IsForeign()); 11641 11642 if (structure->IsExecutableAccessorInfo()) { 11643 // api style callbacks 11644 Handle<JSObject> self(this); 11645 Handle<JSObject> holder_handle(JSObject::cast(holder)); 11646 Handle<ExecutableAccessorInfo> data( 11647 ExecutableAccessorInfo::cast(structure)); 11648 Object* call_obj = data->setter(); 11649 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj); 11650 if (call_fun == NULL) return value; 11651 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 11652 Handle<String> key(isolate->factory()->NumberToString(number)); 11653 LOG(isolate, ApiNamedPropertyAccess("store", *self, *key)); 11654 PropertyCallbackArguments 11655 args(isolate, data->data(), *self, *holder_handle); 11656 args.Call(call_fun, 11657 v8::Utils::ToLocal(key), 11658 v8::Utils::ToLocal(value_handle)); 11659 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 11660 return *value_handle; 11661 } 11662 11663 if (structure->IsAccessorPair()) { 11664 Handle<Object> setter(AccessorPair::cast(structure)->setter(), isolate); 11665 if (setter->IsSpecFunction()) { 11666 // TODO(rossberg): nicer would be to cast to some JSCallable here... 11667 return SetPropertyWithDefinedSetter(JSReceiver::cast(*setter), value); 11668 } else { 11669 if (strict_mode == kNonStrictMode) { 11670 return value; 11671 } 11672 Handle<Object> holder_handle(holder, isolate); 11673 Handle<Object> key(isolate->factory()->NewNumberFromUint(index)); 11674 Handle<Object> args[2] = { key, holder_handle }; 11675 return isolate->Throw( 11676 *isolate->factory()->NewTypeError("no_setter_in_callback", 11677 HandleVector(args, 2))); 11678 } 11679 } 11680 11681 // TODO(dcarney): Handle correctly. 11682 if (structure->IsDeclaredAccessorInfo()) return value; 11683 11684 UNREACHABLE(); 11685 return NULL; 11686 } 11687 11688 11689 bool JSObject::HasFastArgumentsElements() { 11690 Heap* heap = GetHeap(); 11691 if (!elements()->IsFixedArray()) return false; 11692 FixedArray* elements = FixedArray::cast(this->elements()); 11693 if (elements->map() != heap->non_strict_arguments_elements_map()) { 11694 return false; 11695 } 11696 FixedArray* arguments = FixedArray::cast(elements->get(1)); 11697 return !arguments->IsDictionary(); 11698 } 11699 11700 11701 bool JSObject::HasDictionaryArgumentsElements() { 11702 Heap* heap = GetHeap(); 11703 if (!elements()->IsFixedArray()) return false; 11704 FixedArray* elements = FixedArray::cast(this->elements()); 11705 if (elements->map() != heap->non_strict_arguments_elements_map()) { 11706 return false; 11707 } 11708 FixedArray* arguments = FixedArray::cast(elements->get(1)); 11709 return arguments->IsDictionary(); 11710 } 11711 11712 11713 // Adding n elements in fast case is O(n*n). 11714 // Note: revisit design to have dual undefined values to capture absent 11715 // elements. 11716 MaybeObject* JSObject::SetFastElement(uint32_t index, 11717 Object* value, 11718 StrictModeFlag strict_mode, 11719 bool check_prototype) { 11720 ASSERT(HasFastSmiOrObjectElements() || 11721 HasFastArgumentsElements()); 11722 11723 // Array optimizations rely on the prototype lookups of Array objects always 11724 // returning undefined. If there is a store to the initial prototype object, 11725 // make sure all of these optimizations are invalidated. 11726 Isolate* isolate(GetIsolate()); 11727 if (isolate->is_initial_object_prototype(this) || 11728 isolate->is_initial_array_prototype(this)) { 11729 HandleScope scope(GetIsolate()); 11730 map()->dependent_code()->DeoptimizeDependentCodeGroup( 11731 GetIsolate(), 11732 DependentCode::kElementsCantBeAddedGroup); 11733 } 11734 11735 FixedArray* backing_store = FixedArray::cast(elements()); 11736 if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) { 11737 backing_store = FixedArray::cast(backing_store->get(1)); 11738 } else { 11739 MaybeObject* maybe = EnsureWritableFastElements(); 11740 if (!maybe->To(&backing_store)) return maybe; 11741 } 11742 uint32_t capacity = static_cast<uint32_t>(backing_store->length()); 11743 11744 if (check_prototype && 11745 (index >= capacity || backing_store->get(index)->IsTheHole())) { 11746 bool found; 11747 MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index, 11748 value, 11749 &found, 11750 strict_mode); 11751 if (found) return result; 11752 } 11753 11754 uint32_t new_capacity = capacity; 11755 // Check if the length property of this object needs to be updated. 11756 uint32_t array_length = 0; 11757 bool must_update_array_length = false; 11758 bool introduces_holes = true; 11759 if (IsJSArray()) { 11760 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); 11761 introduces_holes = index > array_length; 11762 if (index >= array_length) { 11763 must_update_array_length = true; 11764 array_length = index + 1; 11765 } 11766 } else { 11767 introduces_holes = index >= capacity; 11768 } 11769 11770 // If the array is growing, and it's not growth by a single element at the 11771 // end, make sure that the ElementsKind is HOLEY. 11772 ElementsKind elements_kind = GetElementsKind(); 11773 if (introduces_holes && 11774 IsFastElementsKind(elements_kind) && 11775 !IsFastHoleyElementsKind(elements_kind)) { 11776 ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind); 11777 MaybeObject* maybe = TransitionElementsKind(transitioned_kind); 11778 if (maybe->IsFailure()) return maybe; 11779 } 11780 11781 // Check if the capacity of the backing store needs to be increased, or if 11782 // a transition to slow elements is necessary. 11783 if (index >= capacity) { 11784 bool convert_to_slow = true; 11785 if ((index - capacity) < kMaxGap) { 11786 new_capacity = NewElementsCapacity(index + 1); 11787 ASSERT(new_capacity > index); 11788 if (!ShouldConvertToSlowElements(new_capacity)) { 11789 convert_to_slow = false; 11790 } 11791 } 11792 if (convert_to_slow) { 11793 MaybeObject* result = NormalizeElements(); 11794 if (result->IsFailure()) return result; 11795 return SetDictionaryElement(index, value, NONE, strict_mode, 11796 check_prototype); 11797 } 11798 } 11799 // Convert to fast double elements if appropriate. 11800 if (HasFastSmiElements() && !value->IsSmi() && value->IsNumber()) { 11801 // Consider fixing the boilerplate as well if we have one. 11802 ElementsKind to_kind = IsHoleyElementsKind(elements_kind) 11803 ? FAST_HOLEY_DOUBLE_ELEMENTS 11804 : FAST_DOUBLE_ELEMENTS; 11805 11806 MaybeObject* maybe_failure = UpdateAllocationSite(to_kind); 11807 if (maybe_failure->IsFailure()) return maybe_failure; 11808 11809 MaybeObject* maybe = 11810 SetFastDoubleElementsCapacityAndLength(new_capacity, array_length); 11811 if (maybe->IsFailure()) return maybe; 11812 FixedDoubleArray::cast(elements())->set(index, value->Number()); 11813 ValidateElements(); 11814 return value; 11815 } 11816 // Change elements kind from Smi-only to generic FAST if necessary. 11817 if (HasFastSmiElements() && !value->IsSmi()) { 11818 Map* new_map; 11819 ElementsKind kind = HasFastHoleyElements() 11820 ? FAST_HOLEY_ELEMENTS 11821 : FAST_ELEMENTS; 11822 11823 MaybeObject* maybe_failure = UpdateAllocationSite(kind); 11824 if (maybe_failure->IsFailure()) return maybe_failure; 11825 11826 MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(), 11827 kind); 11828 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 11829 11830 set_map(new_map); 11831 } 11832 // Increase backing store capacity if that's been decided previously. 11833 if (new_capacity != capacity) { 11834 FixedArray* new_elements; 11835 SetFastElementsCapacitySmiMode smi_mode = 11836 value->IsSmi() && HasFastSmiElements() 11837 ? kAllowSmiElements 11838 : kDontAllowSmiElements; 11839 { MaybeObject* maybe = 11840 SetFastElementsCapacityAndLength(new_capacity, 11841 array_length, 11842 smi_mode); 11843 if (!maybe->To(&new_elements)) return maybe; 11844 } 11845 new_elements->set(index, value); 11846 ValidateElements(); 11847 return value; 11848 } 11849 11850 // Finally, set the new element and length. 11851 ASSERT(elements()->IsFixedArray()); 11852 backing_store->set(index, value); 11853 if (must_update_array_length) { 11854 JSArray::cast(this)->set_length(Smi::FromInt(array_length)); 11855 } 11856 return value; 11857 } 11858 11859 11860 MaybeObject* JSObject::SetDictionaryElement(uint32_t index, 11861 Object* value_raw, 11862 PropertyAttributes attributes, 11863 StrictModeFlag strict_mode, 11864 bool check_prototype, 11865 SetPropertyMode set_mode) { 11866 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 11867 Isolate* isolate = GetIsolate(); 11868 Heap* heap = isolate->heap(); 11869 Handle<JSObject> self(this); 11870 Handle<Object> value(value_raw, isolate); 11871 11872 // Insert element in the dictionary. 11873 Handle<FixedArray> elements(FixedArray::cast(this->elements())); 11874 bool is_arguments = 11875 (elements->map() == heap->non_strict_arguments_elements_map()); 11876 Handle<SeededNumberDictionary> dictionary(is_arguments 11877 ? SeededNumberDictionary::cast(elements->get(1)) 11878 : SeededNumberDictionary::cast(*elements)); 11879 11880 int entry = dictionary->FindEntry(index); 11881 if (entry != SeededNumberDictionary::kNotFound) { 11882 Object* element = dictionary->ValueAt(entry); 11883 PropertyDetails details = dictionary->DetailsAt(entry); 11884 if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) { 11885 return SetElementWithCallback(element, index, *value, this, strict_mode); 11886 } else { 11887 dictionary->UpdateMaxNumberKey(index); 11888 // If a value has not been initialized we allow writing to it even if it 11889 // is read-only (a declared const that has not been initialized). If a 11890 // value is being defined we skip attribute checks completely. 11891 if (set_mode == DEFINE_PROPERTY) { 11892 details = PropertyDetails( 11893 attributes, NORMAL, details.dictionary_index()); 11894 dictionary->DetailsAtPut(entry, details); 11895 } else if (details.IsReadOnly() && !element->IsTheHole()) { 11896 if (strict_mode == kNonStrictMode) { 11897 return isolate->heap()->undefined_value(); 11898 } else { 11899 Handle<Object> holder(this, isolate); 11900 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 11901 Handle<Object> args[2] = { number, holder }; 11902 Handle<Object> error = 11903 isolate->factory()->NewTypeError("strict_read_only_property", 11904 HandleVector(args, 2)); 11905 return isolate->Throw(*error); 11906 } 11907 } 11908 // Elements of the arguments object in slow mode might be slow aliases. 11909 if (is_arguments && element->IsAliasedArgumentsEntry()) { 11910 AliasedArgumentsEntry* entry = AliasedArgumentsEntry::cast(element); 11911 Context* context = Context::cast(elements->get(0)); 11912 int context_index = entry->aliased_context_slot(); 11913 ASSERT(!context->get(context_index)->IsTheHole()); 11914 context->set(context_index, *value); 11915 // For elements that are still writable we keep slow aliasing. 11916 if (!details.IsReadOnly()) value = handle(element, isolate); 11917 } 11918 dictionary->ValueAtPut(entry, *value); 11919 } 11920 } else { 11921 // Index not already used. Look for an accessor in the prototype chain. 11922 // Can cause GC! 11923 if (check_prototype) { 11924 bool found; 11925 MaybeObject* result = SetElementWithCallbackSetterInPrototypes( 11926 index, *value, &found, strict_mode); 11927 if (found) return result; 11928 } 11929 // When we set the is_extensible flag to false we always force the 11930 // element into dictionary mode (and force them to stay there). 11931 if (!self->map()->is_extensible()) { 11932 if (strict_mode == kNonStrictMode) { 11933 return isolate->heap()->undefined_value(); 11934 } else { 11935 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 11936 Handle<String> name = isolate->factory()->NumberToString(number); 11937 Handle<Object> args[1] = { name }; 11938 Handle<Object> error = 11939 isolate->factory()->NewTypeError("object_not_extensible", 11940 HandleVector(args, 1)); 11941 return isolate->Throw(*error); 11942 } 11943 } 11944 FixedArrayBase* new_dictionary; 11945 PropertyDetails details = PropertyDetails(attributes, NORMAL, 0); 11946 MaybeObject* maybe = dictionary->AddNumberEntry(index, *value, details); 11947 if (!maybe->To(&new_dictionary)) return maybe; 11948 if (*dictionary != SeededNumberDictionary::cast(new_dictionary)) { 11949 if (is_arguments) { 11950 elements->set(1, new_dictionary); 11951 } else { 11952 self->set_elements(new_dictionary); 11953 } 11954 dictionary = 11955 handle(SeededNumberDictionary::cast(new_dictionary), isolate); 11956 } 11957 } 11958 11959 // Update the array length if this JSObject is an array. 11960 if (self->IsJSArray()) { 11961 MaybeObject* result = 11962 JSArray::cast(*self)->JSArrayUpdateLengthFromIndex(index, *value); 11963 if (result->IsFailure()) return result; 11964 } 11965 11966 // Attempt to put this object back in fast case. 11967 if (self->ShouldConvertToFastElements()) { 11968 uint32_t new_length = 0; 11969 if (self->IsJSArray()) { 11970 CHECK(JSArray::cast(*self)->length()->ToArrayIndex(&new_length)); 11971 } else { 11972 new_length = dictionary->max_number_key() + 1; 11973 } 11974 SetFastElementsCapacitySmiMode smi_mode = FLAG_smi_only_arrays 11975 ? kAllowSmiElements 11976 : kDontAllowSmiElements; 11977 bool has_smi_only_elements = false; 11978 bool should_convert_to_fast_double_elements = 11979 self->ShouldConvertToFastDoubleElements(&has_smi_only_elements); 11980 if (has_smi_only_elements) { 11981 smi_mode = kForceSmiElements; 11982 } 11983 MaybeObject* result = should_convert_to_fast_double_elements 11984 ? self->SetFastDoubleElementsCapacityAndLength(new_length, new_length) 11985 : self->SetFastElementsCapacityAndLength( 11986 new_length, new_length, smi_mode); 11987 self->ValidateElements(); 11988 if (result->IsFailure()) return result; 11989 #ifdef DEBUG 11990 if (FLAG_trace_normalization) { 11991 PrintF("Object elements are fast case again:\n"); 11992 Print(); 11993 } 11994 #endif 11995 } 11996 return *value; 11997 } 11998 11999 12000 MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( 12001 uint32_t index, 12002 Object* value, 12003 StrictModeFlag strict_mode, 12004 bool check_prototype) { 12005 ASSERT(HasFastDoubleElements()); 12006 12007 FixedArrayBase* base_elms = FixedArrayBase::cast(elements()); 12008 uint32_t elms_length = static_cast<uint32_t>(base_elms->length()); 12009 12010 // If storing to an element that isn't in the array, pass the store request 12011 // up the prototype chain before storing in the receiver's elements. 12012 if (check_prototype && 12013 (index >= elms_length || 12014 FixedDoubleArray::cast(base_elms)->is_the_hole(index))) { 12015 bool found; 12016 MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index, 12017 value, 12018 &found, 12019 strict_mode); 12020 if (found) return result; 12021 } 12022 12023 // If the value object is not a heap number, switch to fast elements and try 12024 // again. 12025 bool value_is_smi = value->IsSmi(); 12026 bool introduces_holes = true; 12027 uint32_t length = elms_length; 12028 if (IsJSArray()) { 12029 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); 12030 introduces_holes = index > length; 12031 } else { 12032 introduces_holes = index >= elms_length; 12033 } 12034 12035 if (!value->IsNumber()) { 12036 MaybeObject* maybe_obj = SetFastElementsCapacityAndLength( 12037 elms_length, 12038 length, 12039 kDontAllowSmiElements); 12040 if (maybe_obj->IsFailure()) return maybe_obj; 12041 maybe_obj = SetFastElement(index, value, strict_mode, check_prototype); 12042 if (maybe_obj->IsFailure()) return maybe_obj; 12043 ValidateElements(); 12044 return maybe_obj; 12045 } 12046 12047 double double_value = value_is_smi 12048 ? static_cast<double>(Smi::cast(value)->value()) 12049 : HeapNumber::cast(value)->value(); 12050 12051 // If the array is growing, and it's not growth by a single element at the 12052 // end, make sure that the ElementsKind is HOLEY. 12053 ElementsKind elements_kind = GetElementsKind(); 12054 if (introduces_holes && !IsFastHoleyElementsKind(elements_kind)) { 12055 ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind); 12056 MaybeObject* maybe = TransitionElementsKind(transitioned_kind); 12057 if (maybe->IsFailure()) return maybe; 12058 } 12059 12060 // Check whether there is extra space in the fixed array. 12061 if (index < elms_length) { 12062 FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); 12063 elms->set(index, double_value); 12064 if (IsJSArray()) { 12065 // Update the length of the array if needed. 12066 uint32_t array_length = 0; 12067 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); 12068 if (index >= array_length) { 12069 JSArray::cast(this)->set_length(Smi::FromInt(index + 1)); 12070 } 12071 } 12072 return value; 12073 } 12074 12075 // Allow gap in fast case. 12076 if ((index - elms_length) < kMaxGap) { 12077 // Try allocating extra space. 12078 int new_capacity = NewElementsCapacity(index+1); 12079 if (!ShouldConvertToSlowElements(new_capacity)) { 12080 ASSERT(static_cast<uint32_t>(new_capacity) > index); 12081 MaybeObject* maybe_obj = 12082 SetFastDoubleElementsCapacityAndLength(new_capacity, index + 1); 12083 if (maybe_obj->IsFailure()) return maybe_obj; 12084 FixedDoubleArray::cast(elements())->set(index, double_value); 12085 ValidateElements(); 12086 return value; 12087 } 12088 } 12089 12090 // Otherwise default to slow case. 12091 ASSERT(HasFastDoubleElements()); 12092 ASSERT(map()->has_fast_double_elements()); 12093 ASSERT(elements()->IsFixedDoubleArray()); 12094 Object* obj; 12095 { MaybeObject* maybe_obj = NormalizeElements(); 12096 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 12097 } 12098 ASSERT(HasDictionaryElements()); 12099 return SetElement(index, value, NONE, strict_mode, check_prototype); 12100 } 12101 12102 12103 MaybeObject* JSReceiver::SetElement(uint32_t index, 12104 Object* value, 12105 PropertyAttributes attributes, 12106 StrictModeFlag strict_mode, 12107 bool check_proto) { 12108 if (IsJSProxy()) { 12109 return JSProxy::cast(this)->SetElementWithHandler( 12110 this, index, value, strict_mode); 12111 } else { 12112 return JSObject::cast(this)->SetElement( 12113 index, value, attributes, strict_mode, check_proto); 12114 } 12115 } 12116 12117 12118 Handle<Object> JSObject::SetOwnElement(Handle<JSObject> object, 12119 uint32_t index, 12120 Handle<Object> value, 12121 StrictModeFlag strict_mode) { 12122 ASSERT(!object->HasExternalArrayElements()); 12123 CALL_HEAP_FUNCTION( 12124 object->GetIsolate(), 12125 object->SetElement(index, *value, NONE, strict_mode, false), 12126 Object); 12127 } 12128 12129 12130 Handle<Object> JSObject::SetElement(Handle<JSObject> object, 12131 uint32_t index, 12132 Handle<Object> value, 12133 PropertyAttributes attr, 12134 StrictModeFlag strict_mode, 12135 SetPropertyMode set_mode) { 12136 if (object->HasExternalArrayElements()) { 12137 if (!value->IsNumber() && !value->IsUndefined()) { 12138 bool has_exception; 12139 Handle<Object> number = Execution::ToNumber(value, &has_exception); 12140 if (has_exception) return Handle<Object>(); 12141 value = number; 12142 } 12143 } 12144 CALL_HEAP_FUNCTION( 12145 object->GetIsolate(), 12146 object->SetElement(index, *value, attr, strict_mode, true, set_mode), 12147 Object); 12148 } 12149 12150 12151 MaybeObject* JSObject::SetElement(uint32_t index, 12152 Object* value_raw, 12153 PropertyAttributes attributes, 12154 StrictModeFlag strict_mode, 12155 bool check_prototype, 12156 SetPropertyMode set_mode) { 12157 Isolate* isolate = GetIsolate(); 12158 12159 // Check access rights if needed. 12160 if (IsAccessCheckNeeded()) { 12161 if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_SET)) { 12162 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); 12163 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 12164 return value_raw; 12165 } 12166 } 12167 12168 if (IsJSGlobalProxy()) { 12169 Object* proto = GetPrototype(); 12170 if (proto->IsNull()) return value_raw; 12171 ASSERT(proto->IsJSGlobalObject()); 12172 return JSObject::cast(proto)->SetElement(index, 12173 value_raw, 12174 attributes, 12175 strict_mode, 12176 check_prototype, 12177 set_mode); 12178 } 12179 12180 // Don't allow element properties to be redefined for external arrays. 12181 if (HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) { 12182 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 12183 Handle<Object> args[] = { handle(this, isolate), number }; 12184 Handle<Object> error = isolate->factory()->NewTypeError( 12185 "redef_external_array_element", HandleVector(args, ARRAY_SIZE(args))); 12186 return isolate->Throw(*error); 12187 } 12188 12189 // Normalize the elements to enable attributes on the property. 12190 if ((attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) { 12191 SeededNumberDictionary* dictionary; 12192 MaybeObject* maybe_object = NormalizeElements(); 12193 if (!maybe_object->To(&dictionary)) return maybe_object; 12194 // Make sure that we never go back to fast case. 12195 dictionary->set_requires_slow_elements(); 12196 } 12197 12198 if (!(FLAG_harmony_observation && map()->is_observed())) { 12199 return HasIndexedInterceptor() 12200 ? SetElementWithInterceptor( 12201 index, value_raw, attributes, strict_mode, check_prototype, set_mode) 12202 : SetElementWithoutInterceptor( 12203 index, value_raw, attributes, strict_mode, check_prototype, set_mode); 12204 } 12205 12206 // From here on, everything has to be handlified. 12207 Handle<JSObject> self(this); 12208 Handle<Object> value(value_raw, isolate); 12209 PropertyAttributes old_attributes = self->GetLocalElementAttribute(index); 12210 Handle<Object> old_value = isolate->factory()->the_hole_value(); 12211 Handle<Object> old_length_handle; 12212 Handle<Object> new_length_handle; 12213 12214 if (old_attributes != ABSENT) { 12215 if (self->GetLocalElementAccessorPair(index) == NULL) 12216 old_value = Object::GetElement(self, index); 12217 } else if (self->IsJSArray()) { 12218 // Store old array length in case adding an element grows the array. 12219 old_length_handle = handle(Handle<JSArray>::cast(self)->length(), isolate); 12220 } 12221 12222 // Check for lookup interceptor 12223 MaybeObject* result = self->HasIndexedInterceptor() 12224 ? self->SetElementWithInterceptor( 12225 index, *value, attributes, strict_mode, check_prototype, set_mode) 12226 : self->SetElementWithoutInterceptor( 12227 index, *value, attributes, strict_mode, check_prototype, set_mode); 12228 12229 Handle<Object> hresult; 12230 if (!result->ToHandle(&hresult, isolate)) return result; 12231 12232 Handle<String> name = isolate->factory()->Uint32ToString(index); 12233 PropertyAttributes new_attributes = self->GetLocalElementAttribute(index); 12234 if (old_attributes == ABSENT) { 12235 if (self->IsJSArray() && 12236 !old_length_handle->SameValue(Handle<JSArray>::cast(self)->length())) { 12237 new_length_handle = handle(Handle<JSArray>::cast(self)->length(), 12238 isolate); 12239 uint32_t old_length = 0; 12240 uint32_t new_length = 0; 12241 CHECK(old_length_handle->ToArrayIndex(&old_length)); 12242 CHECK(new_length_handle->ToArrayIndex(&new_length)); 12243 12244 BeginPerformSplice(Handle<JSArray>::cast(self)); 12245 EnqueueChangeRecord(self, "new", name, old_value); 12246 EnqueueChangeRecord(self, "updated", isolate->factory()->length_string(), 12247 old_length_handle); 12248 EndPerformSplice(Handle<JSArray>::cast(self)); 12249 Handle<JSArray> deleted = isolate->factory()->NewJSArray(0); 12250 EnqueueSpliceRecord(Handle<JSArray>::cast(self), old_length, deleted, 12251 new_length - old_length); 12252 } else { 12253 EnqueueChangeRecord(self, "new", name, old_value); 12254 } 12255 } else if (old_value->IsTheHole()) { 12256 EnqueueChangeRecord(self, "reconfigured", name, old_value); 12257 } else { 12258 Handle<Object> new_value = Object::GetElement(self, index); 12259 bool value_changed = !old_value->SameValue(*new_value); 12260 if (old_attributes != new_attributes) { 12261 if (!value_changed) old_value = isolate->factory()->the_hole_value(); 12262 EnqueueChangeRecord(self, "reconfigured", name, old_value); 12263 } else if (value_changed) { 12264 EnqueueChangeRecord(self, "updated", name, old_value); 12265 } 12266 } 12267 12268 return *hresult; 12269 } 12270 12271 12272 MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, 12273 Object* value, 12274 PropertyAttributes attr, 12275 StrictModeFlag strict_mode, 12276 bool check_prototype, 12277 SetPropertyMode set_mode) { 12278 ASSERT(HasDictionaryElements() || 12279 HasDictionaryArgumentsElements() || 12280 (attr & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0); 12281 Isolate* isolate = GetIsolate(); 12282 if (FLAG_trace_external_array_abuse && 12283 IsExternalArrayElementsKind(GetElementsKind())) { 12284 CheckArrayAbuse(this, "external elements write", index); 12285 } 12286 if (FLAG_trace_js_array_abuse && 12287 !IsExternalArrayElementsKind(GetElementsKind())) { 12288 if (IsJSArray()) { 12289 CheckArrayAbuse(this, "elements write", index, true); 12290 } 12291 } 12292 switch (GetElementsKind()) { 12293 case FAST_SMI_ELEMENTS: 12294 case FAST_ELEMENTS: 12295 case FAST_HOLEY_SMI_ELEMENTS: 12296 case FAST_HOLEY_ELEMENTS: 12297 return SetFastElement(index, value, strict_mode, check_prototype); 12298 case FAST_DOUBLE_ELEMENTS: 12299 case FAST_HOLEY_DOUBLE_ELEMENTS: 12300 return SetFastDoubleElement(index, value, strict_mode, check_prototype); 12301 case EXTERNAL_PIXEL_ELEMENTS: { 12302 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); 12303 return pixels->SetValue(index, value); 12304 } 12305 case