1// Copyright 2006-2009 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 "api.h" 31#include "arguments.h" 32#include "bootstrapper.h" 33#include "debug.h" 34#include "execution.h" 35#include "objects-inl.h" 36#include "macro-assembler.h" 37#include "scanner.h" 38#include "scopeinfo.h" 39#include "string-stream.h" 40#include "utils.h" 41 42#ifdef ENABLE_DISASSEMBLER 43#include "disassembler.h" 44#endif 45 46 47namespace v8 { 48namespace internal { 49 50// Getters and setters are stored in a fixed array property. These are 51// constants for their indices. 52const int kGetterIndex = 0; 53const int kSetterIndex = 1; 54 55 56static Object* CreateJSValue(JSFunction* constructor, Object* value) { 57 Object* result = Heap::AllocateJSObject(constructor); 58 if (result->IsFailure()) return result; 59 JSValue::cast(result)->set_value(value); 60 return result; 61} 62 63 64Object* Object::ToObject(Context* global_context) { 65 if (IsNumber()) { 66 return CreateJSValue(global_context->number_function(), this); 67 } else if (IsBoolean()) { 68 return CreateJSValue(global_context->boolean_function(), this); 69 } else if (IsString()) { 70 return CreateJSValue(global_context->string_function(), this); 71 } 72 ASSERT(IsJSObject()); 73 return this; 74} 75 76 77Object* Object::ToObject() { 78 Context* global_context = Top::context()->global_context(); 79 if (IsJSObject()) { 80 return this; 81 } else if (IsNumber()) { 82 return CreateJSValue(global_context->number_function(), this); 83 } else if (IsBoolean()) { 84 return CreateJSValue(global_context->boolean_function(), this); 85 } else if (IsString()) { 86 return CreateJSValue(global_context->string_function(), this); 87 } 88 89 // Throw a type error. 90 return Failure::InternalError(); 91} 92 93 94Object* Object::ToBoolean() { 95 if (IsTrue()) return Heap::true_value(); 96 if (IsFalse()) return Heap::false_value(); 97 if (IsSmi()) { 98 return Heap::ToBoolean(Smi::cast(this)->value() != 0); 99 } 100 if (IsUndefined() || IsNull()) return Heap::false_value(); 101 // Undetectable object is false 102 if (IsUndetectableObject()) { 103 return Heap::false_value(); 104 } 105 if (IsString()) { 106 return Heap::ToBoolean(String::cast(this)->length() != 0); 107 } 108 if (IsHeapNumber()) { 109 return HeapNumber::cast(this)->HeapNumberToBoolean(); 110 } 111 return Heap::true_value(); 112} 113 114 115void Object::Lookup(String* name, LookupResult* result) { 116 if (IsJSObject()) return JSObject::cast(this)->Lookup(name, result); 117 Object* holder = NULL; 118 Context* global_context = Top::context()->global_context(); 119 if (IsString()) { 120 holder = global_context->string_function()->instance_prototype(); 121 } else if (IsNumber()) { 122 holder = global_context->number_function()->instance_prototype(); 123 } else if (IsBoolean()) { 124 holder = global_context->boolean_function()->instance_prototype(); 125 } 126 ASSERT(holder != NULL); // Cannot handle null or undefined. 127 JSObject::cast(holder)->Lookup(name, result); 128} 129 130 131Object* Object::GetPropertyWithReceiver(Object* receiver, 132 String* name, 133 PropertyAttributes* attributes) { 134 LookupResult result; 135 Lookup(name, &result); 136 Object* value = GetProperty(receiver, &result, name, attributes); 137 ASSERT(*attributes <= ABSENT); 138 return value; 139} 140 141 142Object* Object::GetPropertyWithCallback(Object* receiver, 143 Object* structure, 144 String* name, 145 Object* holder) { 146 // To accommodate both the old and the new api we switch on the 147 // data structure used to store the callbacks. Eventually proxy 148 // callbacks should be phased out. 149 if (structure->IsProxy()) { 150 AccessorDescriptor* callback = 151 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy()); 152 Object* value = (callback->getter)(receiver, callback->data); 153 RETURN_IF_SCHEDULED_EXCEPTION(); 154 return value; 155 } 156 157 // api style callbacks. 158 if (structure->IsAccessorInfo()) { 159 AccessorInfo* data = AccessorInfo::cast(structure); 160 Object* fun_obj = data->getter(); 161 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); 162 HandleScope scope; 163 JSObject* self = JSObject::cast(receiver); 164 JSObject* holder_handle = JSObject::cast(holder); 165 Handle<String> key(name); 166 LOG(ApiNamedPropertyAccess("load", self, name)); 167 CustomArguments args(data->data(), self, holder_handle); 168 v8::AccessorInfo info(args.end()); 169 v8::Handle<v8::Value> result; 170 { 171 // Leaving JavaScript. 172 VMState state(EXTERNAL); 173 result = call_fun(v8::Utils::ToLocal(key), info); 174 } 175 RETURN_IF_SCHEDULED_EXCEPTION(); 176 if (result.IsEmpty()) return Heap::undefined_value(); 177 return *v8::Utils::OpenHandle(*result); 178 } 179 180 // __defineGetter__ callback 181 if (structure->IsFixedArray()) { 182 Object* getter = FixedArray::cast(structure)->get(kGetterIndex); 183 if (getter->IsJSFunction()) { 184 return Object::GetPropertyWithDefinedGetter(receiver, 185 JSFunction::cast(getter)); 186 } 187 // Getter is not a function. 188 return Heap::undefined_value(); 189 } 190 191 UNREACHABLE(); 192 return 0; 193} 194 195 196Object* Object::GetPropertyWithDefinedGetter(Object* receiver, 197 JSFunction* getter) { 198 HandleScope scope; 199 Handle<JSFunction> fun(JSFunction::cast(getter)); 200 Handle<Object> self(receiver); 201#ifdef ENABLE_DEBUGGER_SUPPORT 202 // Handle stepping into a getter if step into is active. 203 if (Debug::StepInActive()) { 204 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false); 205 } 206#endif 207 bool has_pending_exception; 208 Handle<Object> result = 209 Execution::Call(fun, self, 0, NULL, &has_pending_exception); 210 // Check for pending exception and return the result. 211 if (has_pending_exception) return Failure::Exception(); 212 return *result; 213} 214 215 216// Only deal with CALLBACKS and INTERCEPTOR 217Object* JSObject::GetPropertyWithFailedAccessCheck( 218 Object* receiver, 219 LookupResult* result, 220 String* name, 221 PropertyAttributes* attributes) { 222 if (result->IsProperty()) { 223 switch (result->type()) { 224 case CALLBACKS: { 225 // Only allow API accessors. 226 Object* obj = result->GetCallbackObject(); 227 if (obj->IsAccessorInfo()) { 228 AccessorInfo* info = AccessorInfo::cast(obj); 229 if (info->all_can_read()) { 230 *attributes = result->GetAttributes(); 231 return GetPropertyWithCallback(receiver, 232 result->GetCallbackObject(), 233 name, 234 result->holder()); 235 } 236 } 237 break; 238 } 239 case NORMAL: 240 case FIELD: 241 case CONSTANT_FUNCTION: { 242 // Search ALL_CAN_READ accessors in prototype chain. 243 LookupResult r; 244 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); 245 if (r.IsProperty()) { 246 return GetPropertyWithFailedAccessCheck(receiver, 247 &r, 248 name, 249 attributes); 250 } 251 break; 252 } 253 case INTERCEPTOR: { 254 // If the object has an interceptor, try real named properties. 255 // No access check in GetPropertyAttributeWithInterceptor. 256 LookupResult r; 257 result->holder()->LookupRealNamedProperty(name, &r); 258 if (r.IsProperty()) { 259 return GetPropertyWithFailedAccessCheck(receiver, 260 &r, 261 name, 262 attributes); 263 } 264 break; 265 } 266 default: 267 UNREACHABLE(); 268 } 269 } 270 271 // No accessible property found. 272 *attributes = ABSENT; 273 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET); 274 return Heap::undefined_value(); 275} 276 277 278PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( 279 Object* receiver, 280 LookupResult* result, 281 String* name, 282 bool continue_search) { 283 if (result->IsProperty()) { 284 switch (result->type()) { 285 case CALLBACKS: { 286 // Only allow API accessors. 287 Object* obj = result->GetCallbackObject(); 288 if (obj->IsAccessorInfo()) { 289 AccessorInfo* info = AccessorInfo::cast(obj); 290 if (info->all_can_read()) { 291 return result->GetAttributes(); 292 } 293 } 294 break; 295 } 296 297 case NORMAL: 298 case FIELD: 299 case CONSTANT_FUNCTION: { 300 if (!continue_search) break; 301 // Search ALL_CAN_READ accessors in prototype chain. 302 LookupResult r; 303 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); 304 if (r.IsProperty()) { 305 return GetPropertyAttributeWithFailedAccessCheck(receiver, 306 &r, 307 name, 308 continue_search); 309 } 310 break; 311 } 312 313 case INTERCEPTOR: { 314 // If the object has an interceptor, try real named properties. 315 // No access check in GetPropertyAttributeWithInterceptor. 316 LookupResult r; 317 if (continue_search) { 318 result->holder()->LookupRealNamedProperty(name, &r); 319 } else { 320 result->holder()->LocalLookupRealNamedProperty(name, &r); 321 } 322 if (r.IsProperty()) { 323 return GetPropertyAttributeWithFailedAccessCheck(receiver, 324 &r, 325 name, 326 continue_search); 327 } 328 break; 329 } 330 331 default: 332 UNREACHABLE(); 333 } 334 } 335 336 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS); 337 return ABSENT; 338} 339 340 341Object* JSObject::GetNormalizedProperty(LookupResult* result) { 342 ASSERT(!HasFastProperties()); 343 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry()); 344 if (IsGlobalObject()) { 345 value = JSGlobalPropertyCell::cast(value)->value(); 346 } 347 ASSERT(!value->IsJSGlobalPropertyCell()); 348 return value; 349} 350 351 352Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) { 353 ASSERT(!HasFastProperties()); 354 if (IsGlobalObject()) { 355 JSGlobalPropertyCell* cell = 356 JSGlobalPropertyCell::cast( 357 property_dictionary()->ValueAt(result->GetDictionaryEntry())); 358 cell->set_value(value); 359 } else { 360 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value); 361 } 362 return value; 363} 364 365 366Object* JSObject::SetNormalizedProperty(String* name, 367 Object* value, 368 PropertyDetails details) { 369 ASSERT(!HasFastProperties()); 370 int entry = property_dictionary()->FindEntry(name); 371 if (entry == StringDictionary::kNotFound) { 372 Object* store_value = value; 373 if (IsGlobalObject()) { 374 store_value = Heap::AllocateJSGlobalPropertyCell(value); 375 if (store_value->IsFailure()) return store_value; 376 } 377 Object* dict = property_dictionary()->Add(name, store_value, details); 378 if (dict->IsFailure()) return dict; 379 set_properties(StringDictionary::cast(dict)); 380 return value; 381 } 382 // Preserve enumeration index. 383 details = PropertyDetails(details.attributes(), 384 details.type(), 385 property_dictionary()->DetailsAt(entry).index()); 386 if (IsGlobalObject()) { 387 JSGlobalPropertyCell* cell = 388 JSGlobalPropertyCell::cast(property_dictionary()->ValueAt(entry)); 389 cell->set_value(value); 390 // Please note we have to update the property details. 391 property_dictionary()->DetailsAtPut(entry, details); 392 } else { 393 property_dictionary()->SetEntry(entry, name, value, details); 394 } 395 return value; 396} 397 398 399Object* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) { 400 ASSERT(!HasFastProperties()); 401 StringDictionary* dictionary = property_dictionary(); 402 int entry = dictionary->FindEntry(name); 403 if (entry != StringDictionary::kNotFound) { 404 // If we have a global object set the cell to the hole. 405 if (IsGlobalObject()) { 406 PropertyDetails details = dictionary->DetailsAt(entry); 407 if (details.IsDontDelete()) { 408 if (mode != FORCE_DELETION) return Heap::false_value(); 409 // When forced to delete global properties, we have to make a 410 // map change to invalidate any ICs that think they can load 411 // from the DontDelete cell without checking if it contains 412 // the hole value. 413 Object* new_map = map()->CopyDropDescriptors(); 414 if (new_map->IsFailure()) return new_map; 415 set_map(Map::cast(new_map)); 416 } 417 JSGlobalPropertyCell* cell = 418 JSGlobalPropertyCell::cast(dictionary->ValueAt(entry)); 419 cell->set_value(Heap::the_hole_value()); 420 dictionary->DetailsAtPut(entry, details.AsDeleted()); 421 } else { 422 return dictionary->DeleteProperty(entry, mode); 423 } 424 } 425 return Heap::true_value(); 426} 427 428 429bool JSObject::IsDirty() { 430 Object* cons_obj = map()->constructor(); 431 if (!cons_obj->IsJSFunction()) 432 return true; 433 JSFunction* fun = JSFunction::cast(cons_obj); 434 if (!fun->shared()->function_data()->IsFunctionTemplateInfo()) 435 return true; 436 // If the object is fully fast case and has the same map it was 437 // created with then no changes can have been made to it. 438 return map() != fun->initial_map() 439 || !HasFastElements() 440 || !HasFastProperties(); 441} 442 443 444Object* Object::GetProperty(Object* receiver, 445 LookupResult* result, 446 String* name, 447 PropertyAttributes* attributes) { 448 // Make sure that the top context does not change when doing 449 // callbacks or interceptor calls. 450 AssertNoContextChange ncc; 451 452 // Traverse the prototype chain from the current object (this) to 453 // the holder and check for access rights. This avoid traversing the 454 // objects more than once in case of interceptors, because the 455 // holder will always be the interceptor holder and the search may 456 // only continue with a current object just after the interceptor 457 // holder in the prototype chain. 458 Object* last = result->IsProperty() ? result->holder() : Heap::null_value(); 459 for (Object* current = this; true; current = current->GetPrototype()) { 460 if (current->IsAccessCheckNeeded()) { 461 // Check if we're allowed to read from the current object. Note 462 // that even though we may not actually end up loading the named 463 // property from the current object, we still check that we have 464 // access to it. 465 JSObject* checked = JSObject::cast(current); 466 if (!Top::MayNamedAccess(checked, name, v8::ACCESS_GET)) { 467 return checked->GetPropertyWithFailedAccessCheck(receiver, 468 result, 469 name, 470 attributes); 471 } 472 } 473 // Stop traversing the chain once we reach the last object in the 474 // chain; either the holder of the result or null in case of an 475 // absent property. 476 if (current == last) break; 477 } 478 479 if (!result->IsProperty()) { 480 *attributes = ABSENT; 481 return Heap::undefined_value(); 482 } 483 *attributes = result->GetAttributes(); 484 Object* value; 485 JSObject* holder = result->holder(); 486 switch (result->type()) { 487 case NORMAL: 488 value = holder->GetNormalizedProperty(result); 489 ASSERT(!value->IsTheHole() || result->IsReadOnly()); 490 return value->IsTheHole() ? Heap::undefined_value() : value; 491 case FIELD: 492 value = holder->FastPropertyAt(result->GetFieldIndex()); 493 ASSERT(!value->IsTheHole() || result->IsReadOnly()); 494 return value->IsTheHole() ? Heap::undefined_value() : value; 495 case CONSTANT_FUNCTION: 496 return result->GetConstantFunction(); 497 case CALLBACKS: 498 return GetPropertyWithCallback(receiver, 499 result->GetCallbackObject(), 500 name, 501 holder); 502 case INTERCEPTOR: { 503 JSObject* recvr = JSObject::cast(receiver); 504 return holder->GetPropertyWithInterceptor(recvr, name, attributes); 505 } 506 default: 507 UNREACHABLE(); 508 return NULL; 509 } 510} 511 512 513Object* Object::GetElementWithReceiver(Object* receiver, uint32_t index) { 514 // Non-JS objects do not have integer indexed properties. 515 if (!IsJSObject()) return Heap::undefined_value(); 516 return JSObject::cast(this)->GetElementWithReceiver(JSObject::cast(receiver), 517 index); 518} 519 520 521Object* Object::GetPrototype() { 522 // The object is either a number, a string, a boolean, or a real JS object. 523 if (IsJSObject()) return JSObject::cast(this)->map()->prototype(); 524 Context* context = Top::context()->global_context(); 525 526 if (IsNumber()) return context->number_function()->instance_prototype(); 527 if (IsString()) return context->string_function()->instance_prototype(); 528 if (IsBoolean()) { 529 return context->boolean_function()->instance_prototype(); 530 } else { 531 return Heap::null_value(); 532 } 533} 534 535 536void Object::ShortPrint() { 537 HeapStringAllocator allocator; 538 StringStream accumulator(&allocator); 539 ShortPrint(&accumulator); 540 accumulator.OutputToStdOut(); 541} 542 543 544void Object::ShortPrint(StringStream* accumulator) { 545 if (IsSmi()) { 546 Smi::cast(this)->SmiPrint(accumulator); 547 } else if (IsFailure()) { 548 Failure::cast(this)->FailurePrint(accumulator); 549 } else { 550 HeapObject::cast(this)->HeapObjectShortPrint(accumulator); 551 } 552} 553 554 555void Smi::SmiPrint() { 556 PrintF("%d", value()); 557} 558 559 560void Smi::SmiPrint(StringStream* accumulator) { 561 accumulator->Add("%d", value()); 562} 563 564 565void Failure::FailurePrint(StringStream* accumulator) { 566 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value())); 567} 568 569 570void Failure::FailurePrint() { 571 PrintF("Failure(%p)", reinterpret_cast<void*>(value())); 572} 573 574 575Failure* Failure::RetryAfterGC(int requested_bytes, AllocationSpace space) { 576 ASSERT((space & ~kSpaceTagMask) == 0); 577 // TODO(X64): Stop using Smi validation for non-smi checks, even if they 578 // happen to be identical at the moment. 579 580 int requested = requested_bytes >> kObjectAlignmentBits; 581 int value = (requested << kSpaceTagSize) | space; 582 // We can't very well allocate a heap number in this situation, and if the 583 // requested memory is so large it seems reasonable to say that this is an 584 // out of memory situation. This fixes a crash in 585 // js1_5/Regress/regress-303213.js. 586 if (value >> kSpaceTagSize != requested || 587 !Smi::IsValid(value) || 588 value != ((value << kFailureTypeTagSize) >> kFailureTypeTagSize) || 589 !Smi::IsValid(value << kFailureTypeTagSize)) { 590 Top::context()->mark_out_of_memory(); 591 return Failure::OutOfMemoryException(); 592 } 593 return Construct(RETRY_AFTER_GC, value); 594} 595 596 597// Should a word be prefixed by 'a' or 'an' in order to read naturally in 598// English? Returns false for non-ASCII or words that don't start with 599// a capital letter. The a/an rule follows pronunciation in English. 600// We don't use the BBC's overcorrect "an historic occasion" though if 601// you speak a dialect you may well say "an 'istoric occasion". 602static bool AnWord(String* str) { 603 if (str->length() == 0) return false; // A nothing. 604 int c0 = str->Get(0); 605 int c1 = str->length() > 1 ? str->Get(1) : 0; 606 if (c0 == 'U') { 607 if (c1 > 'Z') { 608 return true; // An Umpire, but a UTF8String, a U. 609 } 610 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') { 611 return true; // An Ape, an ABCBook. 612 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) && 613 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' || 614 c0 == 'S' || c0 == 'X')) { 615 return true; // An MP3File, an M. 616 } 617 return false; 618} 619 620 621Object* String::TryFlatten() { 622#ifdef DEBUG 623 // Do not attempt to flatten in debug mode when allocation is not 624 // allowed. This is to avoid an assertion failure when allocating. 625 // Flattening strings is the only case where we always allow 626 // allocation because no GC is performed if the allocation fails. 627 if (!Heap::IsAllocationAllowed()) return this; 628#endif 629 630 switch (StringShape(this).representation_tag()) { 631 case kConsStringTag: { 632 ConsString* cs = ConsString::cast(this); 633 if (cs->second()->length() == 0) { 634 return this; 635 } 636 // There's little point in putting the flat string in new space if the 637 // cons string is in old space. It can never get GCed until there is 638 // an old space GC. 639 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED : TENURED; 640 int len = length(); 641 Object* object; 642 String* result; 643 if (IsAsciiRepresentation()) { 644 object = Heap::AllocateRawAsciiString(len, tenure); 645 if (object->IsFailure()) return object; 646 result = String::cast(object); 647 String* first = cs->first(); 648 int first_length = first->length(); 649 char* dest = SeqAsciiString::cast(result)->GetChars(); 650 WriteToFlat(first, dest, 0, first_length); 651 String* second = cs->second(); 652 WriteToFlat(second, 653 dest + first_length, 654 0, 655 len - first_length); 656 } else { 657 object = Heap::AllocateRawTwoByteString(len, tenure); 658 if (object->IsFailure()) return object; 659 result = String::cast(object); 660 uc16* dest = SeqTwoByteString::cast(result)->GetChars(); 661 String* first = cs->first(); 662 int first_length = first->length(); 663 WriteToFlat(first, dest, 0, first_length); 664 String* second = cs->second(); 665 WriteToFlat(second, 666 dest + first_length, 667 0, 668 len - first_length); 669 } 670 cs->set_first(result); 671 cs->set_second(Heap::empty_string()); 672 return this; 673 } 674 default: 675 return this; 676 } 677} 678 679 680bool String::MakeExternal(v8::String::ExternalStringResource* resource) { 681#ifdef DEBUG 682 if (FLAG_enable_slow_asserts) { 683 // Assert that the resource and the string are equivalent. 684 ASSERT(static_cast<size_t>(this->length()) == resource->length()); 685 SmartPointer<uc16> smart_chars(NewArray<uc16>(this->length())); 686 String::WriteToFlat(this, *smart_chars, 0, this->length()); 687 ASSERT(memcmp(*smart_chars, 688 resource->data(), 689 resource->length() * sizeof(**smart_chars)) == 0); 690 } 691#endif // DEBUG 692 693 int size = this->Size(); // Byte size of the original string. 694 if (size < ExternalString::kSize) { 695 // The string is too small to fit an external String in its place. This can 696 // only happen for zero length strings. 697 return false; 698 } 699 ASSERT(size >= ExternalString::kSize); 700 bool is_symbol = this->IsSymbol(); 701 int length = this->length(); 702 int hash_field = this->hash_field(); 703 704 // Morph the object to an external string by adjusting the map and 705 // reinitializing the fields. 706 this->set_map(Heap::external_string_map()); 707 ExternalTwoByteString* self = ExternalTwoByteString::cast(this); 708 self->set_length(length); 709 self->set_hash_field(hash_field); 710 self->set_resource(resource); 711 // Additionally make the object into an external symbol if the original string 712 // was a symbol to start with. 713 if (is_symbol) { 714 self->Hash(); // Force regeneration of the hash value. 715 // Now morph this external string into a external symbol. 716 this->set_map(Heap::external_symbol_map()); 717 } 718 719 // Fill the remainder of the string with dead wood. 720 int new_size = this->Size(); // Byte size of the external String object. 721 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size); 722 return true; 723} 724 725 726bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { 727#ifdef DEBUG 728 if (FLAG_enable_slow_asserts) { 729 // Assert that the resource and the string are equivalent. 730 ASSERT(static_cast<size_t>(this->length()) == resource->length()); 731 SmartPointer<char> smart_chars(NewArray<char>(this->length())); 732 String::WriteToFlat(this, *smart_chars, 0, this->length()); 733 ASSERT(memcmp(*smart_chars, 734 resource->data(), 735 resource->length()*sizeof(**smart_chars)) == 0); 736 } 737#endif // DEBUG 738 739 int size = this->Size(); // Byte size of the original string. 740 if (size < ExternalString::kSize) { 741 // The string is too small to fit an external String in its place. This can 742 // only happen for zero length strings. 743 return false; 744 } 745 ASSERT(size >= ExternalString::kSize); 746 bool is_symbol = this->IsSymbol(); 747 int length = this->length(); 748 int hash_field = this->hash_field(); 749 750 // Morph the object to an external string by adjusting the map and 751 // reinitializing the fields. 752 this->set_map(Heap::external_ascii_string_map()); 753 ExternalAsciiString* self = ExternalAsciiString::cast(this); 754 self->set_length(length); 755 self->set_hash_field(hash_field); 756 self->set_resource(resource); 757 // Additionally make the object into an external symbol if the original string 758 // was a symbol to start with. 759 if (is_symbol) { 760 self->Hash(); // Force regeneration of the hash value. 761 // Now morph this external string into a external symbol. 762 this->set_map(Heap::external_ascii_symbol_map()); 763 } 764 765 // Fill the remainder of the string with dead wood. 766 int new_size = this->Size(); // Byte size of the external String object. 767 Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size); 768 return true; 769} 770 771 772void String::StringShortPrint(StringStream* accumulator) { 773 int len = length(); 774 if (len > kMaxShortPrintLength) { 775 accumulator->Add("<Very long string[%u]>", len); 776 return; 777 } 778 779 if (!LooksValid()) { 780 accumulator->Add("<Invalid String>"); 781 return; 782 } 783 784 StringInputBuffer buf(this); 785 786 bool truncated = false; 787 if (len > kMaxShortPrintLength) { 788 len = kMaxShortPrintLength; 789 truncated = true; 790 } 791 bool ascii = true; 792 for (int i = 0; i < len; i++) { 793 int c = buf.GetNext(); 794 795 if (c < 32 || c >= 127) { 796 ascii = false; 797 } 798 } 799 buf.Reset(this); 800 if (ascii) { 801 accumulator->Add("<String[%u]: ", length()); 802 for (int i = 0; i < len; i++) { 803 accumulator->Put(buf.GetNext()); 804 } 805 accumulator->Put('>'); 806 } else { 807 // Backslash indicates that the string contains control 808 // characters and that backslashes are therefore escaped. 809 accumulator->Add("<String[%u]\\: ", length()); 810 for (int i = 0; i < len; i++) { 811 int c = buf.GetNext(); 812 if (c == '\n') { 813 accumulator->Add("\\n"); 814 } else if (c == '\r') { 815 accumulator->Add("\\r"); 816 } else if (c == '\\') { 817 accumulator->Add("\\\\"); 818 } else if (c < 32 || c > 126) { 819 accumulator->Add("\\x%02x", c); 820 } else { 821 accumulator->Put(c); 822 } 823 } 824 if (truncated) { 825 accumulator->Put('.'); 826 accumulator->Put('.'); 827 accumulator->Put('.'); 828 } 829 accumulator->Put('>'); 830 } 831 return; 832} 833 834 835void JSObject::JSObjectShortPrint(StringStream* accumulator) { 836 switch (map()->instance_type()) { 837 case JS_ARRAY_TYPE: { 838 double length = JSArray::cast(this)->length()->Number(); 839 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length)); 840 break; 841 } 842 case JS_REGEXP_TYPE: { 843 accumulator->Add("<JS RegExp>"); 844 break; 845 } 846 case JS_FUNCTION_TYPE: { 847 Object* fun_name = JSFunction::cast(this)->shared()->name(); 848 bool printed = false; 849 if (fun_name->IsString()) { 850 String* str = String::cast(fun_name); 851 if (str->length() > 0) { 852 accumulator->Add("<JS Function "); 853 accumulator->Put(str); 854 accumulator->Put('>'); 855 printed = true; 856 } 857 } 858 if (!printed) { 859 accumulator->Add("<JS Function>"); 860 } 861 break; 862 } 863 // All other JSObjects are rather similar to each other (JSObject, 864 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). 865 default: { 866 Object* constructor = map()->constructor(); 867 bool printed = false; 868 if (constructor->IsHeapObject() && 869 !Heap::Contains(HeapObject::cast(constructor))) { 870 accumulator->Add("!!!INVALID CONSTRUCTOR!!!"); 871 } else { 872 bool global_object = IsJSGlobalProxy(); 873 if (constructor->IsJSFunction()) { 874 if (!Heap::Contains(JSFunction::cast(constructor)->shared())) { 875 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!"); 876 } else { 877 Object* constructor_name = 878 JSFunction::cast(constructor)->shared()->name(); 879 if (constructor_name->IsString()) { 880 String* str = String::cast(constructor_name); 881 if (str->length() > 0) { 882 bool vowel = AnWord(str); 883 accumulator->Add("<%sa%s ", 884 global_object ? "Global Object: " : "", 885 vowel ? "n" : ""); 886 accumulator->Put(str); 887 accumulator->Put('>'); 888 printed = true; 889 } 890 } 891 } 892 } 893 if (!printed) { 894 accumulator->Add("<JS %sObject", global_object ? "Global " : ""); 895 } 896 } 897 if (IsJSValue()) { 898 accumulator->Add(" value = "); 899 JSValue::cast(this)->value()->ShortPrint(accumulator); 900 } 901 accumulator->Put('>'); 902 break; 903 } 904 } 905} 906 907 908void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { 909 // if (!Heap::InNewSpace(this)) PrintF("*", this); 910 if (!Heap::Contains(this)) { 911 accumulator->Add("!!!INVALID POINTER!!!"); 912 return; 913 } 914 if (!Heap::Contains(map())) { 915 accumulator->Add("!!!INVALID MAP!!!"); 916 return; 917 } 918 919 accumulator->Add("%p ", this); 920 921 if (IsString()) { 922 String::cast(this)->StringShortPrint(accumulator); 923 return; 924 } 925 if (IsJSObject()) { 926 JSObject::cast(this)->JSObjectShortPrint(accumulator); 927 return; 928 } 929 switch (map()->instance_type()) { 930 case MAP_TYPE: 931 accumulator->Add("<Map>"); 932 break; 933 case FIXED_ARRAY_TYPE: 934 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length()); 935 break; 936 case BYTE_ARRAY_TYPE: 937 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length()); 938 break; 939 case PIXEL_ARRAY_TYPE: 940 accumulator->Add("<PixelArray[%u]>", PixelArray::cast(this)->length()); 941 break; 942 case EXTERNAL_BYTE_ARRAY_TYPE: 943 accumulator->Add("<ExternalByteArray[%u]>", 944 ExternalByteArray::cast(this)->length()); 945 break; 946 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: 947 accumulator->Add("<ExternalUnsignedByteArray[%u]>", 948 ExternalUnsignedByteArray::cast(this)->length()); 949 break; 950 case EXTERNAL_SHORT_ARRAY_TYPE: 951 accumulator->Add("<ExternalShortArray[%u]>", 952 ExternalShortArray::cast(this)->length()); 953 break; 954 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: 955 accumulator->Add("<ExternalUnsignedShortArray[%u]>", 956 ExternalUnsignedShortArray::cast(this)->length()); 957 break; 958 case EXTERNAL_INT_ARRAY_TYPE: 959 accumulator->Add("<ExternalIntArray[%u]>", 960 ExternalIntArray::cast(this)->length()); 961 break; 962 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: 963 accumulator->Add("<ExternalUnsignedIntArray[%u]>", 964 ExternalUnsignedIntArray::cast(this)->length()); 965 break; 966 case EXTERNAL_FLOAT_ARRAY_TYPE: 967 accumulator->Add("<ExternalFloatArray[%u]>", 968 ExternalFloatArray::cast(this)->length()); 969 break; 970 case SHARED_FUNCTION_INFO_TYPE: 971 accumulator->Add("<SharedFunctionInfo>"); 972 break; 973#define MAKE_STRUCT_CASE(NAME, Name, name) \ 974 case NAME##_TYPE: \ 975 accumulator->Put('<'); \ 976 accumulator->Add(#Name); \ 977 accumulator->Put('>'); \ 978 break; 979 STRUCT_LIST(MAKE_STRUCT_CASE) 980#undef MAKE_STRUCT_CASE 981 case CODE_TYPE: 982 accumulator->Add("<Code>"); 983 break; 984 case ODDBALL_TYPE: { 985 if (IsUndefined()) 986 accumulator->Add("<undefined>"); 987 else if (IsTheHole()) 988 accumulator->Add("<the hole>"); 989 else if (IsNull()) 990 accumulator->Add("<null>"); 991 else if (IsTrue()) 992 accumulator->Add("<true>"); 993 else if (IsFalse()) 994 accumulator->Add("<false>"); 995 else 996 accumulator->Add("<Odd Oddball>"); 997 break; 998 } 999 case HEAP_NUMBER_TYPE: 1000 accumulator->Add("<Number: "); 1001 HeapNumber::cast(this)->HeapNumberPrint(accumulator); 1002 accumulator->Put('>'); 1003 break; 1004 case PROXY_TYPE: 1005 accumulator->Add("<Proxy>"); 1006 break; 1007 case JS_GLOBAL_PROPERTY_CELL_TYPE: 1008 accumulator->Add("Cell for "); 1009 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator); 1010 break; 1011 default: 1012 accumulator->Add("<Other heap object (%d)>", map()->instance_type()); 1013 break; 1014 } 1015} 1016 1017 1018int HeapObject::SlowSizeFromMap(Map* map) { 1019 // Avoid calling functions such as FixedArray::cast during GC, which 1020 // read map pointer of this object again. 1021 InstanceType instance_type = map->instance_type(); 1022 uint32_t type = static_cast<uint32_t>(instance_type); 1023 1024 if (instance_type < FIRST_NONSTRING_TYPE 1025 && (StringShape(instance_type).IsSequential())) { 1026 if ((type & kStringEncodingMask) == kAsciiStringTag) { 1027 SeqAsciiString* seq_ascii_this = reinterpret_cast<SeqAsciiString*>(this); 1028 return seq_ascii_this->SeqAsciiStringSize(instance_type); 1029 } else { 1030 SeqTwoByteString* self = reinterpret_cast<SeqTwoByteString*>(this); 1031 return self->SeqTwoByteStringSize(instance_type); 1032 } 1033 } 1034 1035 switch (instance_type) { 1036 case FIXED_ARRAY_TYPE: 1037 return reinterpret_cast<FixedArray*>(this)->FixedArraySize(); 1038 case BYTE_ARRAY_TYPE: 1039 return reinterpret_cast<ByteArray*>(this)->ByteArraySize(); 1040 case CODE_TYPE: 1041 return reinterpret_cast<Code*>(this)->CodeSize(); 1042 case MAP_TYPE: 1043 return Map::kSize; 1044 default: 1045 return map->instance_size(); 1046 } 1047} 1048 1049 1050void HeapObject::Iterate(ObjectVisitor* v) { 1051 // Handle header 1052 IteratePointer(v, kMapOffset); 1053 // Handle object body 1054 Map* m = map(); 1055 IterateBody(m->instance_type(), SizeFromMap(m), v); 1056} 1057 1058 1059void HeapObject::IterateBody(InstanceType type, int object_size, 1060 ObjectVisitor* v) { 1061 // Avoiding <Type>::cast(this) because it accesses the map pointer field. 1062 // During GC, the map pointer field is encoded. 1063 if (type < FIRST_NONSTRING_TYPE) { 1064 switch (type & kStringRepresentationMask) { 1065 case kSeqStringTag: 1066 break; 1067 case kConsStringTag: 1068 reinterpret_cast<ConsString*>(this)->ConsStringIterateBody(v); 1069 break; 1070 case kExternalStringTag: 1071 if ((type & kStringEncodingMask) == kAsciiStringTag) { 1072 reinterpret_cast<ExternalAsciiString*>(this)-> 1073 ExternalAsciiStringIterateBody(v); 1074 } else { 1075 reinterpret_cast<ExternalTwoByteString*>(this)-> 1076 ExternalTwoByteStringIterateBody(v); 1077 } 1078 break; 1079 } 1080 return; 1081 } 1082 1083 switch (type) { 1084 case FIXED_ARRAY_TYPE: 1085 reinterpret_cast<FixedArray*>(this)->FixedArrayIterateBody(v); 1086 break; 1087 case JS_OBJECT_TYPE: 1088 case JS_CONTEXT_EXTENSION_OBJECT_TYPE: 1089 case JS_VALUE_TYPE: 1090 case JS_ARRAY_TYPE: 1091 case JS_REGEXP_TYPE: 1092 case JS_FUNCTION_TYPE: 1093 case JS_GLOBAL_PROXY_TYPE: 1094 case JS_GLOBAL_OBJECT_TYPE: 1095 case JS_BUILTINS_OBJECT_TYPE: 1096 reinterpret_cast<JSObject*>(this)->JSObjectIterateBody(object_size, v); 1097 break; 1098 case ODDBALL_TYPE: 1099 reinterpret_cast<Oddball*>(this)->OddballIterateBody(v); 1100 break; 1101 case PROXY_TYPE: 1102 reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v); 1103 break; 1104 case MAP_TYPE: 1105 reinterpret_cast<Map*>(this)->MapIterateBody(v); 1106 break; 1107 case CODE_TYPE: 1108 reinterpret_cast<Code*>(this)->CodeIterateBody(v); 1109 break; 1110 case JS_GLOBAL_PROPERTY_CELL_TYPE: 1111 reinterpret_cast<JSGlobalPropertyCell*>(this) 1112 ->JSGlobalPropertyCellIterateBody(v); 1113 break; 1114 case HEAP_NUMBER_TYPE: 1115 case FILLER_TYPE: 1116 case BYTE_ARRAY_TYPE: 1117 case PIXEL_ARRAY_TYPE: 1118 case EXTERNAL_BYTE_ARRAY_TYPE: 1119 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: 1120 case EXTERNAL_SHORT_ARRAY_TYPE: 1121 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: 1122 case EXTERNAL_INT_ARRAY_TYPE: 1123 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: 1124 case EXTERNAL_FLOAT_ARRAY_TYPE: 1125 break; 1126 case SHARED_FUNCTION_INFO_TYPE: { 1127 SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(this); 1128 shared->SharedFunctionInfoIterateBody(v); 1129 break; 1130 } 1131#define MAKE_STRUCT_CASE(NAME, Name, name) \ 1132 case NAME##_TYPE: 1133 STRUCT_LIST(MAKE_STRUCT_CASE) 1134#undef MAKE_STRUCT_CASE 1135 IterateStructBody(object_size, v); 1136 break; 1137 default: 1138 PrintF("Unknown type: %d\n", type); 1139 UNREACHABLE(); 1140 } 1141} 1142 1143 1144void HeapObject::IterateStructBody(int object_size, ObjectVisitor* v) { 1145 IteratePointers(v, HeapObject::kHeaderSize, object_size); 1146} 1147 1148 1149Object* HeapNumber::HeapNumberToBoolean() { 1150 // NaN, +0, and -0 should return the false object 1151 switch (fpclassify(value())) { 1152 case FP_NAN: // fall through 1153 case FP_ZERO: return Heap::false_value(); 1154 default: return Heap::true_value(); 1155 } 1156} 1157 1158 1159void HeapNumber::HeapNumberPrint() { 1160 PrintF("%.16g", Number()); 1161} 1162 1163 1164void HeapNumber::HeapNumberPrint(StringStream* accumulator) { 1165 // The Windows version of vsnprintf can allocate when printing a %g string 1166 // into a buffer that may not be big enough. We don't want random memory 1167 // allocation when producing post-crash stack traces, so we print into a 1168 // buffer that is plenty big enough for any floating point number, then 1169 // print that using vsnprintf (which may truncate but never allocate if 1170 // there is no more space in the buffer). 1171 EmbeddedVector<char, 100> buffer; 1172 OS::SNPrintF(buffer, "%.16g", Number()); 1173 accumulator->Add("%s", buffer.start()); 1174} 1175 1176 1177String* JSObject::class_name() { 1178 if (IsJSFunction()) { 1179 return Heap::function_class_symbol(); 1180 } 1181 if (map()->constructor()->IsJSFunction()) { 1182 JSFunction* constructor = JSFunction::cast(map()->constructor()); 1183 return String::cast(constructor->shared()->instance_class_name()); 1184 } 1185 // If the constructor is not present, return "Object". 1186 return Heap::Object_symbol(); 1187} 1188 1189 1190String* JSObject::constructor_name() { 1191 if (IsJSFunction()) { 1192 return JSFunction::cast(this)->IsBoilerplate() ? 1193 Heap::function_class_symbol() : Heap::closure_symbol(); 1194 } 1195 if (map()->constructor()->IsJSFunction()) { 1196 JSFunction* constructor = JSFunction::cast(map()->constructor()); 1197 String* name = String::cast(constructor->shared()->name()); 1198 return name->length() > 0 ? name : constructor->shared()->inferred_name(); 1199 } 1200 // If the constructor is not present, return "Object". 1201 return Heap::Object_symbol(); 1202} 1203 1204 1205void JSObject::JSObjectIterateBody(int object_size, ObjectVisitor* v) { 1206 // Iterate over all fields in the body. Assumes all are Object*. 1207 IteratePointers(v, kPropertiesOffset, object_size); 1208} 1209 1210 1211Object* JSObject::AddFastPropertyUsingMap(Map* new_map, 1212 String* name, 1213 Object* value) { 1214 int index = new_map->PropertyIndexFor(name); 1215 if (map()->unused_property_fields() == 0) { 1216 ASSERT(map()->unused_property_fields() == 0); 1217 int new_unused = new_map->unused_property_fields(); 1218 Object* values = 1219 properties()->CopySize(properties()->length() + new_unused + 1); 1220 if (values->IsFailure()) return values; 1221 set_properties(FixedArray::cast(values)); 1222 } 1223 set_map(new_map); 1224 return FastPropertyAtPut(index, value); 1225} 1226 1227 1228Object* JSObject::AddFastProperty(String* name, 1229 Object* value, 1230 PropertyAttributes attributes) { 1231 // Normalize the object if the name is an actual string (not the 1232 // hidden symbols) and is not a real identifier. 1233 StringInputBuffer buffer(name); 1234 if (!Scanner::IsIdentifier(&buffer) && name != Heap::hidden_symbol()) { 1235 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1236 if (obj->IsFailure()) return obj; 1237 return AddSlowProperty(name, value, attributes); 1238 } 1239 1240 DescriptorArray* old_descriptors = map()->instance_descriptors(); 1241 // Compute the new index for new field. 1242 int index = map()->NextFreePropertyIndex(); 1243 1244 // Allocate new instance descriptors with (name, index) added 1245 FieldDescriptor new_field(name, index, attributes); 1246 Object* new_descriptors = 1247 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS); 1248 if (new_descriptors->IsFailure()) return new_descriptors; 1249 1250 // Only allow map transition if the object's map is NOT equal to the 1251 // global object_function's map and there is not a transition for name. 1252 bool allow_map_transition = 1253 !old_descriptors->Contains(name) && 1254 (Top::context()->global_context()->object_function()->map() != map()); 1255 1256 ASSERT(index < map()->inobject_properties() || 1257 (index - map()->inobject_properties()) < properties()->length() || 1258 map()->unused_property_fields() == 0); 1259 // Allocate a new map for the object. 1260 Object* r = map()->CopyDropDescriptors(); 1261 if (r->IsFailure()) return r; 1262 Map* new_map = Map::cast(r); 1263 if (allow_map_transition) { 1264 // Allocate new instance descriptors for the old map with map transition. 1265 MapTransitionDescriptor d(name, Map::cast(new_map), attributes); 1266 Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS); 1267 if (r->IsFailure()) return r; 1268 old_descriptors = DescriptorArray::cast(r); 1269 } 1270 1271 if (map()->unused_property_fields() == 0) { 1272 if (properties()->length() > kMaxFastProperties) { 1273 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1274 if (obj->IsFailure()) return obj; 1275 return AddSlowProperty(name, value, attributes); 1276 } 1277 // Make room for the new value 1278 Object* values = 1279 properties()->CopySize(properties()->length() + kFieldsAdded); 1280 if (values->IsFailure()) return values; 1281 set_properties(FixedArray::cast(values)); 1282 new_map->set_unused_property_fields(kFieldsAdded - 1); 1283 } else { 1284 new_map->set_unused_property_fields(map()->unused_property_fields() - 1); 1285 } 1286 // We have now allocated all the necessary objects. 1287 // All the changes can be applied at once, so they are atomic. 1288 map()->set_instance_descriptors(old_descriptors); 1289 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); 1290 set_map(new_map); 1291 return FastPropertyAtPut(index, value); 1292} 1293 1294 1295Object* JSObject::AddConstantFunctionProperty(String* name, 1296 JSFunction* function, 1297 PropertyAttributes attributes) { 1298 ASSERT(!Heap::InNewSpace(function)); 1299 1300 // Allocate new instance descriptors with (name, function) added 1301 ConstantFunctionDescriptor d(name, function, attributes); 1302 Object* new_descriptors = 1303 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS); 1304 if (new_descriptors->IsFailure()) return new_descriptors; 1305 1306 // Allocate a new map for the object. 1307 Object* new_map = map()->CopyDropDescriptors(); 1308 if (new_map->IsFailure()) return new_map; 1309 1310 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors); 1311 Map::cast(new_map)->set_instance_descriptors(descriptors); 1312 Map* old_map = map(); 1313 set_map(Map::cast(new_map)); 1314 1315 // If the old map is the global object map (from new Object()), 1316 // then transitions are not added to it, so we are done. 1317 if (old_map == Top::context()->global_context()->object_function()->map()) { 1318 return function; 1319 } 1320 1321 // Do not add CONSTANT_TRANSITIONS to global objects 1322 if (IsGlobalObject()) { 1323 return function; 1324 } 1325 1326 // Add a CONSTANT_TRANSITION descriptor to the old map, 1327 // so future assignments to this property on other objects 1328 // of the same type will create a normal field, not a constant function. 1329 // Don't do this for special properties, with non-trival attributes. 1330 if (attributes != NONE) { 1331 return function; 1332 } 1333 ConstTransitionDescriptor mark(name); 1334 new_descriptors = 1335 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS); 1336 if (new_descriptors->IsFailure()) { 1337 return function; // We have accomplished the main goal, so return success. 1338 } 1339 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); 1340 1341 return function; 1342} 1343 1344 1345// Add property in slow mode 1346Object* JSObject::AddSlowProperty(String* name, 1347 Object* value, 1348 PropertyAttributes attributes) { 1349 ASSERT(!HasFastProperties()); 1350 StringDictionary* dict = property_dictionary(); 1351 Object* store_value = value; 1352 if (IsGlobalObject()) { 1353 // In case name is an orphaned property reuse the cell. 1354 int entry = dict->FindEntry(name); 1355 if (entry != StringDictionary::kNotFound) { 1356 store_value = dict->ValueAt(entry); 1357 JSGlobalPropertyCell::cast(store_value)->set_value(value); 1358 // Assign an enumeration index to the property and update 1359 // SetNextEnumerationIndex. 1360 int index = dict->NextEnumerationIndex(); 1361 PropertyDetails details = PropertyDetails(attributes, NORMAL, index); 1362 dict->SetNextEnumerationIndex(index + 1); 1363 dict->SetEntry(entry, name, store_value, details); 1364 return value; 1365 } 1366 store_value = Heap::AllocateJSGlobalPropertyCell(value); 1367 if (store_value->IsFailure()) return store_value; 1368 JSGlobalPropertyCell::cast(store_value)->set_value(value); 1369 } 1370 PropertyDetails details = PropertyDetails(attributes, NORMAL); 1371 Object* result = dict->Add(name, store_value, details); 1372 if (result->IsFailure()) return result; 1373 if (dict != result) set_properties(StringDictionary::cast(result)); 1374 return value; 1375} 1376 1377 1378Object* JSObject::AddProperty(String* name, 1379 Object* value, 1380 PropertyAttributes attributes) { 1381 ASSERT(!IsJSGlobalProxy()); 1382 if (HasFastProperties()) { 1383 // Ensure the descriptor array does not get too big. 1384 if (map()->instance_descriptors()->number_of_descriptors() < 1385 DescriptorArray::kMaxNumberOfDescriptors) { 1386 if (value->IsJSFunction() && !Heap::InNewSpace(value)) { 1387 return AddConstantFunctionProperty(name, 1388 JSFunction::cast(value), 1389 attributes); 1390 } else { 1391 return AddFastProperty(name, value, attributes); 1392 } 1393 } else { 1394 // Normalize the object to prevent very large instance descriptors. 1395 // This eliminates unwanted N^2 allocation and lookup behavior. 1396 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1397 if (obj->IsFailure()) return obj; 1398 } 1399 } 1400 return AddSlowProperty(name, value, attributes); 1401} 1402 1403 1404Object* JSObject::SetPropertyPostInterceptor(String* name, 1405 Object* value, 1406 PropertyAttributes attributes) { 1407 // Check local property, ignore interceptor. 1408 LookupResult result; 1409 LocalLookupRealNamedProperty(name, &result); 1410 if (result.IsFound()) { 1411 // An existing property, a map transition or a null descriptor was 1412 // found. Use set property to handle all these cases. 1413 return SetProperty(&result, name, value, attributes); 1414 } 1415 // Add a new real property. 1416 return AddProperty(name, value, attributes); 1417} 1418 1419 1420Object* JSObject::ReplaceSlowProperty(String* name, 1421 Object* value, 1422 PropertyAttributes attributes) { 1423 StringDictionary* dictionary = property_dictionary(); 1424 int old_index = dictionary->FindEntry(name); 1425 int new_enumeration_index = 0; // 0 means "Use the next available index." 1426 if (old_index != -1) { 1427 // All calls to ReplaceSlowProperty have had all transitions removed. 1428 ASSERT(!dictionary->DetailsAt(old_index).IsTransition()); 1429 new_enumeration_index = dictionary->DetailsAt(old_index).index(); 1430 } 1431 1432 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index); 1433 return SetNormalizedProperty(name, value, new_details); 1434} 1435 1436 1437Object* JSObject::ConvertDescriptorToFieldAndMapTransition( 1438 String* name, 1439 Object* new_value, 1440 PropertyAttributes attributes) { 1441 Map* old_map = map(); 1442 Object* result = ConvertDescriptorToField(name, new_value, attributes); 1443 if (result->IsFailure()) return result; 1444 // If we get to this point we have succeeded - do not return failure 1445 // after this point. Later stuff is optional. 1446 if (!HasFastProperties()) { 1447 return result; 1448 } 1449 // Do not add transitions to the map of "new Object()". 1450 if (map() == Top::context()->global_context()->object_function()->map()) { 1451 return result; 1452 } 1453 1454 MapTransitionDescriptor transition(name, 1455 map(), 1456 attributes); 1457 Object* new_descriptors = 1458 old_map->instance_descriptors()-> 1459 CopyInsert(&transition, KEEP_TRANSITIONS); 1460 if (new_descriptors->IsFailure()) return result; // Yes, return _result_. 1461 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); 1462 return result; 1463} 1464 1465 1466Object* JSObject::ConvertDescriptorToField(String* name, 1467 Object* new_value, 1468 PropertyAttributes attributes) { 1469 if (map()->unused_property_fields() == 0 && 1470 properties()->length() > kMaxFastProperties) { 1471 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1472 if (obj->IsFailure()) return obj; 1473 return ReplaceSlowProperty(name, new_value, attributes); 1474 } 1475 1476 int index = map()->NextFreePropertyIndex(); 1477 FieldDescriptor new_field(name, index, attributes); 1478 // Make a new DescriptorArray replacing an entry with FieldDescriptor. 1479 Object* descriptors_unchecked = map()->instance_descriptors()-> 1480 CopyInsert(&new_field, REMOVE_TRANSITIONS); 1481 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked; 1482 DescriptorArray* new_descriptors = 1483 DescriptorArray::cast(descriptors_unchecked); 1484 1485 // Make a new map for the object. 1486 Object* new_map_unchecked = map()->CopyDropDescriptors(); 1487 if (new_map_unchecked->IsFailure()) return new_map_unchecked; 1488 Map* new_map = Map::cast(new_map_unchecked); 1489 new_map->set_instance_descriptors(new_descriptors); 1490 1491 // Make new properties array if necessary. 1492 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer. 1493 int new_unused_property_fields = map()->unused_property_fields() - 1; 1494 if (map()->unused_property_fields() == 0) { 1495 new_unused_property_fields = kFieldsAdded - 1; 1496 Object* new_properties_unchecked = 1497 properties()->CopySize(properties()->length() + kFieldsAdded); 1498 if (new_properties_unchecked->IsFailure()) return new_properties_unchecked; 1499 new_properties = FixedArray::cast(new_properties_unchecked); 1500 } 1501 1502 // Update pointers to commit changes. 1503 // Object points to the new map. 1504 new_map->set_unused_property_fields(new_unused_property_fields); 1505 set_map(new_map); 1506 if (new_properties) { 1507 set_properties(FixedArray::cast(new_properties)); 1508 } 1509 return FastPropertyAtPut(index, new_value); 1510} 1511 1512 1513 1514Object* JSObject::SetPropertyWithInterceptor(String* name, 1515 Object* value, 1516 PropertyAttributes attributes) { 1517 HandleScope scope; 1518 Handle<JSObject> this_handle(this); 1519 Handle<String> name_handle(name); 1520 Handle<Object> value_handle(value); 1521 Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); 1522 if (!interceptor->setter()->IsUndefined()) { 1523 LOG(ApiNamedPropertyAccess("interceptor-named-set", this, name)); 1524 CustomArguments args(interceptor->data(), this, this); 1525 v8::AccessorInfo info(args.end()); 1526 v8::NamedPropertySetter setter = 1527 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter()); 1528 v8::Handle<v8::Value> result; 1529 { 1530 // Leaving JavaScript. 1531 VMState state(EXTERNAL); 1532 Handle<Object> value_unhole(value->IsTheHole() ? 1533 Heap::undefined_value() : 1534 value); 1535 result = setter(v8::Utils::ToLocal(name_handle), 1536 v8::Utils::ToLocal(value_unhole), 1537 info); 1538 } 1539 RETURN_IF_SCHEDULED_EXCEPTION(); 1540 if (!result.IsEmpty()) return *value_handle; 1541 } 1542 Object* raw_result = this_handle->SetPropertyPostInterceptor(*name_handle, 1543 *value_handle, 1544 attributes); 1545 RETURN_IF_SCHEDULED_EXCEPTION(); 1546 return raw_result; 1547} 1548 1549 1550Object* JSObject::SetProperty(String* name, 1551 Object* value, 1552 PropertyAttributes attributes) { 1553 LookupResult result; 1554 LocalLookup(name, &result); 1555 return SetProperty(&result, name, value, attributes); 1556} 1557 1558 1559Object* JSObject::SetPropertyWithCallback(Object* structure, 1560 String* name, 1561 Object* value, 1562 JSObject* holder) { 1563 HandleScope scope; 1564 1565 // We should never get here to initialize a const with the hole 1566 // value since a const declaration would conflict with the setter. 1567 ASSERT(!value->IsTheHole()); 1568 Handle<Object> value_handle(value); 1569 1570 // To accommodate both the old and the new api we switch on the 1571 // data structure used to store the callbacks. Eventually proxy 1572 // callbacks should be phased out. 1573 if (structure->IsProxy()) { 1574 AccessorDescriptor* callback = 1575 reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy()); 1576 Object* obj = (callback->setter)(this, value, callback->data); 1577 RETURN_IF_SCHEDULED_EXCEPTION(); 1578 if (obj->IsFailure()) return obj; 1579 return *value_handle; 1580 } 1581 1582 if (structure->IsAccessorInfo()) { 1583 // api style callbacks 1584 AccessorInfo* data = AccessorInfo::cast(structure); 1585 Object* call_obj = data->setter(); 1586 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj); 1587 if (call_fun == NULL) return value; 1588 Handle<String> key(name); 1589 LOG(ApiNamedPropertyAccess("store", this, name)); 1590 CustomArguments args(data->data(), this, JSObject::cast(holder)); 1591 v8::AccessorInfo info(args.end()); 1592 { 1593 // Leaving JavaScript. 1594 VMState state(EXTERNAL); 1595 call_fun(v8::Utils::ToLocal(key), 1596 v8::Utils::ToLocal(value_handle), 1597 info); 1598 } 1599 RETURN_IF_SCHEDULED_EXCEPTION(); 1600 return *value_handle; 1601 } 1602 1603 if (structure->IsFixedArray()) { 1604 Object* setter = FixedArray::cast(structure)->get(kSetterIndex); 1605 if (setter->IsJSFunction()) { 1606 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value); 1607 } else { 1608 Handle<String> key(name); 1609 Handle<Object> holder_handle(holder); 1610 Handle<Object> args[2] = { key, holder_handle }; 1611 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback", 1612 HandleVector(args, 2))); 1613 } 1614 } 1615 1616 UNREACHABLE(); 1617 return 0; 1618} 1619 1620 1621Object* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter, 1622 Object* value) { 1623 Handle<Object> value_handle(value); 1624 Handle<JSFunction> fun(JSFunction::cast(setter)); 1625 Handle<JSObject> self(this); 1626#ifdef ENABLE_DEBUGGER_SUPPORT 1627 // Handle stepping into a setter if step into is active. 1628 if (Debug::StepInActive()) { 1629 Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false); 1630 } 1631#endif 1632 bool has_pending_exception; 1633 Object** argv[] = { value_handle.location() }; 1634 Execution::Call(fun, self, 1, argv, &has_pending_exception); 1635 // Check for pending exception and return the result. 1636 if (has_pending_exception) return Failure::Exception(); 1637 return *value_handle; 1638} 1639 1640 1641void JSObject::LookupCallbackSetterInPrototypes(String* name, 1642 LookupResult* result) { 1643 for (Object* pt = GetPrototype(); 1644 pt != Heap::null_value(); 1645 pt = pt->GetPrototype()) { 1646 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); 1647 if (result->IsProperty()) { 1648 if (result->IsReadOnly()) { 1649 result->NotFound(); 1650 return; 1651 } 1652 if (result->type() == CALLBACKS) { 1653 return; 1654 } 1655 } 1656 } 1657 result->NotFound(); 1658} 1659 1660 1661Object* JSObject::LookupCallbackSetterInPrototypes(uint32_t index) { 1662 for (Object* pt = GetPrototype(); 1663 pt != Heap::null_value(); 1664 pt = pt->GetPrototype()) { 1665 if (!JSObject::cast(pt)->HasDictionaryElements()) { 1666 continue; 1667 } 1668 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary(); 1669 int entry = dictionary->FindEntry(index); 1670 if (entry != NumberDictionary::kNotFound) { 1671 Object* element = dictionary->ValueAt(entry); 1672 PropertyDetails details = dictionary->DetailsAt(entry); 1673 if (details.type() == CALLBACKS) { 1674 // Only accessors allowed as elements. 1675 return FixedArray::cast(element)->get(kSetterIndex); 1676 } 1677 } 1678 } 1679 return Heap::undefined_value(); 1680} 1681 1682 1683void JSObject::LookupInDescriptor(String* name, LookupResult* result) { 1684 DescriptorArray* descriptors = map()->instance_descriptors(); 1685 int number = DescriptorLookupCache::Lookup(descriptors, name); 1686 if (number == DescriptorLookupCache::kAbsent) { 1687 number = descriptors->Search(name); 1688 DescriptorLookupCache::Update(descriptors, name, number); 1689 } 1690 if (number != DescriptorArray::kNotFound) { 1691 result->DescriptorResult(this, descriptors->GetDetails(number), number); 1692 } else { 1693 result->NotFound(); 1694 } 1695} 1696 1697 1698void JSObject::LocalLookupRealNamedProperty(String* name, 1699 LookupResult* result) { 1700 if (IsJSGlobalProxy()) { 1701 Object* proto = GetPrototype(); 1702 if (proto->IsNull()) return result->NotFound(); 1703 ASSERT(proto->IsJSGlobalObject()); 1704 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result); 1705 } 1706 1707 if (HasFastProperties()) { 1708 LookupInDescriptor(name, result); 1709 if (result->IsFound()) { 1710 // A property, a map transition or a null descriptor was found. 1711 // We return all of these result types because 1712 // LocalLookupRealNamedProperty is used when setting properties 1713 // where map transitions and null descriptors are handled. 1714 ASSERT(result->holder() == this && result->type() != NORMAL); 1715 // Disallow caching for uninitialized constants. These can only 1716 // occur as fields. 1717 if (result->IsReadOnly() && result->type() == FIELD && 1718 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) { 1719 result->DisallowCaching(); 1720 } 1721 return; 1722 } 1723 } else { 1724 int entry = property_dictionary()->FindEntry(name); 1725 if (entry != StringDictionary::kNotFound) { 1726 Object* value = property_dictionary()->ValueAt(entry); 1727 if (IsGlobalObject()) { 1728 PropertyDetails d = property_dictionary()->DetailsAt(entry); 1729 if (d.IsDeleted()) { 1730 result->NotFound(); 1731 return; 1732 } 1733 value = JSGlobalPropertyCell::cast(value)->value(); 1734 } 1735 // Make sure to disallow caching for uninitialized constants 1736 // found in the dictionary-mode objects. 1737 if (value->IsTheHole()) result->DisallowCaching(); 1738 result->DictionaryResult(this, entry); 1739 return; 1740 } 1741 // Slow case object skipped during lookup. Do not use inline caching. 1742 if (!IsGlobalObject()) result->DisallowCaching(); 1743 } 1744 result->NotFound(); 1745} 1746 1747 1748void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) { 1749 LocalLookupRealNamedProperty(name, result); 1750 if (result->IsProperty()) return; 1751 1752 LookupRealNamedPropertyInPrototypes(name, result); 1753} 1754 1755 1756void JSObject::LookupRealNamedPropertyInPrototypes(String* name, 1757 LookupResult* result) { 1758 for (Object* pt = GetPrototype(); 1759 pt != Heap::null_value(); 1760 pt = JSObject::cast(pt)->GetPrototype()) { 1761 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); 1762 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return; 1763 } 1764 result->NotFound(); 1765} 1766 1767 1768// We only need to deal with CALLBACKS and INTERCEPTORS 1769Object* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result, 1770 String* name, 1771 Object* value) { 1772 if (!result->IsProperty()) { 1773 LookupCallbackSetterInPrototypes(name, result); 1774 } 1775 1776 if (result->IsProperty()) { 1777 if (!result->IsReadOnly()) { 1778 switch (result->type()) { 1779 case CALLBACKS: { 1780 Object* obj = result->GetCallbackObject(); 1781 if (obj->IsAccessorInfo()) { 1782 AccessorInfo* info = AccessorInfo::cast(obj); 1783 if (info->all_can_write()) { 1784 return SetPropertyWithCallback(result->GetCallbackObject(), 1785 name, 1786 value, 1787 result->holder()); 1788 } 1789 } 1790 break; 1791 } 1792 case INTERCEPTOR: { 1793 // Try lookup real named properties. Note that only property can be 1794 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain. 1795 LookupResult r; 1796 LookupRealNamedProperty(name, &r); 1797 if (r.IsProperty()) { 1798 return SetPropertyWithFailedAccessCheck(&r, name, value); 1799 } 1800 break; 1801 } 1802 default: { 1803 break; 1804 } 1805 } 1806 } 1807 } 1808 1809 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET); 1810 return value; 1811} 1812 1813 1814Object* JSObject::SetProperty(LookupResult* result, 1815 String* name, 1816 Object* value, 1817 PropertyAttributes attributes) { 1818 // Make sure that the top context does not change when doing callbacks or 1819 // interceptor calls. 1820 AssertNoContextChange ncc; 1821 1822 // Optimization for 2-byte strings often used as keys in a decompression 1823 // dictionary. We make these short keys into symbols to avoid constantly 1824 // reallocating them. 1825 if (!name->IsSymbol() && name->length() <= 2) { 1826 Object* symbol_version = Heap::LookupSymbol(name); 1827 if (!symbol_version->IsFailure()) name = String::cast(symbol_version); 1828 } 1829 1830 // Check access rights if needed. 1831 if (IsAccessCheckNeeded() 1832 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { 1833 return SetPropertyWithFailedAccessCheck(result, name, value); 1834 } 1835 1836 if (IsJSGlobalProxy()) { 1837 Object* proto = GetPrototype(); 1838 if (proto->IsNull()) return value; 1839 ASSERT(proto->IsJSGlobalObject()); 1840 return JSObject::cast(proto)->SetProperty(result, name, value, attributes); 1841 } 1842 1843 if (!result->IsProperty() && !IsJSContextExtensionObject()) { 1844 // We could not find a local property so let's check whether there is an 1845 // accessor that wants to handle the property. 1846 LookupResult accessor_result; 1847 LookupCallbackSetterInPrototypes(name, &accessor_result); 1848 if (accessor_result.IsProperty()) { 1849 return SetPropertyWithCallback(accessor_result.GetCallbackObject(), 1850 name, 1851 value, 1852 accessor_result.holder()); 1853 } 1854 } 1855 if (!result->IsFound()) { 1856 // Neither properties nor transitions found. 1857 return AddProperty(name, value, attributes); 1858 } 1859 if (result->IsReadOnly() && result->IsProperty()) return value; 1860 // This is a real property that is not read-only, or it is a 1861 // transition or null descriptor and there are no setters in the prototypes. 1862 switch (result->type()) { 1863 case NORMAL: 1864 return SetNormalizedProperty(result, value); 1865 case FIELD: 1866 return FastPropertyAtPut(result->GetFieldIndex(), value); 1867 case MAP_TRANSITION: 1868 if (attributes == result->GetAttributes()) { 1869 // Only use map transition if the attributes match. 1870 return AddFastPropertyUsingMap(result->GetTransitionMap(), 1871 name, 1872 value); 1873 } 1874 return ConvertDescriptorToField(name, value, attributes); 1875 case CONSTANT_FUNCTION: 1876 // Only replace the function if necessary. 1877 if (value == result->GetConstantFunction()) return value; 1878 // Preserve the attributes of this existing property. 1879 attributes = result->GetAttributes(); 1880 return ConvertDescriptorToField(name, value, attributes); 1881 case CALLBACKS: 1882 return SetPropertyWithCallback(result->GetCallbackObject(), 1883 name, 1884 value, 1885 result->holder()); 1886 case INTERCEPTOR: 1887 return SetPropertyWithInterceptor(name, value, attributes); 1888 case CONSTANT_TRANSITION: 1889 // Replace with a MAP_TRANSITION to a new map with a FIELD, even 1890 // if the value is a function. 1891 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); 1892 case NULL_DESCRIPTOR: 1893 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); 1894 default: 1895 UNREACHABLE(); 1896 } 1897 UNREACHABLE(); 1898 return value; 1899} 1900 1901 1902// Set a real local property, even if it is READ_ONLY. If the property is not 1903// present, add it with attributes NONE. This code is an exact clone of 1904// SetProperty, with the check for IsReadOnly and the check for a 1905// callback setter removed. The two lines looking up the LookupResult 1906// result are also added. If one of the functions is changed, the other 1907// should be. 1908Object* JSObject::IgnoreAttributesAndSetLocalProperty( 1909 String* name, 1910 Object* value, 1911 PropertyAttributes attributes) { 1912 // Make sure that the top context does not change when doing callbacks or 1913 // interceptor calls. 1914 AssertNoContextChange ncc; 1915 LookupResult result; 1916 LocalLookup(name, &result); 1917 // Check access rights if needed. 1918 if (IsAccessCheckNeeded() 1919 && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { 1920 return SetPropertyWithFailedAccessCheck(&result, name, value); 1921 } 1922 1923 if (IsJSGlobalProxy()) { 1924 Object* proto = GetPrototype(); 1925 if (proto->IsNull()) return value; 1926 ASSERT(proto->IsJSGlobalObject()); 1927 return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty( 1928 name, 1929 value, 1930 attributes); 1931 } 1932 1933 // Check for accessor in prototype chain removed here in clone. 1934 if (!result.IsFound()) { 1935 // Neither properties nor transitions found. 1936 return AddProperty(name, value, attributes); 1937 } 1938 PropertyDetails details = PropertyDetails(attributes, NORMAL); 1939 1940 // Check of IsReadOnly removed from here in clone. 1941 switch (result.type()) { 1942 case NORMAL: 1943 return SetNormalizedProperty(name, value, details); 1944 case FIELD: 1945 return FastPropertyAtPut(result.GetFieldIndex(), value); 1946 case MAP_TRANSITION: 1947 if (attributes == result.GetAttributes()) { 1948 // Only use map transition if the attributes match. 1949 return AddFastPropertyUsingMap(result.GetTransitionMap(), 1950 name, 1951 value); 1952 } 1953 return ConvertDescriptorToField(name, value, attributes); 1954 case CONSTANT_FUNCTION: 1955 // Only replace the function if necessary. 1956 if (value == result.GetConstantFunction()) return value; 1957 // Preserve the attributes of this existing property. 1958 attributes = result.GetAttributes(); 1959 return ConvertDescriptorToField(name, value, attributes); 1960 case CALLBACKS: 1961 case INTERCEPTOR: 1962 // Override callback in clone 1963 return ConvertDescriptorToField(name, value, attributes); 1964 case CONSTANT_TRANSITION: 1965 // Replace with a MAP_TRANSITION to a new map with a FIELD, even 1966 // if the value is a function. 1967 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); 1968 case NULL_DESCRIPTOR: 1969 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); 1970 default: 1971 UNREACHABLE(); 1972 } 1973 UNREACHABLE(); 1974 return value; 1975} 1976 1977 1978PropertyAttributes JSObject::GetPropertyAttributePostInterceptor( 1979 JSObject* receiver, 1980 String* name, 1981 bool continue_search) { 1982 // Check local property, ignore interceptor. 1983 LookupResult result; 1984 LocalLookupRealNamedProperty(name, &result); 1985 if (result.IsProperty()) return result.GetAttributes(); 1986 1987 if (continue_search) { 1988 // Continue searching via the prototype chain. 1989 Object* pt = GetPrototype(); 1990 if (pt != Heap::null_value()) { 1991 return JSObject::cast(pt)-> 1992 GetPropertyAttributeWithReceiver(receiver, name); 1993 } 1994 } 1995 return ABSENT; 1996} 1997 1998 1999PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor( 2000 JSObject* receiver, 2001 String* name, 2002 bool continue_search) { 2003 // Make sure that the top context does not change when doing 2004 // callbacks or interceptor calls. 2005 AssertNoContextChange ncc; 2006 2007 HandleScope scope; 2008 Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); 2009 Handle<JSObject> receiver_handle(receiver); 2010 Handle<JSObject> holder_handle(this); 2011 Handle<String> name_handle(name); 2012 CustomArguments args(interceptor->data(), receiver, this); 2013 v8::AccessorInfo info(args.end()); 2014 if (!interceptor->query()->IsUndefined()) { 2015 v8::NamedPropertyQuery query = 2016 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query()); 2017 LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name)); 2018 v8::Handle<v8::Boolean> result; 2019 { 2020 // Leaving JavaScript. 2021 VMState state(EXTERNAL); 2022 result = query(v8::Utils::ToLocal(name_handle), info); 2023 } 2024 if (!result.IsEmpty()) { 2025 // Convert the boolean result to a property attribute 2026 // specification. 2027 return result->IsTrue() ? NONE : ABSENT; 2028 } 2029 } else if (!interceptor->getter()->IsUndefined()) { 2030 v8::NamedPropertyGetter getter = 2031 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter()); 2032 LOG(ApiNamedPropertyAccess("interceptor-named-get-has", this, name)); 2033 v8::Handle<v8::Value> result; 2034 { 2035 // Leaving JavaScript. 2036 VMState state(EXTERNAL); 2037 result = getter(v8::Utils::ToLocal(name_handle), info); 2038 } 2039 if (!result.IsEmpty()) return NONE; 2040 } 2041 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle, 2042 *name_handle, 2043 continue_search); 2044} 2045 2046 2047PropertyAttributes JSObject::GetPropertyAttributeWithReceiver( 2048 JSObject* receiver, 2049 String* key) { 2050 uint32_t index = 0; 2051 if (key->AsArrayIndex(&index)) { 2052 if (HasElementWithReceiver(receiver, index)) return NONE; 2053 return ABSENT; 2054 } 2055 // Named property. 2056 LookupResult result; 2057 Lookup(key, &result); 2058 return GetPropertyAttribute(receiver, &result, key, true); 2059} 2060 2061 2062PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver, 2063 LookupResult* result, 2064 String* name, 2065 bool continue_search) { 2066 // Check access rights if needed. 2067 if (IsAccessCheckNeeded() && 2068 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) { 2069 return GetPropertyAttributeWithFailedAccessCheck(receiver, 2070 result, 2071 name, 2072 continue_search); 2073 } 2074 if (result->IsProperty()) { 2075 switch (result->type()) { 2076 case NORMAL: // fall through 2077 case FIELD: 2078 case CONSTANT_FUNCTION: 2079 case CALLBACKS: 2080 return result->GetAttributes(); 2081 case INTERCEPTOR: 2082 return result->holder()-> 2083 GetPropertyAttributeWithInterceptor(receiver, name, continue_search); 2084 default: 2085 UNREACHABLE(); 2086 } 2087 } 2088 return ABSENT; 2089} 2090 2091 2092PropertyAttributes JSObject::GetLocalPropertyAttribute(String* name) { 2093 // Check whether the name is an array index. 2094 uint32_t index = 0; 2095 if (name->AsArrayIndex(&index)) { 2096 if (HasLocalElement(index)) return NONE; 2097 return ABSENT; 2098 } 2099 // Named property. 2100 LookupResult result; 2101 LocalLookup(name, &result); 2102 return GetPropertyAttribute(this, &result, name, false); 2103} 2104 2105 2106Object* JSObject::NormalizeProperties(PropertyNormalizationMode mode, 2107 int expected_additional_properties) { 2108 if (!HasFastProperties()) return this; 2109 2110 // The global object is always normalized. 2111 ASSERT(!IsGlobalObject()); 2112 2113 // Allocate new content. 2114 int property_count = map()->NumberOfDescribedProperties(); 2115 if (expected_additional_properties > 0) { 2116 property_count += expected_additional_properties; 2117 } else { 2118 property_count += 2; // Make space for two more properties. 2119 } 2120 Object* obj = 2121 StringDictionary::Allocate(property_count * 2); 2122 if (obj->IsFailure()) return obj; 2123 StringDictionary* dictionary = StringDictionary::cast(obj); 2124 2125 DescriptorArray* descs = map()->instance_descriptors(); 2126 for (int i = 0; i < descs->number_of_descriptors(); i++) { 2127 PropertyDetails details = descs->GetDetails(i); 2128 switch (details.type()) { 2129 case CONSTANT_FUNCTION: { 2130 PropertyDetails d = 2131 PropertyDetails(details.attributes(), NORMAL, details.index()); 2132 Object* value = descs->GetConstantFunction(i); 2133 Object* result = dictionary->Add(descs->GetKey(i), value, d); 2134 if (result->IsFailure()) return result; 2135 dictionary = StringDictionary::cast(result); 2136 break; 2137 } 2138 case FIELD: { 2139 PropertyDetails d = 2140 PropertyDetails(details.attributes(), NORMAL, details.index()); 2141 Object* value = FastPropertyAt(descs->GetFieldIndex(i)); 2142 Object* result = dictionary->Add(descs->GetKey(i), value, d); 2143 if (result->IsFailure()) return result; 2144 dictionary = StringDictionary::cast(result); 2145 break; 2146 } 2147 case CALLBACKS: { 2148 PropertyDetails d = 2149 PropertyDetails(details.attributes(), CALLBACKS, details.index()); 2150 Object* value = descs->GetCallbacksObject(i); 2151 Object* result = dictionary->Add(descs->GetKey(i), value, d); 2152 if (result->IsFailure()) return result; 2153 dictionary = StringDictionary::cast(result); 2154 break; 2155 } 2156 case MAP_TRANSITION: 2157 case CONSTANT_TRANSITION: 2158 case NULL_DESCRIPTOR: 2159 case INTERCEPTOR: 2160 break; 2161 default: 2162 UNREACHABLE(); 2163 } 2164 } 2165 2166 // Copy the next enumeration index from instance descriptor. 2167 int index = map()->instance_descriptors()->NextEnumerationIndex(); 2168 dictionary->SetNextEnumerationIndex(index); 2169 2170 // Allocate new map. 2171 obj = map()->CopyDropDescriptors(); 2172 if (obj->IsFailure()) return obj; 2173 Map* new_map = Map::cast(obj); 2174 2175 // Clear inobject properties if needed by adjusting the instance size and 2176 // putting in a filler object instead of the inobject properties. 2177 if (mode == CLEAR_INOBJECT_PROPERTIES && map()->inobject_properties() > 0) { 2178 int instance_size_delta = map()->inobject_properties() * kPointerSize; 2179 int new_instance_size = map()->instance_size() - instance_size_delta; 2180 new_map->set_inobject_properties(0); 2181 new_map->set_instance_size(new_instance_size); 2182 Heap::CreateFillerObjectAt(this->address() + new_instance_size, 2183 instance_size_delta); 2184 } 2185 new_map->set_unused_property_fields(0); 2186 2187 // We have now successfully allocated all the necessary objects. 2188 // Changes can now be made with the guarantee that all of them take effect. 2189 set_map(new_map); 2190 map()->set_instance_descriptors(Heap::empty_descriptor_array()); 2191 2192 set_properties(dictionary); 2193 2194 Counters::props_to_dictionary.Increment(); 2195 2196#ifdef DEBUG 2197 if (FLAG_trace_normalization) { 2198 PrintF("Object properties have been normalized:\n"); 2199 Print(); 2200 } 2201#endif 2202 return this; 2203} 2204 2205 2206Object* JSObject::TransformToFastProperties(int unused_property_fields) { 2207 if (HasFastProperties()) return this; 2208 ASSERT(!IsGlobalObject()); 2209 return property_dictionary()-> 2210 TransformPropertiesToFastFor(this, unused_property_fields); 2211} 2212 2213 2214Object* JSObject::NormalizeElements() { 2215 ASSERT(!HasPixelElements() && !HasExternalArrayElements()); 2216 if (HasDictionaryElements()) return this; 2217 2218 // Get number of entries. 2219 FixedArray* array = FixedArray::cast(elements()); 2220 2221 // Compute the effective length. 2222 int length = IsJSArray() ? 2223 Smi::cast(JSArray::cast(this)->length())->value() : 2224 array->length(); 2225 Object* obj = NumberDictionary::Allocate(length); 2226 if (obj->IsFailure()) return obj; 2227 NumberDictionary* dictionary = NumberDictionary::cast(obj); 2228 // Copy entries. 2229 for (int i = 0; i < length; i++) { 2230 Object* value = array->get(i); 2231 if (!value->IsTheHole()) { 2232 PropertyDetails details = PropertyDetails(NONE, NORMAL); 2233 Object* result = dictionary->AddNumberEntry(i, array->get(i), details); 2234 if (result->IsFailure()) return result; 2235 dictionary = NumberDictionary::cast(result); 2236 } 2237 } 2238 // Switch to using the dictionary as the backing storage for elements. 2239 set_elements(dictionary); 2240 2241 Counters::elements_to_dictionary.Increment(); 2242 2243#ifdef DEBUG 2244 if (FLAG_trace_normalization) { 2245 PrintF("Object elements have been normalized:\n"); 2246 Print(); 2247 } 2248#endif 2249 2250 return this; 2251} 2252 2253 2254Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) { 2255 // Check local property, ignore interceptor. 2256 LookupResult result; 2257 LocalLookupRealNamedProperty(name, &result); 2258 if (!result.IsProperty()) return Heap::true_value(); 2259 2260 // Normalize object if needed. 2261 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 2262 if (obj->IsFailure()) return obj; 2263 2264 return DeleteNormalizedProperty(name, mode); 2265} 2266 2267 2268Object* JSObject::DeletePropertyWithInterceptor(String* name) { 2269 HandleScope scope; 2270 Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); 2271 Handle<String> name_handle(name); 2272 Handle<JSObject> this_handle(this); 2273 if (!interceptor->deleter()->IsUndefined()) { 2274 v8::NamedPropertyDeleter deleter = 2275 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter()); 2276 LOG(ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name)); 2277 CustomArguments args(interceptor->data(), this, this); 2278 v8::AccessorInfo info(args.end()); 2279 v8::Handle<v8::Boolean> result; 2280 { 2281 // Leaving JavaScript. 2282 VMState state(EXTERNAL); 2283 result = deleter(v8::Utils::ToLocal(name_handle), info); 2284 } 2285 RETURN_IF_SCHEDULED_EXCEPTION(); 2286 if (!result.IsEmpty()) { 2287 ASSERT(result->IsBoolean()); 2288 return *v8::Utils::OpenHandle(*result); 2289 } 2290 } 2291 Object* raw_result = 2292 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION); 2293 RETURN_IF_SCHEDULED_EXCEPTION(); 2294 return raw_result; 2295} 2296 2297 2298Object* JSObject::DeleteElementPostInterceptor(uint32_t index, 2299 DeleteMode mode) { 2300 ASSERT(!HasPixelElements() && !HasExternalArrayElements()); 2301 switch (GetElementsKind()) { 2302 case FAST_ELEMENTS: { 2303 uint32_t length = IsJSArray() ? 2304 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) : 2305 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 2306 if (index < length) { 2307 FixedArray::cast(elements())->set_the_hole(index); 2308 } 2309 break; 2310 } 2311 case DICTIONARY_ELEMENTS: { 2312 NumberDictionary* dictionary = element_dictionary(); 2313 int entry = dictionary->FindEntry(index); 2314 if (entry != NumberDictionary::kNotFound) { 2315 return dictionary->DeleteProperty(entry, mode); 2316 } 2317 break; 2318 } 2319 default: 2320 UNREACHABLE(); 2321 break; 2322 } 2323 return Heap::true_value(); 2324} 2325 2326 2327Object* JSObject::DeleteElementWithInterceptor(uint32_t index) { 2328 // Make sure that the top context does not change when doing 2329 // callbacks or interceptor calls. 2330 AssertNoContextChange ncc; 2331 HandleScope scope; 2332 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 2333 if (interceptor->deleter()->IsUndefined()) return Heap::false_value(); 2334 v8::IndexedPropertyDeleter deleter = 2335 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter()); 2336 Handle<JSObject> this_handle(this); 2337 LOG(ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index)); 2338 CustomArguments args(interceptor->data(), this, this); 2339 v8::AccessorInfo info(args.end()); 2340 v8::Handle<v8::Boolean> result; 2341 { 2342 // Leaving JavaScript. 2343 VMState state(EXTERNAL); 2344 result = deleter(index, info); 2345 } 2346 RETURN_IF_SCHEDULED_EXCEPTION(); 2347 if (!result.IsEmpty()) { 2348 ASSERT(result->IsBoolean()); 2349 return *v8::Utils::OpenHandle(*result); 2350 } 2351 Object* raw_result = 2352 this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION); 2353 RETURN_IF_SCHEDULED_EXCEPTION(); 2354 return raw_result; 2355} 2356 2357 2358Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { 2359 // Check access rights if needed. 2360 if (IsAccessCheckNeeded() && 2361 !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) { 2362 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE); 2363 return Heap::false_value(); 2364 } 2365 2366 if (IsJSGlobalProxy()) { 2367 Object* proto = GetPrototype(); 2368 if (proto->IsNull()) return Heap::false_value(); 2369 ASSERT(proto->IsJSGlobalObject()); 2370 return JSGlobalObject::cast(proto)->DeleteElement(index, mode); 2371 } 2372 2373 if (HasIndexedInterceptor()) { 2374 // Skip interceptor if forcing deletion. 2375 if (mode == FORCE_DELETION) { 2376 return DeleteElementPostInterceptor(index, mode); 2377 } 2378 return DeleteElementWithInterceptor(index); 2379 } 2380 2381 switch (GetElementsKind()) { 2382 case FAST_ELEMENTS: { 2383 uint32_t length = IsJSArray() ? 2384 static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) : 2385 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 2386 if (index < length) { 2387 FixedArray::cast(elements())->set_the_hole(index); 2388 } 2389 break; 2390 } 2391 case PIXEL_ELEMENTS: 2392 case EXTERNAL_BYTE_ELEMENTS: 2393 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 2394 case EXTERNAL_SHORT_ELEMENTS: 2395 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 2396 case EXTERNAL_INT_ELEMENTS: 2397 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 2398 case EXTERNAL_FLOAT_ELEMENTS: 2399 // Pixel and external array elements cannot be deleted. Just 2400 // silently ignore here. 2401 break; 2402 case DICTIONARY_ELEMENTS: { 2403 NumberDictionary* dictionary = element_dictionary(); 2404 int entry = dictionary->FindEntry(index); 2405 if (entry != NumberDictionary::kNotFound) { 2406 return dictionary->DeleteProperty(entry, mode); 2407 } 2408 break; 2409 } 2410 default: 2411 UNREACHABLE(); 2412 break; 2413 } 2414 return Heap::true_value(); 2415} 2416 2417 2418Object* JSObject::DeleteProperty(String* name, DeleteMode mode) { 2419 // ECMA-262, 3rd, 8.6.2.5 2420 ASSERT(name->IsString()); 2421 2422 // Check access rights if needed. 2423 if (IsAccessCheckNeeded() && 2424 !Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) { 2425 Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE); 2426 return Heap::false_value(); 2427 } 2428 2429 if (IsJSGlobalProxy()) { 2430 Object* proto = GetPrototype(); 2431 if (proto->IsNull()) return Heap::false_value(); 2432 ASSERT(proto->IsJSGlobalObject()); 2433 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode); 2434 } 2435 2436 uint32_t index = 0; 2437 if (name->AsArrayIndex(&index)) { 2438 return DeleteElement(index, mode); 2439 } else { 2440 LookupResult result; 2441 LocalLookup(name, &result); 2442 if (!result.IsProperty()) return Heap::true_value(); 2443 // Ignore attributes if forcing a deletion. 2444 if (result.IsDontDelete() && mode != FORCE_DELETION) { 2445 return Heap::false_value(); 2446 } 2447 // Check for interceptor. 2448 if (result.type() == INTERCEPTOR) { 2449 // Skip interceptor if forcing a deletion. 2450 if (mode == FORCE_DELETION) { 2451 return DeletePropertyPostInterceptor(name, mode); 2452 } 2453 return DeletePropertyWithInterceptor(name); 2454 } 2455 // Normalize object if needed. 2456 Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 2457 if (obj->IsFailure()) return obj; 2458 // Make sure the properties are normalized before removing the entry. 2459 return DeleteNormalizedProperty(name, mode); 2460 } 2461} 2462 2463 2464// Check whether this object references another object. 2465bool JSObject::ReferencesObject(Object* obj) { 2466 AssertNoAllocation no_alloc; 2467 2468 // Is the object the constructor for this object? 2469 if (map()->constructor() == obj) { 2470 return true; 2471 } 2472 2473 // Is the object the prototype for this object? 2474 if (map()->prototype() == obj) { 2475 return true; 2476 } 2477 2478 // Check if the object is among the named properties. 2479 Object* key = SlowReverseLookup(obj); 2480 if (key != Heap::undefined_value()) { 2481 return true; 2482 } 2483 2484 // Check if the object is among the indexed properties. 2485 switch (GetElementsKind()) { 2486 case PIXEL_ELEMENTS: 2487 case EXTERNAL_BYTE_ELEMENTS: 2488 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 2489 case EXTERNAL_SHORT_ELEMENTS: 2490 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 2491 case EXTERNAL_INT_ELEMENTS: 2492 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 2493 case EXTERNAL_FLOAT_ELEMENTS: 2494 // Raw pixels and external arrays do not reference other 2495 // objects. 2496 break; 2497 case FAST_ELEMENTS: { 2498 int length = IsJSArray() ? 2499 Smi::cast(JSArray::cast(this)->length())->value() : 2500 FixedArray::cast(elements())->length(); 2501 for (int i = 0; i < length; i++) { 2502 Object* element = FixedArray::cast(elements())->get(i); 2503 if (!element->IsTheHole() && element == obj) { 2504 return true; 2505 } 2506 } 2507 break; 2508 } 2509 case DICTIONARY_ELEMENTS: { 2510 key = element_dictionary()->SlowReverseLookup(obj); 2511 if (key != Heap::undefined_value()) { 2512 return true; 2513 } 2514 break; 2515 } 2516 default: 2517 UNREACHABLE(); 2518 break; 2519 } 2520 2521 // For functions check the context. Boilerplate functions do 2522 // not have to be traversed since they have no real context. 2523 if (IsJSFunction() && !JSFunction::cast(this)->IsBoilerplate()) { 2524 // Get the constructor function for arguments array. 2525 JSObject* arguments_boilerplate = 2526 Top::context()->global_context()->arguments_boilerplate(); 2527 JSFunction* arguments_function = 2528 JSFunction::cast(arguments_boilerplate->map()->constructor()); 2529 2530 // Get the context and don't check if it is the global context. 2531 JSFunction* f = JSFunction::cast(this); 2532 Context* context = f->context(); 2533 if (context->IsGlobalContext()) { 2534 return false; 2535 } 2536 2537 // Check the non-special context slots. 2538 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) { 2539 // Only check JS objects. 2540 if (context->get(i)->IsJSObject()) { 2541 JSObject* ctxobj = JSObject::cast(context->get(i)); 2542 // If it is an arguments array check the content. 2543 if (ctxobj->map()->constructor() == arguments_function) { 2544 if (ctxobj->ReferencesObject(obj)) { 2545 return true; 2546 } 2547 } else if (ctxobj == obj) { 2548 return true; 2549 } 2550 } 2551 } 2552 2553 // Check the context extension if any. 2554 if (context->has_extension()) { 2555 return context->extension()->ReferencesObject(obj); 2556 } 2557 } 2558 2559 // No references to object. 2560 return false; 2561} 2562 2563 2564// Tests for the fast common case for property enumeration: 2565// - This object and all prototypes has an enum cache (which means that it has 2566// no interceptors and needs no access checks). 2567// - This object has no elements. 2568// - No prototype has enumerable properties/elements. 2569bool JSObject::IsSimpleEnum() { 2570 for (Object* o = this; 2571 o != Heap::null_value(); 2572 o = JSObject::cast(o)->GetPrototype()) { 2573 JSObject* curr = JSObject::cast(o); 2574 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false; 2575 ASSERT(!curr->HasNamedInterceptor()); 2576 ASSERT(!curr->HasIndexedInterceptor()); 2577 ASSERT(!curr->IsAccessCheckNeeded()); 2578 if (curr->NumberOfEnumElements() > 0) return false; 2579 if (curr != this) { 2580 FixedArray* curr_fixed_array = 2581 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache()); 2582 if (curr_fixed_array->length() > 0) return false; 2583 } 2584 } 2585 return true; 2586} 2587 2588 2589int Map::NumberOfDescribedProperties() { 2590 int result = 0; 2591 DescriptorArray* descs = instance_descriptors(); 2592 for (int i = 0; i < descs->number_of_descriptors(); i++) { 2593 if (descs->IsProperty(i)) result++; 2594 } 2595 return result; 2596} 2597 2598 2599int Map::PropertyIndexFor(String* name) { 2600 DescriptorArray* descs = instance_descriptors(); 2601 for (int i = 0; i < descs->number_of_descriptors(); i++) { 2602 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) { 2603 return descs->GetFieldIndex(i); 2604 } 2605 } 2606 return -1; 2607} 2608 2609 2610int Map::NextFreePropertyIndex() { 2611 int max_index = -1; 2612 DescriptorArray* descs = instance_descriptors(); 2613 for (int i = 0; i < descs->number_of_descriptors(); i++) { 2614 if (descs->GetType(i) == FIELD) { 2615 int current_index = descs->GetFieldIndex(i); 2616 if (current_index > max_index) max_index = current_index; 2617 } 2618 } 2619 return max_index + 1; 2620} 2621 2622 2623AccessorDescriptor* Map::FindAccessor(String* name) { 2624 DescriptorArray* descs = instance_descriptors(); 2625 for (int i = 0; i < descs->number_of_descriptors(); i++) { 2626 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) { 2627 return descs->GetCallbacks(i); 2628 } 2629 } 2630 return NULL; 2631} 2632 2633 2634void JSObject::LocalLookup(String* name, LookupResult* result) { 2635 ASSERT(name->IsString()); 2636 2637 if (IsJSGlobalProxy()) { 2638 Object* proto = GetPrototype(); 2639 if (proto->IsNull()) return result->NotFound(); 2640 ASSERT(proto->IsJSGlobalObject()); 2641 return JSObject::cast(proto)->LocalLookup(name, result); 2642 } 2643 2644 // Do not use inline caching if the object is a non-global object 2645 // that requires access checks. 2646 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) { 2647 result->DisallowCaching(); 2648 } 2649 2650 // Check __proto__ before interceptor. 2651 if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) { 2652 result->ConstantResult(this); 2653 return; 2654 } 2655 2656 // Check for lookup interceptor except when bootstrapping. 2657 if (HasNamedInterceptor() && !Bootstrapper::IsActive()) { 2658 result->InterceptorResult(this); 2659 return; 2660 } 2661 2662 LocalLookupRealNamedProperty(name, result); 2663} 2664 2665 2666void JSObject::Lookup(String* name, LookupResult* result) { 2667 // Ecma-262 3rd 8.6.2.4 2668 for (Object* current = this; 2669 current != Heap::null_value(); 2670 current = JSObject::cast(current)->GetPrototype()) { 2671 JSObject::cast(current)->LocalLookup(name, result); 2672 if (result->IsProperty()) return; 2673 } 2674 result->NotFound(); 2675} 2676 2677 2678// Search object and it's prototype chain for callback properties. 2679void JSObject::LookupCallback(String* name, LookupResult* result) { 2680 for (Object* current = this; 2681 current != Heap::null_value(); 2682 current = JSObject::cast(current)->GetPrototype()) { 2683 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result); 2684 if (result->IsProperty() && result->type() == CALLBACKS) return; 2685 } 2686 result->NotFound(); 2687} 2688 2689 2690Object* JSObject::DefineGetterSetter(String* name, 2691 PropertyAttributes attributes) { 2692 // Make sure that the top context does not change when doing callbacks or 2693 // interceptor calls. 2694 AssertNoContextChange ncc; 2695 2696 // Check access rights if needed. 2697 if (IsAccessCheckNeeded() && 2698 !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { 2699 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET); 2700 return Heap::undefined_value(); 2701 } 2702 2703 // Try to flatten before operating on the string. 2704 name->TryFlattenIfNotFlat(); 2705 2706 // Check if there is an API defined callback object which prohibits 2707 // callback overwriting in this object or it's prototype chain. 2708 // This mechanism is needed for instance in a browser setting, where 2709 // certain accessors such as window.location should not be allowed 2710 // to be overwritten because allowing overwriting could potentially 2711 // cause security problems. 2712 LookupResult callback_result; 2713 LookupCallback(name, &callback_result); 2714 if (callback_result.IsFound()) { 2715 Object* obj = callback_result.GetCallbackObject(); 2716 if (obj->IsAccessorInfo() && 2717 AccessorInfo::cast(obj)->prohibits_overwriting()) { 2718 return Heap::undefined_value(); 2719 } 2720 } 2721 2722 uint32_t index; 2723 bool is_element = name->AsArrayIndex(&index); 2724 if (is_element && IsJSArray()) return Heap::undefined_value(); 2725 2726 if (is_element) { 2727 switch (GetElementsKind()) { 2728 case FAST_ELEMENTS: 2729 break; 2730 case PIXEL_ELEMENTS: 2731 case EXTERNAL_BYTE_ELEMENTS: 2732 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 2733 case EXTERNAL_SHORT_ELEMENTS: 2734 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 2735 case EXTERNAL_INT_ELEMENTS: 2736 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 2737 case EXTERNAL_FLOAT_ELEMENTS: 2738 // Ignore getters and setters on pixel and external array 2739 // elements. 2740 return Heap::undefined_value(); 2741 case DICTIONARY_ELEMENTS: { 2742 // Lookup the index. 2743 NumberDictionary* dictionary = element_dictionary(); 2744 int entry = dictionary->FindEntry(index); 2745 if (entry != NumberDictionary::kNotFound) { 2746 Object* result = dictionary->ValueAt(entry); 2747 PropertyDetails details = dictionary->DetailsAt(entry); 2748 if (details.IsReadOnly()) return Heap::undefined_value(); 2749 if (details.type() == CALLBACKS) { 2750 // Only accessors allowed as elements. 2751 ASSERT(result->IsFixedArray()); 2752 return result; 2753 } 2754 } 2755 break; 2756 } 2757 default: 2758 UNREACHABLE(); 2759 break; 2760 } 2761 } else { 2762 // Lookup the name. 2763 LookupResult result; 2764 LocalLookup(name, &result); 2765 if (result.IsProperty()) { 2766 if (result.IsReadOnly()) return Heap::undefined_value(); 2767 if (result.type() == CALLBACKS) { 2768 Object* obj = result.GetCallbackObject(); 2769 if (obj->IsFixedArray()) { 2770 // The object might be in fast mode even though it has 2771 // a getter/setter. 2772 Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 2773 if (ok->IsFailure()) return ok; 2774 2775 PropertyDetails details = PropertyDetails(attributes, CALLBACKS); 2776 SetNormalizedProperty(name, obj, details); 2777 return obj; 2778 } 2779 } 2780 } 2781 } 2782 2783 // Allocate the fixed array to hold getter and setter. 2784 Object* structure = Heap::AllocateFixedArray(2, TENURED); 2785 if (structure->IsFailure()) return structure; 2786 PropertyDetails details = PropertyDetails(attributes, CALLBACKS); 2787 2788 if (is_element) { 2789 // Normalize object to make this operation simple. 2790 Object* ok = NormalizeElements(); 2791 if (ok->IsFailure()) return ok; 2792 2793 // Update the dictionary with the new CALLBACKS property. 2794 Object* dict = 2795 element_dictionary()->Set(index, structure, details); 2796 if (dict->IsFailure()) return dict; 2797 2798 // If name is an index we need to stay in slow case. 2799 NumberDictionary* elements = NumberDictionary::cast(dict); 2800 elements->set_requires_slow_elements(); 2801 // Set the potential new dictionary on the object. 2802 set_elements(NumberDictionary::cast(dict)); 2803 } else { 2804 // Normalize object to make this operation simple. 2805 Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 2806 if (ok->IsFailure()) return ok; 2807 2808 // For the global object allocate a new map to invalidate the global inline 2809 // caches which have a global property cell reference directly in the code. 2810 if (IsGlobalObject()) { 2811 Object* new_map = map()->CopyDropDescriptors(); 2812 if (new_map->IsFailure()) return new_map; 2813 set_map(Map::cast(new_map)); 2814 } 2815 2816 // Update the dictionary with the new CALLBACKS property. 2817 return SetNormalizedProperty(name, structure, details); 2818 } 2819 2820 return structure; 2821} 2822 2823 2824Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun, 2825 PropertyAttributes attributes) { 2826 // Check access rights if needed. 2827 if (IsAccessCheckNeeded() && 2828 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) { 2829 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS); 2830 return Heap::undefined_value(); 2831 } 2832 2833 if (IsJSGlobalProxy()) { 2834 Object* proto = GetPrototype(); 2835 if (proto->IsNull()) return this; 2836 ASSERT(proto->IsJSGlobalObject()); 2837 return JSObject::cast(proto)->DefineAccessor(name, is_getter, 2838 fun, attributes); 2839 } 2840 2841 Object* array = DefineGetterSetter(name, attributes); 2842 if (array->IsFailure() || array->IsUndefined()) return array; 2843 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun); 2844 return this; 2845} 2846 2847 2848Object* JSObject::LookupAccessor(String* name, bool is_getter) { 2849 // Make sure that the top context does not change when doing callbacks or 2850 // interceptor calls. 2851 AssertNoContextChange ncc; 2852 2853 // Check access rights if needed. 2854 if (IsAccessCheckNeeded() && 2855 !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) { 2856 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS); 2857 return Heap::undefined_value(); 2858 } 2859 2860 // Make the lookup and include prototypes. 2861 int accessor_index = is_getter ? kGetterIndex : kSetterIndex; 2862 uint32_t index; 2863 if (name->AsArrayIndex(&index)) { 2864 for (Object* obj = this; 2865 obj != Heap::null_value(); 2866 obj = JSObject::cast(obj)->GetPrototype()) { 2867 JSObject* js_object = JSObject::cast(obj); 2868 if (js_object->HasDictionaryElements()) { 2869 NumberDictionary* dictionary = js_object->element_dictionary(); 2870 int entry = dictionary->FindEntry(index); 2871 if (entry != NumberDictionary::kNotFound) { 2872 Object* element = dictionary->ValueAt(entry); 2873 PropertyDetails details = dictionary->DetailsAt(entry); 2874 if (details.type() == CALLBACKS) { 2875 // Only accessors allowed as elements. 2876 return FixedArray::cast(element)->get(accessor_index); 2877 } 2878 } 2879 } 2880 } 2881 } else { 2882 for (Object* obj = this; 2883 obj != Heap::null_value(); 2884 obj = JSObject::cast(obj)->GetPrototype()) { 2885 LookupResult result; 2886 JSObject::cast(obj)->LocalLookup(name, &result); 2887 if (result.IsProperty()) { 2888 if (result.IsReadOnly()) return Heap::undefined_value(); 2889 if (result.type() == CALLBACKS) { 2890 Object* obj = result.GetCallbackObject(); 2891 if (obj->IsFixedArray()) { 2892 return FixedArray::cast(obj)->get(accessor_index); 2893 } 2894 } 2895 } 2896 } 2897 } 2898 return Heap::undefined_value(); 2899} 2900 2901 2902Object* JSObject::SlowReverseLookup(Object* value) { 2903 if (HasFastProperties()) { 2904 DescriptorArray* descs = map()->instance_descriptors(); 2905 for (int i = 0; i < descs->number_of_descriptors(); i++) { 2906 if (descs->GetType(i) == FIELD) { 2907 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) { 2908 return descs->GetKey(i); 2909 } 2910 } else if (descs->GetType(i) == CONSTANT_FUNCTION) { 2911 if (descs->GetConstantFunction(i) == value) { 2912 return descs->GetKey(i); 2913 } 2914 } 2915 } 2916 return Heap::undefined_value(); 2917 } else { 2918 return property_dictionary()->SlowReverseLookup(value); 2919 } 2920} 2921 2922 2923Object* Map::CopyDropDescriptors() { 2924 Object* result = Heap::AllocateMap(instance_type(), instance_size()); 2925 if (result->IsFailure()) return result; 2926 Map::cast(result)->set_prototype(prototype()); 2927 Map::cast(result)->set_constructor(constructor()); 2928 // Don't copy descriptors, so map transitions always remain a forest. 2929 // If we retained the same descriptors we would have two maps 2930 // pointing to the same transition which is bad because the garbage 2931 // collector relies on being able to reverse pointers from transitions 2932 // to maps. If properties need to be retained use CopyDropTransitions. 2933 Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array()); 2934 // Please note instance_type and instance_size are set when allocated. 2935 Map::cast(result)->set_inobject_properties(inobject_properties()); 2936 Map::cast(result)->set_unused_property_fields(unused_property_fields()); 2937 2938 // If the map has pre-allocated properties always start out with a descriptor 2939 // array describing these properties. 2940 if (pre_allocated_property_fields() > 0) { 2941 ASSERT(constructor()->IsJSFunction()); 2942 JSFunction* ctor = JSFunction::cast(constructor()); 2943 Object* descriptors = 2944 ctor->initial_map()->instance_descriptors()->RemoveTransitions(); 2945 if (descriptors->IsFailure()) return descriptors; 2946 Map::cast(result)->set_instance_descriptors( 2947 DescriptorArray::cast(descriptors)); 2948 Map::cast(result)->set_pre_allocated_property_fields( 2949 pre_allocated_property_fields()); 2950 } 2951 Map::cast(result)->set_bit_field(bit_field()); 2952 Map::cast(result)->set_bit_field2(bit_field2()); 2953 Map::cast(result)->ClearCodeCache(); 2954 return result; 2955} 2956 2957 2958Object* Map::CopyDropTransitions() { 2959 Object* new_map = CopyDropDescriptors(); 2960 if (new_map->IsFailure()) return new_map; 2961 Object* descriptors = instance_descriptors()->RemoveTransitions(); 2962 if (descriptors->IsFailure()) return descriptors; 2963 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors)); 2964 return cast(new_map); 2965} 2966 2967 2968Object* Map::UpdateCodeCache(String* name, Code* code) { 2969 ASSERT(code->ic_state() == MONOMORPHIC); 2970 FixedArray* cache = code_cache(); 2971 2972 // When updating the code cache we disregard the type encoded in the 2973 // flags. This allows call constant stubs to overwrite call field 2974 // stubs, etc. 2975 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags()); 2976 2977 // First check whether we can update existing code cache without 2978 // extending it. 2979 int length = cache->length(); 2980 int deleted_index = -1; 2981 for (int i = 0; i < length; i += 2) { 2982 Object* key = cache->get(i); 2983 if (key->IsNull()) { 2984 if (deleted_index < 0) deleted_index = i; 2985 continue; 2986 } 2987 if (key->IsUndefined()) { 2988 if (deleted_index >= 0) i = deleted_index; 2989 cache->set(i + 0, name); 2990 cache->set(i + 1, code); 2991 return this; 2992 } 2993 if (name->Equals(String::cast(key))) { 2994 Code::Flags found = Code::cast(cache->get(i + 1))->flags(); 2995 if (Code::RemoveTypeFromFlags(found) == flags) { 2996 cache->set(i + 1, code); 2997 return this; 2998 } 2999 } 3000 } 3001 3002 // Reached the end of the code cache. If there were deleted 3003 // elements, reuse the space for the first of them. 3004 if (deleted_index >= 0) { 3005 cache->set(deleted_index + 0, name); 3006 cache->set(deleted_index + 1, code); 3007 return this; 3008 } 3009 3010 // Extend the code cache with some new entries (at least one). 3011 int new_length = length + ((length >> 1) & ~1) + 2; 3012 ASSERT((new_length & 1) == 0); // must be a multiple of two 3013 Object* result = cache->CopySize(new_length); 3014 if (result->IsFailure()) return result; 3015 3016 // Add the (name, code) pair to the new cache. 3017 cache = FixedArray::cast(result); 3018 cache->set(length + 0, name); 3019 cache->set(length + 1, code); 3020 set_code_cache(cache); 3021 return this; 3022} 3023 3024 3025Object* Map::FindInCodeCache(String* name, Code::Flags flags) { 3026 FixedArray* cache = code_cache(); 3027 int length = cache->length(); 3028 for (int i = 0; i < length; i += 2) { 3029 Object* key = cache->get(i); 3030 // Skip deleted elements. 3031 if (key->IsNull()) continue; 3032 if (key->IsUndefined()) return key; 3033 if (name->Equals(String::cast(key))) { 3034 Code* code = Code::cast(cache->get(i + 1)); 3035 if (code->flags() == flags) return code; 3036 } 3037 } 3038 return Heap::undefined_value(); 3039} 3040 3041 3042int Map::IndexInCodeCache(Code* code) { 3043 FixedArray* array = code_cache(); 3044 int len = array->length(); 3045 for (int i = 0; i < len; i += 2) { 3046 if (array->get(i + 1) == code) return i + 1; 3047 } 3048 return -1; 3049} 3050 3051 3052void Map::RemoveFromCodeCache(int index) { 3053 FixedArray* array = code_cache(); 3054 ASSERT(array->length() >= index && array->get(index)->IsCode()); 3055 // Use null instead of undefined for deleted elements to distinguish 3056 // deleted elements from unused elements. This distinction is used 3057 // when looking up in the cache and when updating the cache. 3058 array->set_null(index - 1); // key 3059 array->set_null(index); // code 3060} 3061 3062 3063void FixedArray::FixedArrayIterateBody(ObjectVisitor* v) { 3064 IteratePointers(v, kHeaderSize, kHeaderSize + length() * kPointerSize); 3065} 3066 3067 3068static bool HasKey(FixedArray* array, Object* key) { 3069 int len0 = array->length(); 3070 for (int i = 0; i < len0; i++) { 3071 Object* element = array->get(i); 3072 if (element->IsSmi() && key->IsSmi() && (element == key)) return true; 3073 if (element->IsString() && 3074 key->IsString() && String::cast(element)->Equals(String::cast(key))) { 3075 return true; 3076 } 3077 } 3078 return false; 3079} 3080 3081 3082Object* FixedArray::AddKeysFromJSArray(JSArray* array) { 3083 ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements()); 3084 switch (array->GetElementsKind()) { 3085 case JSObject::FAST_ELEMENTS: 3086 return UnionOfKeys(FixedArray::cast(array->elements())); 3087 case JSObject::DICTIONARY_ELEMENTS: { 3088 NumberDictionary* dict = array->element_dictionary(); 3089 int size = dict->NumberOfElements(); 3090 3091 // Allocate a temporary fixed array. 3092 Object* object = Heap::AllocateFixedArray(size); 3093 if (object->IsFailure()) return object; 3094 FixedArray* key_array = FixedArray::cast(object); 3095 3096 int capacity = dict->Capacity(); 3097 int pos = 0; 3098 // Copy the elements from the JSArray to the temporary fixed array. 3099 for (int i = 0; i < capacity; i++) { 3100 if (dict->IsKey(dict->KeyAt(i))) { 3101 key_array->set(pos++, dict->ValueAt(i)); 3102 } 3103 } 3104 // Compute the union of this and the temporary fixed array. 3105 return UnionOfKeys(key_array); 3106 } 3107 default: 3108 UNREACHABLE(); 3109 } 3110 UNREACHABLE(); 3111 return Heap::null_value(); // Failure case needs to "return" a value. 3112} 3113 3114 3115Object* FixedArray::UnionOfKeys(FixedArray* other) { 3116 int len0 = length(); 3117 int len1 = other->length(); 3118 // Optimize if either is empty. 3119 if (len0 == 0) return other; 3120 if (len1 == 0) return this; 3121 3122 // Compute how many elements are not in this. 3123 int extra = 0; 3124 for (int y = 0; y < len1; y++) { 3125 Object* value = other->get(y); 3126 if (!value->IsTheHole() && !HasKey(this, value)) extra++; 3127 } 3128 3129 if (extra == 0) return this; 3130 3131 // Allocate the result 3132 Object* obj = Heap::AllocateFixedArray(len0 + extra); 3133 if (obj->IsFailure()) return obj; 3134 // Fill in the content 3135 AssertNoAllocation no_gc; 3136 FixedArray* result = FixedArray::cast(obj); 3137 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); 3138 for (int i = 0; i < len0; i++) { 3139 result->set(i, get(i), mode); 3140 } 3141 // Fill in the extra keys. 3142 int index = 0; 3143 for (int y = 0; y < len1; y++) { 3144 Object* value = other->get(y); 3145 if (!value->IsTheHole() && !HasKey(this, value)) { 3146 result->set(len0 + index, other->get(y), mode); 3147 index++; 3148 } 3149 } 3150 ASSERT(extra == index); 3151 return result; 3152} 3153 3154 3155Object* FixedArray::CopySize(int new_length) { 3156 if (new_length == 0) return Heap::empty_fixed_array(); 3157 Object* obj = Heap::AllocateFixedArray(new_length); 3158 if (obj->IsFailure()) return obj; 3159 FixedArray* result = FixedArray::cast(obj); 3160 // Copy the content 3161 AssertNoAllocation no_gc; 3162 int len = length(); 3163 if (new_length < len) len = new_length; 3164 result->set_map(map()); 3165 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); 3166 for (int i = 0; i < len; i++) { 3167 result->set(i, get(i), mode); 3168 } 3169 return result; 3170} 3171 3172 3173void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) { 3174 AssertNoAllocation no_gc; 3175 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc); 3176 for (int index = 0; index < len; index++) { 3177 dest->set(dest_pos+index, get(pos+index), mode); 3178 } 3179} 3180 3181 3182#ifdef DEBUG 3183bool FixedArray::IsEqualTo(FixedArray* other) { 3184 if (length() != other->length()) return false; 3185 for (int i = 0 ; i < length(); ++i) { 3186 if (get(i) != other->get(i)) return false; 3187 } 3188 return true; 3189} 3190#endif 3191 3192 3193Object* DescriptorArray::Allocate(int number_of_descriptors) { 3194 if (number_of_descriptors == 0) { 3195 return Heap::empty_descriptor_array(); 3196 } 3197 // Allocate the array of keys. 3198 Object* array = 3199 Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors)); 3200 if (array->IsFailure()) return array; 3201 // Do not use DescriptorArray::cast on incomplete object. 3202 FixedArray* result = FixedArray::cast(array); 3203 3204 // Allocate the content array and set it in the descriptor array. 3205 array = Heap::AllocateFixedArray(number_of_descriptors << 1); 3206 if (array->IsFailure()) return array; 3207 result->set(kContentArrayIndex, array); 3208 result->set(kEnumerationIndexIndex, 3209 Smi::FromInt(PropertyDetails::kInitialIndex)); 3210 return result; 3211} 3212 3213 3214void DescriptorArray::SetEnumCache(FixedArray* bridge_storage, 3215 FixedArray* new_cache) { 3216 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength); 3217 if (HasEnumCache()) { 3218 FixedArray::cast(get(kEnumerationIndexIndex))-> 3219 set(kEnumCacheBridgeCacheIndex, new_cache); 3220 } else { 3221 if (IsEmpty()) return; // Do nothing for empty descriptor array. 3222 FixedArray::cast(bridge_storage)-> 3223 set(kEnumCacheBridgeCacheIndex, new_cache); 3224 fast_set(FixedArray::cast(bridge_storage), 3225 kEnumCacheBridgeEnumIndex, 3226 get(kEnumerationIndexIndex)); 3227 set(kEnumerationIndexIndex, bridge_storage); 3228 } 3229} 3230 3231 3232Object* DescriptorArray::CopyInsert(Descriptor* descriptor, 3233 TransitionFlag transition_flag) { 3234 // Transitions are only kept when inserting another transition. 3235 // This precondition is not required by this function's implementation, but 3236 // is currently required by the semantics of maps, so we check it. 3237 // Conversely, we filter after replacing, so replacing a transition and 3238 // removing all other transitions is not supported. 3239 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS; 3240 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition()); 3241 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR); 3242 3243 // Ensure the key is a symbol. 3244 Object* result = descriptor->KeyToSymbol(); 3245 if (result->IsFailure()) return result; 3246 3247 int transitions = 0; 3248 int null_descriptors = 0; 3249 if (remove_transitions) { 3250 for (int i = 0; i < number_of_descriptors(); i++) { 3251 if (IsTransition(i)) transitions++; 3252 if (IsNullDescriptor(i)) null_descriptors++; 3253 } 3254 } else { 3255 for (int i = 0; i < number_of_descriptors(); i++) { 3256 if (IsNullDescriptor(i)) null_descriptors++; 3257 } 3258 } 3259 int new_size = number_of_descriptors() - transitions - null_descriptors; 3260 3261 // If key is in descriptor, we replace it in-place when filtering. 3262 // Count a null descriptor for key as inserted, not replaced. 3263 int index = Search(descriptor->GetKey()); 3264 const bool inserting = (index == kNotFound); 3265 const bool replacing = !inserting; 3266 bool keep_enumeration_index = false; 3267 if (inserting) { 3268 ++new_size; 3269 } 3270 if (replacing) { 3271 // We are replacing an existing descriptor. We keep the enumeration 3272 // index of a visible property. 3273 PropertyType t = PropertyDetails(GetDetails(index)).type(); 3274 if (t == CONSTANT_FUNCTION || 3275 t == FIELD || 3276 t == CALLBACKS || 3277 t == INTERCEPTOR) { 3278 keep_enumeration_index = true; 3279 } else if (remove_transitions) { 3280 // Replaced descriptor has been counted as removed if it is 3281 // a transition that will be replaced. Adjust count in this case. 3282 ++new_size; 3283 } 3284 } 3285 result = Allocate(new_size); 3286 if (result->IsFailure()) return result; 3287 DescriptorArray* new_descriptors = DescriptorArray::cast(result); 3288 // Set the enumeration index in the descriptors and set the enumeration index 3289 // in the result. 3290 int enumeration_index = NextEnumerationIndex(); 3291 if (!descriptor->GetDetails().IsTransition()) { 3292 if (keep_enumeration_index) { 3293 descriptor->SetEnumerationIndex( 3294 PropertyDetails(GetDetails(index)).index()); 3295 } else { 3296 descriptor->SetEnumerationIndex(enumeration_index); 3297 ++enumeration_index; 3298 } 3299 } 3300 new_descriptors->SetNextEnumerationIndex(enumeration_index); 3301 3302 // Copy the descriptors, filtering out transitions and null descriptors, 3303 // and inserting or replacing a descriptor. 3304 uint32_t descriptor_hash = descriptor->GetKey()->Hash(); 3305 int from_index = 0; 3306 int to_index = 0; 3307 3308 for (; from_index < number_of_descriptors(); from_index++) { 3309 String* key = GetKey(from_index); 3310 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) { 3311 break; 3312 } 3313 if (IsNullDescriptor(from_index)) continue; 3314 if (remove_transitions && IsTransition(from_index)) continue; 3315 new_descriptors->CopyFrom(to_index++, this, from_index); 3316 } 3317 3318 new_descriptors->Set(to_index++, descriptor); 3319 if (replacing) from_index++; 3320 3321 for (; from_index < number_of_descriptors(); from_index++) { 3322 if (IsNullDescriptor(from_index)) continue; 3323 if (remove_transitions && IsTransition(from_index)) continue; 3324 new_descriptors->CopyFrom(to_index++, this, from_index); 3325 } 3326 3327 ASSERT(to_index == new_descriptors->number_of_descriptors()); 3328 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates()); 3329 3330 return new_descriptors; 3331} 3332 3333 3334Object* DescriptorArray::RemoveTransitions() { 3335 // Remove all transitions and null descriptors. Return a copy of the array 3336 // with all transitions removed, or a Failure object if the new array could 3337 // not be allocated. 3338 3339 // Compute the size of the map transition entries to be removed. 3340 int num_removed = 0; 3341 for (int i = 0; i < number_of_descriptors(); i++) { 3342 if (!IsProperty(i)) num_removed++; 3343 } 3344 3345 // Allocate the new descriptor array. 3346 Object* result = Allocate(number_of_descriptors() - num_removed); 3347 if (result->IsFailure()) return result; 3348 DescriptorArray* new_descriptors = DescriptorArray::cast(result); 3349 3350 // Copy the content. 3351 int next_descriptor = 0; 3352 for (int i = 0; i < number_of_descriptors(); i++) { 3353 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i); 3354 } 3355 ASSERT(next_descriptor == new_descriptors->number_of_descriptors()); 3356 3357 return new_descriptors; 3358} 3359 3360 3361void DescriptorArray::Sort() { 3362 // In-place heap sort. 3363 int len = number_of_descriptors(); 3364 3365 // Bottom-up max-heap construction. 3366 for (int i = 1; i < len; ++i) { 3367 int child_index = i; 3368 while (child_index > 0) { 3369 int parent_index = ((child_index + 1) >> 1) - 1; 3370 uint32_t parent_hash = GetKey(parent_index)->Hash(); 3371 uint32_t child_hash = GetKey(child_index)->Hash(); 3372 if (parent_hash < child_hash) { 3373 Swap(parent_index, child_index); 3374 } else { 3375 break; 3376 } 3377 child_index = parent_index; 3378 } 3379 } 3380 3381 // Extract elements and create sorted array. 3382 for (int i = len - 1; i > 0; --i) { 3383 // Put max element at the back of the array. 3384 Swap(0, i); 3385 // Sift down the new top element. 3386 int parent_index = 0; 3387 while (true) { 3388 int child_index = ((parent_index + 1) << 1) - 1; 3389 if (child_index >= i) break; 3390 uint32_t child1_hash = GetKey(child_index)->Hash(); 3391 uint32_t child2_hash = GetKey(child_index + 1)->Hash(); 3392 uint32_t parent_hash = GetKey(parent_index)->Hash(); 3393 if (child_index + 1 >= i || child1_hash > child2_hash) { 3394 if (parent_hash > child1_hash) break; 3395 Swap(parent_index, child_index); 3396 parent_index = child_index; 3397 } else { 3398 if (parent_hash > child2_hash) break; 3399 Swap(parent_index, child_index + 1); 3400 parent_index = child_index + 1; 3401 } 3402 } 3403 } 3404 3405 SLOW_ASSERT(IsSortedNoDuplicates()); 3406} 3407 3408 3409int DescriptorArray::BinarySearch(String* name, int low, int high) { 3410 uint32_t hash = name->Hash(); 3411 3412 while (low <= high) { 3413 int mid = (low + high) / 2; 3414 String* mid_name = GetKey(mid); 3415 uint32_t mid_hash = mid_name->Hash(); 3416 3417 if (mid_hash > hash) { 3418 high = mid - 1; 3419 continue; 3420 } 3421 if (mid_hash < hash) { 3422 low = mid + 1; 3423 continue; 3424 } 3425 // Found an element with the same hash-code. 3426 ASSERT(hash == mid_hash); 3427 // There might be more, so we find the first one and 3428 // check them all to see if we have a match. 3429 if (name == mid_name && !is_null_descriptor(mid)) return mid; 3430 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--; 3431 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) { 3432 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid; 3433 } 3434 break; 3435 } 3436 return kNotFound; 3437} 3438 3439 3440int DescriptorArray::LinearSearch(String* name, int len) { 3441 uint32_t hash = name->Hash(); 3442 for (int number = 0; number < len; number++) { 3443 String* entry = GetKey(number); 3444 if ((entry->Hash() == hash) && 3445 name->Equals(entry) && 3446 !is_null_descriptor(number)) { 3447 return number; 3448 } 3449 } 3450 return kNotFound; 3451} 3452 3453 3454#ifdef DEBUG 3455bool DescriptorArray::IsEqualTo(DescriptorArray* other) { 3456 if (IsEmpty()) return other->IsEmpty(); 3457 if (other->IsEmpty()) return false; 3458 if (length() != other->length()) return false; 3459 for (int i = 0; i < length(); ++i) { 3460 if (get(i) != other->get(i) && i != kContentArrayIndex) return false; 3461 } 3462 return GetContentArray()->IsEqualTo(other->GetContentArray()); 3463} 3464#endif 3465 3466 3467static StaticResource<StringInputBuffer> string_input_buffer; 3468 3469 3470bool String::LooksValid() { 3471 if (!Heap::Contains(this)) return false; 3472 return true; 3473} 3474 3475 3476int String::Utf8Length() { 3477 if (IsAsciiRepresentation()) return length(); 3478 // Attempt to flatten before accessing the string. It probably 3479 // doesn't make Utf8Length faster, but it is very likely that 3480 // the string will be accessed later (for example by WriteUtf8) 3481 // so it's still a good idea. 3482 TryFlattenIfNotFlat(); 3483 Access<StringInputBuffer> buffer(&string_input_buffer); 3484 buffer->Reset(0, this); 3485 int result = 0; 3486 while (buffer->has_more()) 3487 result += unibrow::Utf8::Length(buffer->GetNext()); 3488 return result; 3489} 3490 3491 3492Vector<const char> String::ToAsciiVector() { 3493 ASSERT(IsAsciiRepresentation()); 3494 ASSERT(IsFlat()); 3495 3496 int offset = 0; 3497 int length = this->length(); 3498 StringRepresentationTag string_tag = StringShape(this).representation_tag(); 3499 String* string = this; 3500 if (string_tag == kConsStringTag) { 3501 ConsString* cons = ConsString::cast(string); 3502 ASSERT(cons->second()->length() == 0); 3503 string = cons->first(); 3504 string_tag = StringShape(string).representation_tag(); 3505 } 3506 if (string_tag == kSeqStringTag) { 3507 SeqAsciiString* seq = SeqAsciiString::cast(string); 3508 char* start = seq->GetChars(); 3509 return Vector<const char>(start + offset, length); 3510 } 3511 ASSERT(string_tag == kExternalStringTag); 3512 ExternalAsciiString* ext = ExternalAsciiString::cast(string); 3513 const char* start = ext->resource()->data(); 3514 return Vector<const char>(start + offset, length); 3515} 3516 3517 3518Vector<const uc16> String::ToUC16Vector() { 3519 ASSERT(IsTwoByteRepresentation()); 3520 ASSERT(IsFlat()); 3521 3522 int offset = 0; 3523 int length = this->length(); 3524 StringRepresentationTag string_tag = StringShape(this).representation_tag(); 3525 String* string = this; 3526 if (string_tag == kConsStringTag) { 3527 ConsString* cons = ConsString::cast(string); 3528 ASSERT(cons->second()->length() == 0); 3529 string = cons->first(); 3530 string_tag = StringShape(string).representation_tag(); 3531 } 3532 if (string_tag == kSeqStringTag) { 3533 SeqTwoByteString* seq = SeqTwoByteString::cast(string); 3534 return Vector<const uc16>(seq->GetChars() + offset, length); 3535 } 3536 ASSERT(string_tag == kExternalStringTag); 3537 ExternalTwoByteString* ext = ExternalTwoByteString::cast(string); 3538 const uc16* start = 3539 reinterpret_cast<const uc16*>(ext->resource()->data()); 3540 return Vector<const uc16>(start + offset, length); 3541} 3542 3543 3544SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls, 3545 RobustnessFlag robust_flag, 3546 int offset, 3547 int length, 3548 int* length_return) { 3549 ASSERT(NativeAllocationChecker::allocation_allowed()); 3550 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { 3551 return SmartPointer<char>(NULL); 3552 } 3553 3554 // Negative length means the to the end of the string. 3555 if (length < 0) length = kMaxInt - offset; 3556 3557 // Compute the size of the UTF-8 string. Start at the specified offset. 3558 Access<StringInputBuffer> buffer(&string_input_buffer); 3559 buffer->Reset(offset, this); 3560 int character_position = offset; 3561 int utf8_bytes = 0; 3562 while (buffer->has_more()) { 3563 uint16_t character = buffer->GetNext(); 3564 if (character_position < offset + length) { 3565 utf8_bytes += unibrow::Utf8::Length(character); 3566 } 3567 character_position++; 3568 } 3569 3570 if (length_return) { 3571 *length_return = utf8_bytes; 3572 } 3573 3574 char* result = NewArray<char>(utf8_bytes + 1); 3575 3576 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset. 3577 buffer->Rewind(); 3578 buffer->Seek(offset); 3579 character_position = offset; 3580 int utf8_byte_position = 0; 3581 while (buffer->has_more()) { 3582 uint16_t character = buffer->GetNext(); 3583 if (character_position < offset + length) { 3584 if (allow_nulls == DISALLOW_NULLS && character == 0) { 3585 character = ' '; 3586 } 3587 utf8_byte_position += 3588 unibrow::Utf8::Encode(result + utf8_byte_position, character); 3589 } 3590 character_position++; 3591 } 3592 result[utf8_byte_position] = 0; 3593 return SmartPointer<char>(result); 3594} 3595 3596 3597SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls, 3598 RobustnessFlag robust_flag, 3599 int* length_return) { 3600 return ToCString(allow_nulls, robust_flag, 0, -1, length_return); 3601} 3602 3603 3604const uc16* String::GetTwoByteData() { 3605 return GetTwoByteData(0); 3606} 3607 3608 3609const uc16* String::GetTwoByteData(unsigned start) { 3610 ASSERT(!IsAsciiRepresentation()); 3611 switch (StringShape(this).representation_tag()) { 3612 case kSeqStringTag: 3613 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start); 3614 case kExternalStringTag: 3615 return ExternalTwoByteString::cast(this)-> 3616 ExternalTwoByteStringGetData(start); 3617 case kConsStringTag: 3618 UNREACHABLE(); 3619 return NULL; 3620 } 3621 UNREACHABLE(); 3622 return NULL; 3623} 3624 3625 3626SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) { 3627 ASSERT(NativeAllocationChecker::allocation_allowed()); 3628 3629 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { 3630 return SmartPointer<uc16>(); 3631 } 3632 3633 Access<StringInputBuffer> buffer(&string_input_buffer); 3634 buffer->Reset(this); 3635 3636 uc16* result = NewArray<uc16>(length() + 1); 3637 3638 int i = 0; 3639 while (buffer->has_more()) { 3640 uint16_t character = buffer->GetNext(); 3641 result[i++] = character; 3642 } 3643 result[i] = 0; 3644 return SmartPointer<uc16>(result); 3645} 3646 3647 3648const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) { 3649 return reinterpret_cast<uc16*>( 3650 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start; 3651} 3652 3653 3654void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, 3655 unsigned* offset_ptr, 3656 unsigned max_chars) { 3657 unsigned chars_read = 0; 3658 unsigned offset = *offset_ptr; 3659 while (chars_read < max_chars) { 3660 uint16_t c = *reinterpret_cast<uint16_t*>( 3661 reinterpret_cast<char*>(this) - 3662 kHeapObjectTag + kHeaderSize + offset * kShortSize); 3663 if (c <= kMaxAsciiCharCode) { 3664 // Fast case for ASCII characters. Cursor is an input output argument. 3665 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c, 3666 rbb->util_buffer, 3667 rbb->capacity, 3668 rbb->cursor)) { 3669 break; 3670 } 3671 } else { 3672 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c, 3673 rbb->util_buffer, 3674 rbb->capacity, 3675 rbb->cursor)) { 3676 break; 3677 } 3678 } 3679 offset++; 3680 chars_read++; 3681 } 3682 *offset_ptr = offset; 3683 rbb->remaining += chars_read; 3684} 3685 3686 3687const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock( 3688 unsigned* remaining, 3689 unsigned* offset_ptr, 3690 unsigned max_chars) { 3691 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) - 3692 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize; 3693 *remaining = max_chars; 3694 *offset_ptr += max_chars; 3695 return b; 3696} 3697 3698 3699// This will iterate unless the block of string data spans two 'halves' of 3700// a ConsString, in which case it will recurse. Since the block of string 3701// data to be read has a maximum size this limits the maximum recursion 3702// depth to something sane. Since C++ does not have tail call recursion 3703// elimination, the iteration must be explicit. Since this is not an 3704// -IntoBuffer method it can delegate to one of the efficient 3705// *AsciiStringReadBlock routines. 3706const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb, 3707 unsigned* offset_ptr, 3708 unsigned max_chars) { 3709 ConsString* current = this; 3710 unsigned offset = *offset_ptr; 3711 int offset_correction = 0; 3712 3713 while (true) { 3714 String* left = current->first(); 3715 unsigned left_length = (unsigned)left->length(); 3716 if (left_length > offset && 3717 (max_chars <= left_length - offset || 3718 (rbb->capacity <= left_length - offset && 3719 (max_chars = left_length - offset, true)))) { // comma operator! 3720 // Left hand side only - iterate unless we have reached the bottom of 3721 // the cons tree. The assignment on the left of the comma operator is 3722 // in order to make use of the fact that the -IntoBuffer routines can 3723 // produce at most 'capacity' characters. This enables us to postpone 3724 // the point where we switch to the -IntoBuffer routines (below) in order 3725 // to maximize the chances of delegating a big chunk of work to the 3726 // efficient *AsciiStringReadBlock routines. 3727 if (StringShape(left).IsCons()) { 3728 current = ConsString::cast(left); 3729 continue; 3730 } else { 3731 const unibrow::byte* answer = 3732 String::ReadBlock(left, rbb, &offset, max_chars); 3733 *offset_ptr = offset + offset_correction; 3734 return answer; 3735 } 3736 } else if (left_length <= offset) { 3737 // Right hand side only - iterate unless we have reached the bottom of 3738 // the cons tree. 3739 String* right = current->second(); 3740 offset -= left_length; 3741 offset_correction += left_length; 3742 if (StringShape(right).IsCons()) { 3743 current = ConsString::cast(right); 3744 continue; 3745 } else { 3746 const unibrow::byte* answer = 3747 String::ReadBlock(right, rbb, &offset, max_chars); 3748 *offset_ptr = offset + offset_correction; 3749 return answer; 3750 } 3751 } else { 3752 // The block to be read spans two sides of the ConsString, so we call the 3753 // -IntoBuffer version, which will recurse. The -IntoBuffer methods 3754 // are able to assemble data from several part strings because they use 3755 // the util_buffer to store their data and never return direct pointers 3756 // to their storage. We don't try to read more than the buffer capacity 3757 // here or we can get too much recursion. 3758 ASSERT(rbb->remaining == 0); 3759 ASSERT(rbb->cursor == 0); 3760 current->ConsStringReadBlockIntoBuffer( 3761 rbb, 3762 &offset, 3763 max_chars > rbb->capacity ? rbb->capacity : max_chars); 3764 *offset_ptr = offset + offset_correction; 3765 return rbb->util_buffer; 3766 } 3767 } 3768} 3769 3770 3771uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) { 3772 ASSERT(index >= 0 && index < length()); 3773 return resource()->data()[index]; 3774} 3775 3776 3777const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock( 3778 unsigned* remaining, 3779 unsigned* offset_ptr, 3780 unsigned max_chars) { 3781 // Cast const char* to unibrow::byte* (signedness difference). 3782 const unibrow::byte* b = 3783 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr; 3784 *remaining = max_chars; 3785 *offset_ptr += max_chars; 3786 return b; 3787} 3788 3789 3790const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData( 3791 unsigned start) { 3792 return resource()->data() + start; 3793} 3794 3795 3796uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) { 3797 ASSERT(index >= 0 && index < length()); 3798 return resource()->data()[index]; 3799} 3800 3801 3802void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer( 3803 ReadBlockBuffer* rbb, 3804 unsigned* offset_ptr, 3805 unsigned max_chars) { 3806 unsigned chars_read = 0; 3807 unsigned offset = *offset_ptr; 3808 const uint16_t* data = resource()->data(); 3809 while (chars_read < max_chars) { 3810 uint16_t c = data[offset]; 3811 if (c <= kMaxAsciiCharCode) { 3812 // Fast case for ASCII characters. Cursor is an input output argument. 3813 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c, 3814 rbb->util_buffer, 3815 rbb->capacity, 3816 rbb->cursor)) 3817 break; 3818 } else { 3819 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c, 3820 rbb->util_buffer, 3821 rbb->capacity, 3822 rbb->cursor)) 3823 break; 3824 } 3825 offset++; 3826 chars_read++; 3827 } 3828 *offset_ptr = offset; 3829 rbb->remaining += chars_read; 3830} 3831 3832 3833void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, 3834 unsigned* offset_ptr, 3835 unsigned max_chars) { 3836 unsigned capacity = rbb->capacity - rbb->cursor; 3837 if (max_chars > capacity) max_chars = capacity; 3838 memcpy(rbb->util_buffer + rbb->cursor, 3839 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize + 3840 *offset_ptr * kCharSize, 3841 max_chars); 3842 rbb->remaining += max_chars; 3843 *offset_ptr += max_chars; 3844 rbb->cursor += max_chars; 3845} 3846 3847 3848void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer( 3849 ReadBlockBuffer* rbb, 3850 unsigned* offset_ptr, 3851 unsigned max_chars) { 3852 unsigned capacity = rbb->capacity - rbb->cursor; 3853 if (max_chars > capacity) max_chars = capacity; 3854 memcpy(rbb->util_buffer + rbb->cursor, 3855 resource()->data() + *offset_ptr, 3856 max_chars); 3857 rbb->remaining += max_chars; 3858 *offset_ptr += max_chars; 3859 rbb->cursor += max_chars; 3860} 3861 3862 3863// This method determines the type of string involved and then copies 3864// a whole chunk of characters into a buffer, or returns a pointer to a buffer 3865// where they can be found. The pointer is not necessarily valid across a GC 3866// (see AsciiStringReadBlock). 3867const unibrow::byte* String::ReadBlock(String* input, 3868 ReadBlockBuffer* rbb, 3869 unsigned* offset_ptr, 3870 unsigned max_chars) { 3871 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length())); 3872 if (max_chars == 0) { 3873 rbb->remaining = 0; 3874 return NULL; 3875 } 3876 switch (StringShape(input).representation_tag()) { 3877 case kSeqStringTag: 3878 if (input->IsAsciiRepresentation()) { 3879 SeqAsciiString* str = SeqAsciiString::cast(input); 3880 return str->SeqAsciiStringReadBlock(&rbb->remaining, 3881 offset_ptr, 3882 max_chars); 3883 } else { 3884 SeqTwoByteString* str = SeqTwoByteString::cast(input); 3885 str->SeqTwoByteStringReadBlockIntoBuffer(rbb, 3886 offset_ptr, 3887 max_chars); 3888 return rbb->util_buffer; 3889 } 3890 case kConsStringTag: 3891 return ConsString::cast(input)->ConsStringReadBlock(rbb, 3892 offset_ptr, 3893 max_chars); 3894 case kExternalStringTag: 3895 if (input->IsAsciiRepresentation()) { 3896 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock( 3897 &rbb->remaining, 3898 offset_ptr, 3899 max_chars); 3900 } else { 3901 ExternalTwoByteString::cast(input)-> 3902 ExternalTwoByteStringReadBlockIntoBuffer(rbb, 3903 offset_ptr, 3904 max_chars); 3905 return rbb->util_buffer; 3906 } 3907 default: 3908 break; 3909 } 3910 3911 UNREACHABLE(); 3912 return 0; 3913} 3914 3915 3916Relocatable* Relocatable::top_ = NULL; 3917 3918 3919void Relocatable::PostGarbageCollectionProcessing() { 3920 Relocatable* current = top_; 3921 while (current != NULL) { 3922 current->PostGarbageCollection(); 3923 current = current->prev_; 3924 } 3925} 3926 3927 3928// Reserve space for statics needing saving and restoring. 3929int Relocatable::ArchiveSpacePerThread() { 3930 return sizeof(top_); 3931} 3932 3933 3934// Archive statics that are thread local. 3935char* Relocatable::ArchiveState(char* to) { 3936 *reinterpret_cast<Relocatable**>(to) = top_; 3937 top_ = NULL; 3938 return to + ArchiveSpacePerThread(); 3939} 3940 3941 3942// Restore statics that are thread local. 3943char* Relocatable::RestoreState(char* from) { 3944 top_ = *reinterpret_cast<Relocatable**>(from); 3945 return from + ArchiveSpacePerThread(); 3946} 3947 3948 3949char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) { 3950 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage); 3951 Iterate(v, top); 3952 return thread_storage + ArchiveSpacePerThread(); 3953} 3954 3955 3956void Relocatable::Iterate(ObjectVisitor* v) { 3957 Iterate(v, top_); 3958} 3959 3960 3961void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) { 3962 Relocatable* current = top; 3963 while (current != NULL) { 3964 current->IterateInstance(v); 3965 current = current->prev_; 3966 } 3967} 3968 3969 3970FlatStringReader::FlatStringReader(Handle<String> str) 3971 : str_(str.location()), 3972 length_(str->length()) { 3973 PostGarbageCollection(); 3974} 3975 3976 3977FlatStringReader::FlatStringReader(Vector<const char> input) 3978 : str_(0), 3979 is_ascii_(true), 3980 length_(input.length()), 3981 start_(input.start()) { } 3982 3983 3984void FlatStringReader::PostGarbageCollection() { 3985 if (str_ == NULL) return; 3986 Handle<String> str(str_); 3987 ASSERT(str->IsFlat()); 3988 is_ascii_ = str->IsAsciiRepresentation(); 3989 if (is_ascii_) { 3990 start_ = str->ToAsciiVector().start(); 3991 } else { 3992 start_ = str->ToUC16Vector().start(); 3993 } 3994} 3995 3996 3997void StringInputBuffer::Seek(unsigned pos) { 3998 Reset(pos, input_); 3999} 4000 4001 4002void SafeStringInputBuffer::Seek(unsigned pos) { 4003 Reset(pos, input_); 4004} 4005 4006 4007// This method determines the type of string involved and then copies 4008// a whole chunk of characters into a buffer. It can be used with strings 4009// that have been glued together to form a ConsString and which must cooperate 4010// to fill up a buffer. 4011void String::ReadBlockIntoBuffer(String* input, 4012 ReadBlockBuffer* rbb, 4013 unsigned* offset_ptr, 4014 unsigned max_chars) { 4015 ASSERT(*offset_ptr <= (unsigned)input->length()); 4016 if (max_chars == 0) return; 4017 4018 switch (StringShape(input).representation_tag()) { 4019 case kSeqStringTag: 4020 if (input->IsAsciiRepresentation()) { 4021 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb, 4022 offset_ptr, 4023 max_chars); 4024 return; 4025 } else { 4026 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb, 4027 offset_ptr, 4028 max_chars); 4029 return; 4030 } 4031 case kConsStringTag: 4032 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb, 4033 offset_ptr, 4034 max_chars); 4035 return; 4036 case kExternalStringTag: 4037 if (input->IsAsciiRepresentation()) { 4038 ExternalAsciiString::cast(input)-> 4039 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars); 4040 } else { 4041 ExternalTwoByteString::cast(input)-> 4042 ExternalTwoByteStringReadBlockIntoBuffer(rbb, 4043 offset_ptr, 4044 max_chars); 4045 } 4046 return; 4047 default: 4048 break; 4049 } 4050 4051 UNREACHABLE(); 4052 return; 4053} 4054 4055 4056const unibrow::byte* String::ReadBlock(String* input, 4057 unibrow::byte* util_buffer, 4058 unsigned capacity, 4059 unsigned* remaining, 4060 unsigned* offset_ptr) { 4061 ASSERT(*offset_ptr <= (unsigned)input->length()); 4062 unsigned chars = input->length() - *offset_ptr; 4063 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0); 4064 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars); 4065 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length())); 4066 *remaining = rbb.remaining; 4067 return answer; 4068} 4069 4070 4071const unibrow::byte* String::ReadBlock(String** raw_input, 4072 unibrow::byte* util_buffer, 4073 unsigned capacity, 4074 unsigned* remaining, 4075 unsigned* offset_ptr) { 4076 Handle<String> input(raw_input); 4077 ASSERT(*offset_ptr <= (unsigned)input->length()); 4078 unsigned chars = input->length() - *offset_ptr; 4079 if (chars > capacity) chars = capacity; 4080 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0); 4081 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars); 4082 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length())); 4083 *remaining = rbb.remaining; 4084 return rbb.util_buffer; 4085} 4086 4087 4088// This will iterate unless the block of string data spans two 'halves' of 4089// a ConsString, in which case it will recurse. Since the block of string 4090// data to be read has a maximum size this limits the maximum recursion 4091// depth to something sane. Since C++ does not have tail call recursion 4092// elimination, the iteration must be explicit. 4093void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, 4094 unsigned* offset_ptr, 4095 unsigned max_chars) { 4096 ConsString* current = this; 4097 unsigned offset = *offset_ptr; 4098 int offset_correction = 0; 4099 4100 while (true) { 4101 String* left = current->first(); 4102 unsigned left_length = (unsigned)left->length(); 4103 if (left_length > offset && 4104 max_chars <= left_length - offset) { 4105 // Left hand side only - iterate unless we have reached the bottom of 4106 // the cons tree. 4107 if (StringShape(left).IsCons()) { 4108 current = ConsString::cast(left); 4109 continue; 4110 } else { 4111 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars); 4112 *offset_ptr = offset + offset_correction; 4113 return; 4114 } 4115 } else if (left_length <= offset) { 4116 // Right hand side only - iterate unless we have reached the bottom of 4117 // the cons tree. 4118 offset -= left_length; 4119 offset_correction += left_length; 4120 String* right = current->second(); 4121 if (StringShape(right).IsCons()) { 4122 current = ConsString::cast(right); 4123 continue; 4124 } else { 4125 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars); 4126 *offset_ptr = offset + offset_correction; 4127 return; 4128 } 4129 } else { 4130 // The block to be read spans two sides of the ConsString, so we recurse. 4131 // First recurse on the left. 4132 max_chars -= left_length - offset; 4133 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset); 4134 // We may have reached the max or there may not have been enough space 4135 // in the buffer for the characters in the left hand side. 4136 if (offset == left_length) { 4137 // Recurse on the right. 4138 String* right = String::cast(current->second()); 4139 offset -= left_length; 4140 offset_correction += left_length; 4141 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars); 4142 } 4143 *offset_ptr = offset + offset_correction; 4144 return; 4145 } 4146 } 4147} 4148 4149 4150void ConsString::ConsStringIterateBody(ObjectVisitor* v) { 4151 IteratePointers(v, kFirstOffset, kSecondOffset + kPointerSize); 4152} 4153 4154 4155void JSGlobalPropertyCell::JSGlobalPropertyCellIterateBody(ObjectVisitor* v) { 4156 IteratePointers(v, kValueOffset, kValueOffset + kPointerSize); 4157} 4158 4159 4160uint16_t ConsString::ConsStringGet(int index) { 4161 ASSERT(index >= 0 && index < this->length()); 4162 4163 // Check for a flattened cons string 4164 if (second()->length() == 0) { 4165 String* left = first(); 4166 return left->Get(index); 4167 } 4168 4169 String* string = String::cast(this); 4170 4171 while (true) { 4172 if (StringShape(string).IsCons()) { 4173 ConsString* cons_string = ConsString::cast(string); 4174 String* left = cons_string->first(); 4175 if (left->length() > index) { 4176 string = left; 4177 } else { 4178 index -= left->length(); 4179 string = cons_string->second(); 4180 } 4181 } else { 4182 return string->Get(index); 4183 } 4184 } 4185 4186 UNREACHABLE(); 4187 return 0; 4188} 4189 4190 4191template <typename sinkchar> 4192void String::WriteToFlat(String* src, 4193 sinkchar* sink, 4194 int f, 4195 int t) { 4196 String* source = src; 4197 int from = f; 4198 int to = t; 4199 while (true) { 4200 ASSERT(0 <= from && from <= to && to <= source->length()); 4201 switch (StringShape(source).full_representation_tag()) { 4202 case kAsciiStringTag | kExternalStringTag: { 4203 CopyChars(sink, 4204 ExternalAsciiString::cast(source)->resource()->data() + from, 4205 to - from); 4206 return; 4207 } 4208 case kTwoByteStringTag | kExternalStringTag: { 4209 const uc16* data = 4210 ExternalTwoByteString::cast(source)->resource()->data(); 4211 CopyChars(sink, 4212 data + from, 4213 to - from); 4214 return; 4215 } 4216 case kAsciiStringTag | kSeqStringTag: { 4217 CopyChars(sink, 4218 SeqAsciiString::cast(source)->GetChars() + from, 4219 to - from); 4220 return; 4221 } 4222 case kTwoByteStringTag | kSeqStringTag: { 4223 CopyChars(sink, 4224 SeqTwoByteString::cast(source)->GetChars() + from, 4225 to - from); 4226 return; 4227 } 4228 case kAsciiStringTag | kConsStringTag: 4229 case kTwoByteStringTag | kConsStringTag: { 4230 ConsString* cons_string = ConsString::cast(source); 4231 String* first = cons_string->first(); 4232 int boundary = first->length(); 4233 if (to - boundary >= boundary - from) { 4234 // Right hand side is longer. Recurse over left. 4235 if (from < boundary) { 4236 WriteToFlat(first, sink, from, boundary); 4237 sink += boundary - from; 4238 from = 0; 4239 } else { 4240 from -= boundary; 4241 } 4242 to -= boundary; 4243 source = cons_string->second(); 4244 } else { 4245 // Left hand side is longer. Recurse over right. 4246 if (to > boundary) { 4247 String* second = cons_string->second(); 4248 WriteToFlat(second, 4249 sink + boundary - from, 4250 0, 4251 to - boundary); 4252 to = boundary; 4253 } 4254 source = first; 4255 } 4256 break; 4257 } 4258 } 4259 } 4260} 4261 4262 4263#define FIELD_ADDR(p, offset) \ 4264 (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag) 4265 4266void ExternalAsciiString::ExternalAsciiStringIterateBody(ObjectVisitor* v) { 4267 typedef v8::String::ExternalAsciiStringResource Resource; 4268 v->VisitExternalAsciiString( 4269 reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset))); 4270} 4271 4272 4273void ExternalTwoByteString::ExternalTwoByteStringIterateBody(ObjectVisitor* v) { 4274 typedef v8::String::ExternalStringResource Resource; 4275 v->VisitExternalTwoByteString( 4276 reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset))); 4277} 4278 4279#undef FIELD_ADDR 4280 4281template <typename IteratorA, typename IteratorB> 4282static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) { 4283 // General slow case check. We know that the ia and ib iterators 4284 // have the same length. 4285 while (ia->has_more()) { 4286 uc32 ca = ia->GetNext(); 4287 uc32 cb = ib->GetNext(); 4288 if (ca != cb) 4289 return false; 4290 } 4291 return true; 4292} 4293 4294 4295// Compares the contents of two strings by reading and comparing 4296// int-sized blocks of characters. 4297template <typename Char> 4298static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) { 4299 int length = a.length(); 4300 ASSERT_EQ(length, b.length()); 4301 const Char* pa = a.start(); 4302 const Char* pb = b.start(); 4303 int i = 0; 4304#ifndef V8_HOST_CAN_READ_UNALIGNED 4305 // If this architecture isn't comfortable reading unaligned ints 4306 // then we have to check that the strings are aligned before 4307 // comparing them blockwise. 4308 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT 4309 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa); 4310 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb); 4311 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) { 4312#endif 4313 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT 4314 int endpoint = length - kStepSize; 4315 // Compare blocks until we reach near the end of the string. 4316 for (; i <= endpoint; i += kStepSize) { 4317 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i); 4318 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i); 4319 if (wa != wb) { 4320 return false; 4321 } 4322 } 4323#ifndef V8_HOST_CAN_READ_UNALIGNED 4324 } 4325#endif 4326 // Compare the remaining characters that didn't fit into a block. 4327 for (; i < length; i++) { 4328 if (a[i] != b[i]) { 4329 return false; 4330 } 4331 } 4332 return true; 4333} 4334 4335 4336static StringInputBuffer string_compare_buffer_b; 4337 4338 4339template <typename IteratorA> 4340static inline bool CompareStringContentsPartial(IteratorA* ia, String* b) { 4341 if (b->IsFlat()) { 4342 if (b->IsAsciiRepresentation()) { 4343 VectorIterator<char> ib(b->ToAsciiVector()); 4344 return CompareStringContents(ia, &ib); 4345 } else { 4346 VectorIterator<uc16> ib(b->ToUC16Vector()); 4347 return CompareStringContents(ia, &ib); 4348 } 4349 } else { 4350 string_compare_buffer_b.Reset(0, b); 4351 return CompareStringContents(ia, &string_compare_buffer_b); 4352 } 4353} 4354 4355 4356static StringInputBuffer string_compare_buffer_a; 4357 4358 4359bool String::SlowEquals(String* other) { 4360 // Fast check: negative check with lengths. 4361 int len = length(); 4362 if (len != other->length()) return false; 4363 if (len == 0) return true; 4364 4365 // Fast check: if hash code is computed for both strings 4366 // a fast negative check can be performed. 4367 if (HasHashCode() && other->HasHashCode()) { 4368 if (Hash() != other->Hash()) return false; 4369 } 4370 4371 if (StringShape(this).IsSequentialAscii() && 4372 StringShape(other).IsSequentialAscii()) { 4373 const char* str1 = SeqAsciiString::cast(this)->GetChars(); 4374 const char* str2 = SeqAsciiString::cast(other)->GetChars(); 4375 return CompareRawStringContents(Vector<const char>(str1, len), 4376 Vector<const char>(str2, len)); 4377 } 4378 4379 if (this->IsFlat()) { 4380 if (IsAsciiRepresentation()) { 4381 Vector<const char> vec1 = this->ToAsciiVector(); 4382 if (other->IsFlat()) { 4383 if (other->IsAsciiRepresentation()) { 4384 Vector<const char> vec2 = other->ToAsciiVector(); 4385 return CompareRawStringContents(vec1, vec2); 4386 } else { 4387 VectorIterator<char> buf1(vec1); 4388 VectorIterator<uc16> ib(other->ToUC16Vector()); 4389 return CompareStringContents(&buf1, &ib); 4390 } 4391 } else { 4392 VectorIterator<char> buf1(vec1); 4393 string_compare_buffer_b.Reset(0, other); 4394 return CompareStringContents(&buf1, &string_compare_buffer_b); 4395 } 4396 } else { 4397 Vector<const uc16> vec1 = this->ToUC16Vector(); 4398 if (other->IsFlat()) { 4399 if (other->IsAsciiRepresentation()) { 4400 VectorIterator<uc16> buf1(vec1); 4401 VectorIterator<char> ib(other->ToAsciiVector()); 4402 return CompareStringContents(&buf1, &ib); 4403 } else { 4404 Vector<const uc16> vec2(other->ToUC16Vector()); 4405 return CompareRawStringContents(vec1, vec2); 4406 } 4407 } else { 4408 VectorIterator<uc16> buf1(vec1); 4409 string_compare_buffer_b.Reset(0, other); 4410 return CompareStringContents(&buf1, &string_compare_buffer_b); 4411 } 4412 } 4413 } else { 4414 string_compare_buffer_a.Reset(0, this); 4415 return CompareStringContentsPartial(&string_compare_buffer_a, other); 4416 } 4417} 4418 4419 4420bool String::MarkAsUndetectable() { 4421 if (StringShape(this).IsSymbol()) return false; 4422 4423 Map* map = this->map(); 4424 if (map == Heap::string_map()) { 4425 this->set_map(Heap::undetectable_string_map()); 4426 return true; 4427 } else if (map == Heap::ascii_string_map()) { 4428 this->set_map(Heap::undetectable_ascii_string_map()); 4429 return true; 4430 } 4431 // Rest cannot be marked as undetectable 4432 return false; 4433} 4434 4435 4436bool String::IsEqualTo(Vector<const char> str) { 4437 int slen = length(); 4438 Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder()); 4439 decoder->Reset(str.start(), str.length()); 4440 int i; 4441 for (i = 0; i < slen && decoder->has_more(); i++) { 4442 uc32 r = decoder->GetNext(); 4443 if (Get(i) != r) return false; 4444 } 4445 return i == slen && !decoder->has_more(); 4446} 4447 4448 4449uint32_t String::ComputeAndSetHash() { 4450 // Should only be called if hash code has not yet been computed. 4451 ASSERT(!(hash_field() & kHashComputedMask)); 4452 4453 // Compute the hash code. 4454 StringInputBuffer buffer(this); 4455 uint32_t field = ComputeHashField(&buffer, length()); 4456 4457 // Store the hash code in the object. 4458 set_hash_field(field); 4459 4460 // Check the hash code is there. 4461 ASSERT(hash_field() & kHashComputedMask); 4462 uint32_t result = field >> kHashShift; 4463 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. 4464 return result; 4465} 4466 4467 4468bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer, 4469 uint32_t* index, 4470 int length) { 4471 if (length == 0 || length > kMaxArrayIndexSize) return false; 4472 uc32 ch = buffer->GetNext(); 4473 4474 // If the string begins with a '0' character, it must only consist 4475 // of it to be a legal array index. 4476 if (ch == '0') { 4477 *index = 0; 4478 return length == 1; 4479 } 4480 4481 // Convert string to uint32 array index; character by character. 4482 int d = ch - '0'; 4483 if (d < 0 || d > 9) return false; 4484 uint32_t result = d; 4485 while (buffer->has_more()) { 4486 d = buffer->GetNext() - '0'; 4487 if (d < 0 || d > 9) return false; 4488 // Check that the new result is below the 32 bit limit. 4489 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false; 4490 result = (result * 10) + d; 4491 } 4492 4493 *index = result; 4494 return true; 4495} 4496 4497 4498bool String::SlowAsArrayIndex(uint32_t* index) { 4499 if (length() <= kMaxCachedArrayIndexLength) { 4500 Hash(); // force computation of hash code 4501 uint32_t field = hash_field(); 4502 if ((field & kIsArrayIndexMask) == 0) return false; 4503 // Isolate the array index form the full hash field. 4504 *index = (kArrayIndexHashMask & field) >> kHashShift; 4505 return true; 4506 } else { 4507 StringInputBuffer buffer(this); 4508 return ComputeArrayIndex(&buffer, index, length()); 4509 } 4510} 4511 4512 4513static inline uint32_t HashField(uint32_t hash, 4514 bool is_array_index, 4515 int length = -1) { 4516 uint32_t result = 4517 (hash << String::kHashShift) | String::kHashComputedMask; 4518 if (is_array_index) { 4519 // For array indexes mix the length into the hash as an array index could 4520 // be zero. 4521 ASSERT(length > 0); 4522 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < 4523 (1 << String::kArrayIndexValueBits)); 4524 result |= String::kIsArrayIndexMask; 4525 result |= length << String::kArrayIndexHashLengthShift; 4526 } 4527 return result; 4528} 4529 4530 4531uint32_t StringHasher::GetHashField() { 4532 ASSERT(is_valid()); 4533 if (length_ <= String::kMaxHashCalcLength) { 4534 if (is_array_index()) { 4535 return v8::internal::HashField(array_index(), true, length_); 4536 } else { 4537 return v8::internal::HashField(GetHash(), false); 4538 } 4539 uint32_t payload = v8::internal::HashField(GetHash(), false); 4540 return payload; 4541 } else { 4542 return v8::internal::HashField(length_, false); 4543 } 4544} 4545 4546 4547uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer, 4548 int length) { 4549 StringHasher hasher(length); 4550 4551 // Very long strings have a trivial hash that doesn't inspect the 4552 // string contents. 4553 if (hasher.has_trivial_hash()) { 4554 return hasher.GetHashField(); 4555 } 4556 4557 // Do the iterative array index computation as long as there is a 4558 // chance this is an array index. 4559 while (buffer->has_more() && hasher.is_array_index()) { 4560 hasher.AddCharacter(buffer->GetNext()); 4561 } 4562 4563 // Process the remaining characters without updating the array 4564 // index. 4565 while (buffer->has_more()) { 4566 hasher.AddCharacterNoIndex(buffer->GetNext()); 4567 } 4568 4569 return hasher.GetHashField(); 4570} 4571 4572 4573Object* String::SubString(int start, int end) { 4574 if (start == 0 && end == length()) return this; 4575 Object* result = Heap::AllocateSubString(this, start, end); 4576 return result; 4577} 4578 4579 4580void String::PrintOn(FILE* file) { 4581 int length = this->length(); 4582 for (int i = 0; i < length; i++) { 4583 fprintf(file, "%c", Get(i)); 4584 } 4585} 4586 4587 4588void Map::CreateBackPointers() { 4589 DescriptorArray* descriptors = instance_descriptors(); 4590 for (int i = 0; i < descriptors->number_of_descriptors(); i++) { 4591 if (descriptors->GetType(i) == MAP_TRANSITION) { 4592 // Get target. 4593 Map* target = Map::cast(descriptors->GetValue(i)); 4594#ifdef DEBUG 4595 // Verify target. 4596 Object* source_prototype = prototype(); 4597 Object* target_prototype = target->prototype(); 4598 ASSERT(source_prototype->IsJSObject() || 4599 source_prototype->IsMap() || 4600 source_prototype->IsNull()); 4601 ASSERT(target_prototype->IsJSObject() || 4602 target_prototype->IsNull()); 4603 ASSERT(source_prototype->IsMap() || 4604 source_prototype == target_prototype); 4605#endif 4606 // Point target back to source. set_prototype() will not let us set 4607 // the prototype to a map, as we do here. 4608 *RawField(target, kPrototypeOffset) = this; 4609 } 4610 } 4611} 4612 4613 4614void Map::ClearNonLiveTransitions(Object* real_prototype) { 4615 // Live DescriptorArray objects will be marked, so we must use 4616 // low-level accessors to get and modify their data. 4617 DescriptorArray* d = reinterpret_cast<DescriptorArray*>( 4618 *RawField(this, Map::kInstanceDescriptorsOffset)); 4619 if (d == Heap::raw_unchecked_empty_descriptor_array()) return; 4620 Smi* NullDescriptorDetails = 4621 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi(); 4622 FixedArray* contents = reinterpret_cast<FixedArray*>( 4623 d->get(DescriptorArray::kContentArrayIndex)); 4624 ASSERT(contents->length() >= 2); 4625 for (int i = 0; i < contents->length(); i += 2) { 4626 // If the pair (value, details) is a map transition, 4627 // check if the target is live. If not, null the descriptor. 4628 // Also drop the back pointer for that map transition, so that this 4629 // map is not reached again by following a back pointer from a 4630 // non-live object. 4631 PropertyDetails details(Smi::cast(contents->get(i + 1))); 4632 if (details.type() == MAP_TRANSITION) { 4633 Map* target = reinterpret_cast<Map*>(contents->get(i)); 4634 ASSERT(target->IsHeapObject()); 4635 if (!target->IsMarked()) { 4636 ASSERT(target->IsMap()); 4637 contents->set(i + 1, NullDescriptorDetails); 4638 contents->set_null(i); 4639 ASSERT(target->prototype() == this || 4640 target->prototype() == real_prototype); 4641 // Getter prototype() is read-only, set_prototype() has side effects. 4642 *RawField(target, Map::kPrototypeOffset) = real_prototype; 4643 } 4644 } 4645 } 4646} 4647 4648 4649void Map::MapIterateBody(ObjectVisitor* v) { 4650 // Assumes all Object* members are contiguously allocated! 4651 IteratePointers(v, kPrototypeOffset, kCodeCacheOffset + kPointerSize); 4652} 4653 4654 4655Object* JSFunction::SetInstancePrototype(Object* value) { 4656 ASSERT(value->IsJSObject()); 4657 4658 if (has_initial_map()) { 4659 initial_map()->set_prototype(value); 4660 } else { 4661 // Put the value in the initial map field until an initial map is 4662 // needed. At that point, a new initial map is created and the 4663 // prototype is put into the initial map where it belongs. 4664 set_prototype_or_initial_map(value); 4665 } 4666 return value; 4667} 4668 4669 4670 4671Object* JSFunction::SetPrototype(Object* value) { 4672 Object* construct_prototype = value; 4673 4674 // If the value is not a JSObject, store the value in the map's 4675 // constructor field so it can be accessed. Also, set the prototype 4676 // used for constructing objects to the original object prototype. 4677 // See ECMA-262 13.2.2. 4678 if (!value->IsJSObject()) { 4679 // Copy the map so this does not affect unrelated functions. 4680 // Remove map transitions because they point to maps with a 4681 // different prototype. 4682 Object* new_map = map()->CopyDropTransitions(); 4683 if (new_map->IsFailure()) return new_map; 4684 set_map(Map::cast(new_map)); 4685 map()->set_constructor(value); 4686 map()->set_non_instance_prototype(true); 4687 construct_prototype = 4688 Top::context()->global_context()->initial_object_prototype(); 4689 } else { 4690 map()->set_non_instance_prototype(false); 4691 } 4692 4693 return SetInstancePrototype(construct_prototype); 4694} 4695 4696 4697Object* JSFunction::SetInstanceClassName(String* name) { 4698 shared()->set_instance_class_name(name); 4699 return this; 4700} 4701 4702 4703Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) { 4704 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex)); 4705} 4706 4707 4708void Oddball::OddballIterateBody(ObjectVisitor* v) { 4709 // Assumes all Object* members are contiguously allocated! 4710 IteratePointers(v, kToStringOffset, kToNumberOffset + kPointerSize); 4711} 4712 4713 4714Object* Oddball::Initialize(const char* to_string, Object* to_number) { 4715 Object* symbol = Heap::LookupAsciiSymbol(to_string); 4716 if (symbol->IsFailure()) return symbol; 4717 set_to_string(String::cast(symbol)); 4718 set_to_number(to_number); 4719 return this; 4720} 4721 4722 4723bool SharedFunctionInfo::HasSourceCode() { 4724 return !script()->IsUndefined() && 4725 !Script::cast(script())->source()->IsUndefined(); 4726} 4727 4728 4729Object* SharedFunctionInfo::GetSourceCode() { 4730 HandleScope scope; 4731 if (script()->IsUndefined()) return Heap::undefined_value(); 4732 Object* source = Script::cast(script())->source(); 4733 if (source->IsUndefined()) return Heap::undefined_value(); 4734 return *SubString(Handle<String>(String::cast(source)), 4735 start_position(), end_position()); 4736} 4737 4738 4739int SharedFunctionInfo::CalculateInstanceSize() { 4740 int instance_size = 4741 JSObject::kHeaderSize + 4742 expected_nof_properties() * kPointerSize; 4743 if (instance_size > JSObject::kMaxInstanceSize) { 4744 instance_size = JSObject::kMaxInstanceSize; 4745 } 4746 return instance_size; 4747} 4748 4749 4750int SharedFunctionInfo::CalculateInObjectProperties() { 4751 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize; 4752} 4753 4754 4755bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) { 4756 // Check the basic conditions for generating inline constructor code. 4757 if (!FLAG_inline_new 4758 || !has_only_simple_this_property_assignments() 4759 || this_property_assignments_count() == 0) { 4760 return false; 4761 } 4762 4763 // If the prototype is null inline constructors cause no problems. 4764 if (!prototype->IsJSObject()) { 4765 ASSERT(prototype->IsNull()); 4766 return true; 4767 } 4768 4769 // Traverse the proposed prototype chain looking for setters for properties of 4770 // the same names as are set by the inline constructor. 4771 for (Object* obj = prototype; 4772 obj != Heap::null_value(); 4773 obj = obj->GetPrototype()) { 4774 JSObject* js_object = JSObject::cast(obj); 4775 for (int i = 0; i < this_property_assignments_count(); i++) { 4776 LookupResult result; 4777 String* name = GetThisPropertyAssignmentName(i); 4778 js_object->LocalLookupRealNamedProperty(name, &result); 4779 if (result.IsProperty() && result.type() == CALLBACKS) { 4780 return false; 4781 } 4782 } 4783 } 4784 4785 return true; 4786} 4787 4788 4789void SharedFunctionInfo::SetThisPropertyAssignmentsInfo( 4790 bool only_simple_this_property_assignments, 4791 FixedArray* assignments) { 4792 set_compiler_hints(BooleanBit::set(compiler_hints(), 4793 kHasOnlySimpleThisPropertyAssignments, 4794 only_simple_this_property_assignments)); 4795 set_this_property_assignments(assignments); 4796 set_this_property_assignments_count(assignments->length() / 3); 4797} 4798 4799 4800void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() { 4801 set_compiler_hints(BooleanBit::set(compiler_hints(), 4802 kHasOnlySimpleThisPropertyAssignments, 4803 false)); 4804 set_this_property_assignments(Heap::undefined_value()); 4805 set_this_property_assignments_count(0); 4806} 4807 4808 4809String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) { 4810 Object* obj = this_property_assignments(); 4811 ASSERT(obj->IsFixedArray()); 4812 ASSERT(index < this_property_assignments_count()); 4813 obj = FixedArray::cast(obj)->get(index * 3); 4814 ASSERT(obj->IsString()); 4815 return String::cast(obj); 4816} 4817 4818 4819bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) { 4820 Object* obj = this_property_assignments(); 4821 ASSERT(obj->IsFixedArray()); 4822 ASSERT(index < this_property_assignments_count()); 4823 obj = FixedArray::cast(obj)->get(index * 3 + 1); 4824 return Smi::cast(obj)->value() != -1; 4825} 4826 4827 4828int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) { 4829 ASSERT(IsThisPropertyAssignmentArgument(index)); 4830 Object* obj = 4831 FixedArray::cast(this_property_assignments())->get(index * 3 + 1); 4832 return Smi::cast(obj)->value(); 4833} 4834 4835 4836Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) { 4837 ASSERT(!IsThisPropertyAssignmentArgument(index)); 4838 Object* obj = 4839 FixedArray::cast(this_property_assignments())->get(index * 3 + 2); 4840 return obj; 4841} 4842 4843 4844// Support function for printing the source code to a StringStream 4845// without any allocation in the heap. 4846void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator, 4847 int max_length) { 4848 // For some native functions there is no source. 4849 if (script()->IsUndefined() || 4850 Script::cast(script())->source()->IsUndefined()) { 4851 accumulator->Add("<No Source>"); 4852 return; 4853 } 4854 4855 // Get the source for the script which this function came from. 4856 // Don't use String::cast because we don't want more assertion errors while 4857 // we are already creating a stack dump. 4858 String* script_source = 4859 reinterpret_cast<String*>(Script::cast(script())->source()); 4860 4861 if (!script_source->LooksValid()) { 4862 accumulator->Add("<Invalid Source>"); 4863 return; 4864 } 4865 4866 if (!is_toplevel()) { 4867 accumulator->Add("function "); 4868 Object* name = this->name(); 4869 if (name->IsString() && String::cast(name)->length() > 0) { 4870 accumulator->PrintName(name); 4871 } 4872 } 4873 4874 int len = end_position() - start_position(); 4875 if (len > max_length) { 4876 accumulator->Put(script_source, 4877 start_position(), 4878 start_position() + max_length); 4879 accumulator->Add("...\n"); 4880 } else { 4881 accumulator->Put(script_source, start_position(), end_position()); 4882 } 4883} 4884 4885 4886void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) { 4887 IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize); 4888 IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize); 4889 IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize); 4890 IteratePointers(v, kThisPropertyAssignmentsOffset, 4891 kThisPropertyAssignmentsOffset + kPointerSize); 4892} 4893 4894 4895void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { 4896 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); 4897 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); 4898 Object* old_target = target; 4899 VisitPointer(&target); 4900 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. 4901} 4902 4903 4904void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) { 4905 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) && 4906 rinfo->IsPatchedReturnSequence()); 4907 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address()); 4908 Object* old_target = target; 4909 VisitPointer(&target); 4910 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. 4911} 4912 4913 4914void Code::CodeIterateBody(ObjectVisitor* v) { 4915 int mode_mask = RelocInfo::kCodeTargetMask | 4916 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | 4917 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | 4918 RelocInfo::ModeMask(RelocInfo::JS_RETURN) | 4919 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); 4920 4921 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) { 4922 RelocInfo::Mode rmode = it.rinfo()->rmode(); 4923 if (rmode == RelocInfo::EMBEDDED_OBJECT) { 4924 v->VisitPointer(it.rinfo()->target_object_address()); 4925 } else if (RelocInfo::IsCodeTarget(rmode)) { 4926 v->VisitCodeTarget(it.rinfo()); 4927 } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) { 4928 v->VisitExternalReference(it.rinfo()->target_reference_address()); 4929#ifdef ENABLE_DEBUGGER_SUPPORT 4930 } else if (Debug::has_break_points() && 4931 RelocInfo::IsJSReturn(rmode) && 4932 it.rinfo()->IsPatchedReturnSequence()) { 4933 v->VisitDebugTarget(it.rinfo()); 4934#endif 4935 } else if (rmode == RelocInfo::RUNTIME_ENTRY) { 4936 v->VisitRuntimeEntry(it.rinfo()); 4937 } 4938 } 4939 4940 ScopeInfo<>::IterateScopeInfo(this, v); 4941} 4942 4943 4944void Code::Relocate(intptr_t delta) { 4945 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) { 4946 it.rinfo()->apply(delta); 4947 } 4948 CPU::FlushICache(instruction_start(), instruction_size()); 4949} 4950 4951 4952void Code::CopyFrom(const CodeDesc& desc) { 4953 // copy code 4954 memmove(instruction_start(), desc.buffer, desc.instr_size); 4955 4956 // fill gap with zero bytes 4957 { byte* p = instruction_start() + desc.instr_size; 4958 byte* q = relocation_start(); 4959 while (p < q) { 4960 *p++ = 0; 4961 } 4962 } 4963 4964 // copy reloc info 4965 memmove(relocation_start(), 4966 desc.buffer + desc.buffer_size - desc.reloc_size, 4967 desc.reloc_size); 4968 4969 // unbox handles and relocate 4970 intptr_t delta = instruction_start() - desc.buffer; 4971 int mode_mask = RelocInfo::kCodeTargetMask | 4972 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | 4973 RelocInfo::kApplyMask; 4974 Assembler* origin = desc.origin; // Needed to find target_object on X64. 4975 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) { 4976 RelocInfo::Mode mode = it.rinfo()->rmode(); 4977 if (mode == RelocInfo::EMBEDDED_OBJECT) { 4978 Handle<Object> p = it.rinfo()->target_object_handle(origin); 4979 it.rinfo()->set_target_object(*p); 4980 } else if (RelocInfo::IsCodeTarget(mode)) { 4981 // rewrite code handles in inline cache targets to direct 4982 // pointers to the first instruction in the code object 4983 Handle<Object> p = it.rinfo()->target_object_handle(origin); 4984 Code* code = Code::cast(*p); 4985 it.rinfo()->set_target_address(code->instruction_start()); 4986 } else { 4987 it.rinfo()->apply(delta); 4988 } 4989 } 4990 CPU::FlushICache(instruction_start(), instruction_size()); 4991} 4992 4993 4994// Locate the source position which is closest to the address in the code. This 4995// is using the source position information embedded in the relocation info. 4996// The position returned is relative to the beginning of the script where the 4997// source for this function is found. 4998int Code::SourcePosition(Address pc) { 4999 int distance = kMaxInt; 5000 int position = RelocInfo::kNoPosition; // Initially no position found. 5001 // Run through all the relocation info to find the best matching source 5002 // position. All the code needs to be considered as the sequence of the 5003 // instructions in the code does not necessarily follow the same order as the 5004 // source. 5005 RelocIterator it(this, RelocInfo::kPositionMask); 5006 while (!it.done()) { 5007 // Only look at positions after the current pc. 5008 if (it.rinfo()->pc() < pc) { 5009 // Get position and distance. 5010 5011 int dist = static_cast<int>(pc - it.rinfo()->pc()); 5012 int pos = static_cast<int>(it.rinfo()->data()); 5013 // If this position is closer than the current candidate or if it has the 5014 // same distance as the current candidate and the position is higher then 5015 // this position is the new candidate. 5016 if ((dist < distance) || 5017 (dist == distance && pos > position)) { 5018 position = pos; 5019 distance = dist; 5020 } 5021 } 5022 it.next(); 5023 } 5024 return position; 5025} 5026 5027 5028// Same as Code::SourcePosition above except it only looks for statement 5029// positions. 5030int Code::SourceStatementPosition(Address pc) { 5031 // First find the position as close as possible using all position 5032 // information. 5033 int position = SourcePosition(pc); 5034 // Now find the closest statement position before the position. 5035 int statement_position = 0; 5036 RelocIterator it(this, RelocInfo::kPositionMask); 5037 while (!it.done()) { 5038 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) { 5039 int p = static_cast<int>(it.rinfo()->data()); 5040 if (statement_position < p && p <= position) { 5041 statement_position = p; 5042 } 5043 } 5044 it.next(); 5045 } 5046 return statement_position; 5047} 5048 5049 5050#ifdef ENABLE_DISASSEMBLER 5051// Identify kind of code. 5052const char* Code::Kind2String(Kind kind) { 5053 switch (kind) { 5054 case FUNCTION: return "FUNCTION"; 5055 case STUB: return "STUB"; 5056 case BUILTIN: return "BUILTIN"; 5057 case LOAD_IC: return "LOAD_IC"; 5058 case KEYED_LOAD_IC: return "KEYED_LOAD_IC"; 5059 case STORE_IC: return "STORE_IC"; 5060 case KEYED_STORE_IC: return "KEYED_STORE_IC"; 5061 case CALL_IC: return "CALL_IC"; 5062 } 5063 UNREACHABLE(); 5064 return NULL; 5065} 5066 5067 5068const char* Code::ICState2String(InlineCacheState state) { 5069 switch (state) { 5070 case UNINITIALIZED: return "UNINITIALIZED"; 5071 case PREMONOMORPHIC: return "PREMONOMORPHIC"; 5072 case MONOMORPHIC: return "MONOMORPHIC"; 5073 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE"; 5074 case MEGAMORPHIC: return "MEGAMORPHIC"; 5075 case DEBUG_BREAK: return "DEBUG_BREAK"; 5076 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN"; 5077 } 5078 UNREACHABLE(); 5079 return NULL; 5080} 5081 5082 5083const char* Code::PropertyType2String(PropertyType type) { 5084 switch (type) { 5085 case NORMAL: return "NORMAL"; 5086 case FIELD: return "FIELD"; 5087 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION"; 5088 case CALLBACKS: return "CALLBACKS"; 5089 case INTERCEPTOR: return "INTERCEPTOR"; 5090 case MAP_TRANSITION: return "MAP_TRANSITION"; 5091 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION"; 5092 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR"; 5093 } 5094 UNREACHABLE(); 5095 return NULL; 5096} 5097 5098void Code::Disassemble(const char* name) { 5099 PrintF("kind = %s\n", Kind2String(kind())); 5100 if (is_inline_cache_stub()) { 5101 PrintF("ic_state = %s\n", ICState2String(ic_state())); 5102 PrintF("ic_in_loop = %d\n", ic_in_loop() == IN_LOOP); 5103 if (ic_state() == MONOMORPHIC) { 5104 PrintF("type = %s\n", PropertyType2String(type())); 5105 } 5106 } 5107 if ((name != NULL) && (name[0] != '\0')) { 5108 PrintF("name = %s\n", name); 5109 } 5110 5111 PrintF("Instructions (size = %d)\n", instruction_size()); 5112 Disassembler::Decode(NULL, this); 5113 PrintF("\n"); 5114 5115 PrintF("RelocInfo (size = %d)\n", relocation_size()); 5116 for (RelocIterator it(this); !it.done(); it.next()) 5117 it.rinfo()->Print(); 5118 PrintF("\n"); 5119} 5120#endif // ENABLE_DISASSEMBLER 5121 5122 5123void JSObject::SetFastElements(FixedArray* elems) { 5124 // We should never end in here with a pixel or external array. 5125 ASSERT(!HasPixelElements() && !HasExternalArrayElements()); 5126#ifdef DEBUG 5127 // Check the provided array is filled with the_hole. 5128 uint32_t len = static_cast<uint32_t>(elems->length()); 5129 for (uint32_t i = 0; i < len; i++) ASSERT(elems->get(i)->IsTheHole()); 5130#endif 5131 AssertNoAllocation no_gc; 5132 WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc); 5133 switch (GetElementsKind()) { 5134 case FAST_ELEMENTS: { 5135 FixedArray* old_elements = FixedArray::cast(elements()); 5136 uint32_t old_length = static_cast<uint32_t>(old_elements->length()); 5137 // Fill out the new array with this content and array holes. 5138 for (uint32_t i = 0; i < old_length; i++) { 5139 elems->set(i, old_elements->get(i), mode); 5140 } 5141 break; 5142 } 5143 case DICTIONARY_ELEMENTS: { 5144 NumberDictionary* dictionary = NumberDictionary::cast(elements()); 5145 for (int i = 0; i < dictionary->Capacity(); i++) { 5146 Object* key = dictionary->KeyAt(i); 5147 if (key->IsNumber()) { 5148 uint32_t entry = static_cast<uint32_t>(key->Number()); 5149 elems->set(entry, dictionary->ValueAt(i), mode); 5150 } 5151 } 5152 break; 5153 } 5154 default: 5155 UNREACHABLE(); 5156 break; 5157 } 5158 set_elements(elems); 5159} 5160 5161 5162Object* JSObject::SetSlowElements(Object* len) { 5163 // We should never end in here with a pixel or external array. 5164 ASSERT(!HasPixelElements() && !HasExternalArrayElements()); 5165 5166 uint32_t new_length = static_cast<uint32_t>(len->Number()); 5167 5168 switch (GetElementsKind()) { 5169 case FAST_ELEMENTS: { 5170 // Make sure we never try to shrink dense arrays into sparse arrays. 5171 ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <= 5172 new_length); 5173 Object* obj = NormalizeElements(); 5174 if (obj->IsFailure()) return obj; 5175 5176 // Update length for JSArrays. 5177 if (IsJSArray()) JSArray::cast(this)->set_length(len); 5178 break; 5179 } 5180 case DICTIONARY_ELEMENTS: { 5181 if (IsJSArray()) { 5182 uint32_t old_length = 5183 static_cast<uint32_t>(JSArray::cast(this)->length()->Number()); 5184 element_dictionary()->RemoveNumberEntries(new_length, old_length), 5185 JSArray::cast(this)->set_length(len); 5186 } 5187 break; 5188 } 5189 default: 5190 UNREACHABLE(); 5191 break; 5192 } 5193 return this; 5194} 5195 5196 5197Object* JSArray::Initialize(int capacity) { 5198 ASSERT(capacity >= 0); 5199 set_length(Smi::FromInt(0)); 5200 FixedArray* new_elements; 5201 if (capacity == 0) { 5202 new_elements = Heap::empty_fixed_array(); 5203 } else { 5204 Object* obj = Heap::AllocateFixedArrayWithHoles(capacity); 5205 if (obj->IsFailure()) return obj; 5206 new_elements = FixedArray::cast(obj); 5207 } 5208 set_elements(new_elements); 5209 return this; 5210} 5211 5212 5213void JSArray::Expand(int required_size) { 5214 Handle<JSArray> self(this); 5215 Handle<FixedArray> old_backing(FixedArray::cast(elements())); 5216 int old_size = old_backing->length(); 5217 int new_size = required_size > old_size ? required_size : old_size; 5218 Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size); 5219 // Can't use this any more now because we may have had a GC! 5220 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i)); 5221 self->SetContent(*new_backing); 5222} 5223 5224 5225// Computes the new capacity when expanding the elements of a JSObject. 5226static int NewElementsCapacity(int old_capacity) { 5227 // (old_capacity + 50%) + 16 5228 return old_capacity + (old_capacity >> 1) + 16; 5229} 5230 5231 5232static Object* ArrayLengthRangeError() { 5233 HandleScope scope; 5234 return Top::Throw(*Factory::NewRangeError("invalid_array_length", 5235 HandleVector<Object>(NULL, 0))); 5236} 5237 5238 5239Object* JSObject::SetElementsLength(Object* len) { 5240 // We should never end in here with a pixel or external array. 5241 ASSERT(!HasPixelElements() && !HasExternalArrayElements()); 5242 5243 Object* smi_length = len->ToSmi(); 5244 if (smi_length->IsSmi()) { 5245 int value = Smi::cast(smi_length)->value(); 5246 if (value < 0) return ArrayLengthRangeError(); 5247 switch (GetElementsKind()) { 5248 case FAST_ELEMENTS: { 5249 int old_capacity = FixedArray::cast(elements())->length(); 5250 if (value <= old_capacity) { 5251 if (IsJSArray()) { 5252 int old_length = FastD2I(JSArray::cast(this)->length()->Number()); 5253 // NOTE: We may be able to optimize this by removing the 5254 // last part of the elements backing storage array and 5255 // setting the capacity to the new size. 5256 for (int i = value; i < old_length; i++) { 5257 FixedArray::cast(elements())->set_the_hole(i); 5258 } 5259 JSArray::cast(this)->set_length(Smi::cast(smi_length)); 5260 } 5261 return this; 5262 } 5263 int min = NewElementsCapacity(old_capacity); 5264 int new_capacity = value > min ? value : min; 5265 if (new_capacity <= kMaxFastElementsLength || 5266 !ShouldConvertToSlowElements(new_capacity)) { 5267 Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity); 5268 if (obj->IsFailure()) return obj; 5269 if (IsJSArray()) { 5270 JSArray::cast(this)->set_length(Smi::cast(smi_length)); 5271 } 5272 SetFastElements(FixedArray::cast(obj)); 5273 return this; 5274 } 5275 break; 5276 } 5277 case DICTIONARY_ELEMENTS: { 5278 if (IsJSArray()) { 5279 if (value == 0) { 5280 // If the length of a slow array is reset to zero, we clear 5281 // the array and flush backing storage. This has the added 5282 // benefit that the array returns to fast mode. 5283 initialize_elements(); 5284 } else { 5285 // Remove deleted elements. 5286 uint32_t old_length = 5287 static_cast<uint32_t>(JSArray::cast(this)->length()->Number()); 5288 element_dictionary()->RemoveNumberEntries(value, old_length); 5289 } 5290 JSArray::cast(this)->set_length(Smi::cast(smi_length)); 5291 } 5292 return this; 5293 } 5294 default: 5295 UNREACHABLE(); 5296 break; 5297 } 5298 } 5299 5300 // General slow case. 5301 if (len->IsNumber()) { 5302 uint32_t length; 5303 if (Array::IndexFromObject(len, &length)) { 5304 return SetSlowElements(len); 5305 } else { 5306 return ArrayLengthRangeError(); 5307 } 5308 } 5309 5310 // len is not a number so make the array size one and 5311 // set only element to len. 5312 Object* obj = Heap::AllocateFixedArray(1); 5313 if (obj->IsFailure()) return obj; 5314 FixedArray::cast(obj)->set(0, len); 5315 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1)); 5316 set_elements(FixedArray::cast(obj)); 5317 return this; 5318} 5319 5320 5321Object* JSObject::SetPrototype(Object* value, 5322 bool skip_hidden_prototypes) { 5323 // Silently ignore the change if value is not a JSObject or null. 5324 // SpiderMonkey behaves this way. 5325 if (!value->IsJSObject() && !value->IsNull()) return value; 5326 5327 // Before we can set the prototype we need to be sure 5328 // prototype cycles are prevented. 5329 // It is sufficient to validate that the receiver is not in the new prototype 5330 // chain. 5331 for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) { 5332 if (JSObject::cast(pt) == this) { 5333 // Cycle detected. 5334 HandleScope scope; 5335 return Top::Throw(*Factory::NewError("cyclic_proto", 5336 HandleVector<Object>(NULL, 0))); 5337 } 5338 } 5339 5340 JSObject* real_receiver = this; 5341 5342 if (skip_hidden_prototypes) { 5343 // Find the first object in the chain whose prototype object is not 5344 // hidden and set the new prototype on that object. 5345 Object* current_proto = real_receiver->GetPrototype(); 5346 while (current_proto->IsJSObject() && 5347 JSObject::cast(current_proto)->map()->is_hidden_prototype()) { 5348 real_receiver = JSObject::cast(current_proto); 5349 current_proto = current_proto->GetPrototype(); 5350 } 5351 } 5352 5353 // Set the new prototype of the object. 5354 Object* new_map = real_receiver->map()->CopyDropTransitions(); 5355 if (new_map->IsFailure()) return new_map; 5356 Map::cast(new_map)->set_prototype(value); 5357 real_receiver->set_map(Map::cast(new_map)); 5358 5359 return value; 5360} 5361 5362 5363bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) { 5364 switch (GetElementsKind()) { 5365 case FAST_ELEMENTS: { 5366 uint32_t length = IsJSArray() ? 5367 static_cast<uint32_t> 5368 (Smi::cast(JSArray::cast(this)->length())->value()) : 5369 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 5370 if ((index < length) && 5371 !FixedArray::cast(elements())->get(index)->IsTheHole()) { 5372 return true; 5373 } 5374 break; 5375 } 5376 case PIXEL_ELEMENTS: { 5377 // TODO(iposva): Add testcase. 5378 PixelArray* pixels = PixelArray::cast(elements()); 5379 if (index < static_cast<uint32_t>(pixels->length())) { 5380 return true; 5381 } 5382 break; 5383 } 5384 case EXTERNAL_BYTE_ELEMENTS: 5385 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 5386 case EXTERNAL_SHORT_ELEMENTS: 5387 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 5388 case EXTERNAL_INT_ELEMENTS: 5389 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 5390 case EXTERNAL_FLOAT_ELEMENTS: { 5391 // TODO(kbr): Add testcase. 5392 ExternalArray* array = ExternalArray::cast(elements()); 5393 if (index < static_cast<uint32_t>(array->length())) { 5394 return true; 5395 } 5396 break; 5397 } 5398 case DICTIONARY_ELEMENTS: { 5399 if (element_dictionary()->FindEntry(index) 5400 != NumberDictionary::kNotFound) { 5401 return true; 5402 } 5403 break; 5404 } 5405 default: 5406 UNREACHABLE(); 5407 break; 5408 } 5409 5410 // Handle [] on String objects. 5411 if (this->IsStringObjectWithCharacterAt(index)) return true; 5412 5413 Object* pt = GetPrototype(); 5414 if (pt == Heap::null_value()) return false; 5415 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index); 5416} 5417 5418 5419bool JSObject::HasElementWithInterceptor(JSObject* receiver, uint32_t index) { 5420 // Make sure that the top context does not change when doing 5421 // callbacks or interceptor calls. 5422 AssertNoContextChange ncc; 5423 HandleScope scope; 5424 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 5425 Handle<JSObject> receiver_handle(receiver); 5426 Handle<JSObject> holder_handle(this); 5427 CustomArguments args(interceptor->data(), receiver, this); 5428 v8::AccessorInfo info(args.end()); 5429 if (!interceptor->query()->IsUndefined()) { 5430 v8::IndexedPropertyQuery query = 5431 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); 5432 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); 5433 v8::Handle<v8::Boolean> result; 5434 { 5435 // Leaving JavaScript. 5436 VMState state(EXTERNAL); 5437 result = query(index, info); 5438 } 5439 if (!result.IsEmpty()) return result->IsTrue(); 5440 } else if (!interceptor->getter()->IsUndefined()) { 5441 v8::IndexedPropertyGetter getter = 5442 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); 5443 LOG(ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index)); 5444 v8::Handle<v8::Value> result; 5445 { 5446 // Leaving JavaScript. 5447 VMState state(EXTERNAL); 5448 result = getter(index, info); 5449 } 5450 if (!result.IsEmpty()) return true; 5451 } 5452 return holder_handle->HasElementPostInterceptor(*receiver_handle, index); 5453} 5454 5455 5456bool JSObject::HasLocalElement(uint32_t index) { 5457 // Check access rights if needed. 5458 if (IsAccessCheckNeeded() && 5459 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) { 5460 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS); 5461 return false; 5462 } 5463 5464 // Check for lookup interceptor 5465 if (HasIndexedInterceptor()) { 5466 return HasElementWithInterceptor(this, index); 5467 } 5468 5469 // Handle [] on String objects. 5470 if (this->IsStringObjectWithCharacterAt(index)) return true; 5471 5472 switch (GetElementsKind()) { 5473 case FAST_ELEMENTS: { 5474 uint32_t length = IsJSArray() ? 5475 static_cast<uint32_t> 5476 (Smi::cast(JSArray::cast(this)->length())->value()) : 5477 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 5478 return (index < length) && 5479 !FixedArray::cast(elements())->get(index)->IsTheHole(); 5480 } 5481 case PIXEL_ELEMENTS: { 5482 PixelArray* pixels = PixelArray::cast(elements()); 5483 return (index < static_cast<uint32_t>(pixels->length())); 5484 } 5485 case EXTERNAL_BYTE_ELEMENTS: 5486 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 5487 case EXTERNAL_SHORT_ELEMENTS: 5488 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 5489 case EXTERNAL_INT_ELEMENTS: 5490 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 5491 case EXTERNAL_FLOAT_ELEMENTS: { 5492 ExternalArray* array = ExternalArray::cast(elements()); 5493 return (index < static_cast<uint32_t>(array->length())); 5494 } 5495 case DICTIONARY_ELEMENTS: { 5496 return element_dictionary()->FindEntry(index) 5497 != NumberDictionary::kNotFound; 5498 } 5499 default: 5500 UNREACHABLE(); 5501 break; 5502 } 5503 UNREACHABLE(); 5504 return Heap::null_value(); 5505} 5506 5507 5508bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) { 5509 // Check access rights if needed. 5510 if (IsAccessCheckNeeded() && 5511 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) { 5512 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS); 5513 return false; 5514 } 5515 5516 // Check for lookup interceptor 5517 if (HasIndexedInterceptor()) { 5518 return HasElementWithInterceptor(receiver, index); 5519 } 5520 5521 switch (GetElementsKind()) { 5522 case FAST_ELEMENTS: { 5523 uint32_t length = IsJSArray() ? 5524 static_cast<uint32_t> 5525 (Smi::cast(JSArray::cast(this)->length())->value()) : 5526 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 5527 if ((index < length) && 5528 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true; 5529 break; 5530 } 5531 case PIXEL_ELEMENTS: { 5532 PixelArray* pixels = PixelArray::cast(elements()); 5533 if (index < static_cast<uint32_t>(pixels->length())) { 5534 return true; 5535 } 5536 break; 5537 } 5538 case EXTERNAL_BYTE_ELEMENTS: 5539 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 5540 case EXTERNAL_SHORT_ELEMENTS: 5541 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 5542 case EXTERNAL_INT_ELEMENTS: 5543 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 5544 case EXTERNAL_FLOAT_ELEMENTS: { 5545 ExternalArray* array = ExternalArray::cast(elements()); 5546 if (index < static_cast<uint32_t>(array->length())) { 5547 return true; 5548 } 5549 break; 5550 } 5551 case DICTIONARY_ELEMENTS: { 5552 if (element_dictionary()->FindEntry(index) 5553 != NumberDictionary::kNotFound) { 5554 return true; 5555 } 5556 break; 5557 } 5558 default: 5559 UNREACHABLE(); 5560 break; 5561 } 5562 5563 // Handle [] on String objects. 5564 if (this->IsStringObjectWithCharacterAt(index)) return true; 5565 5566 Object* pt = GetPrototype(); 5567 if (pt == Heap::null_value()) return false; 5568 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index); 5569} 5570 5571 5572Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) { 5573 // Make sure that the top context does not change when doing 5574 // callbacks or interceptor calls. 5575 AssertNoContextChange ncc; 5576 HandleScope scope; 5577 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 5578 Handle<JSObject> this_handle(this); 5579 Handle<Object> value_handle(value); 5580 if (!interceptor->setter()->IsUndefined()) { 5581 v8::IndexedPropertySetter setter = 5582 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter()); 5583 LOG(ApiIndexedPropertyAccess("interceptor-indexed-set", this, index)); 5584 CustomArguments args(interceptor->data(), this, this); 5585 v8::AccessorInfo info(args.end()); 5586 v8::Handle<v8::Value> result; 5587 { 5588 // Leaving JavaScript. 5589 VMState state(EXTERNAL); 5590 result = setter(index, v8::Utils::ToLocal(value_handle), info); 5591 } 5592 RETURN_IF_SCHEDULED_EXCEPTION(); 5593 if (!result.IsEmpty()) return *value_handle; 5594 } 5595 Object* raw_result = 5596 this_handle->SetElementWithoutInterceptor(index, *value_handle); 5597 RETURN_IF_SCHEDULED_EXCEPTION(); 5598 return raw_result; 5599} 5600 5601 5602// Adding n elements in fast case is O(n*n). 5603// Note: revisit design to have dual undefined values to capture absent 5604// elements. 5605Object* JSObject::SetFastElement(uint32_t index, Object* value) { 5606 ASSERT(HasFastElements()); 5607 5608 FixedArray* elms = FixedArray::cast(elements()); 5609 uint32_t elms_length = static_cast<uint32_t>(elms->length()); 5610 5611 if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) { 5612 Object* setter = LookupCallbackSetterInPrototypes(index); 5613 if (setter->IsJSFunction()) { 5614 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value); 5615 } 5616 } 5617 5618 // Check whether there is extra space in fixed array.. 5619 if (index < elms_length) { 5620 elms->set(index, value); 5621 if (IsJSArray()) { 5622 // Update the length of the array if needed. 5623 uint32_t array_length = 0; 5624 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), 5625 &array_length)); 5626 if (index >= array_length) { 5627 JSArray::cast(this)->set_length(Smi::FromInt(index + 1)); 5628 } 5629 } 5630 return value; 5631 } 5632 5633 // Allow gap in fast case. 5634 if ((index - elms_length) < kMaxGap) { 5635 // Try allocating extra space. 5636 int new_capacity = NewElementsCapacity(index+1); 5637 if (new_capacity <= kMaxFastElementsLength || 5638 !ShouldConvertToSlowElements(new_capacity)) { 5639 ASSERT(static_cast<uint32_t>(new_capacity) > index); 5640 Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity); 5641 if (obj->IsFailure()) return obj; 5642 SetFastElements(FixedArray::cast(obj)); 5643 if (IsJSArray()) { 5644 JSArray::cast(this)->set_length(Smi::FromInt(index + 1)); 5645 } 5646 FixedArray::cast(elements())->set(index, value); 5647 return value; 5648 } 5649 } 5650 5651 // Otherwise default to slow case. 5652 Object* obj = NormalizeElements(); 5653 if (obj->IsFailure()) return obj; 5654 ASSERT(HasDictionaryElements()); 5655 return SetElement(index, value); 5656} 5657 5658Object* JSObject::SetElement(uint32_t index, Object* value) { 5659 // Check access rights if needed. 5660 if (IsAccessCheckNeeded() && 5661 !Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) { 5662 Top::ReportFailedAccessCheck(this, v8::ACCESS_SET); 5663 return value; 5664 } 5665 5666 if (IsJSGlobalProxy()) { 5667 Object* proto = GetPrototype(); 5668 if (proto->IsNull()) return value; 5669 ASSERT(proto->IsJSGlobalObject()); 5670 return JSObject::cast(proto)->SetElement(index, value); 5671 } 5672 5673 // Check for lookup interceptor 5674 if (HasIndexedInterceptor()) { 5675 return SetElementWithInterceptor(index, value); 5676 } 5677 5678 return SetElementWithoutInterceptor(index, value); 5679} 5680 5681 5682Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) { 5683 switch (GetElementsKind()) { 5684 case FAST_ELEMENTS: 5685 // Fast case. 5686 return SetFastElement(index, value); 5687 case PIXEL_ELEMENTS: { 5688 PixelArray* pixels = PixelArray::cast(elements()); 5689 return pixels->SetValue(index, value); 5690 } 5691 case EXTERNAL_BYTE_ELEMENTS: { 5692 ExternalByteArray* array = ExternalByteArray::cast(elements()); 5693 return array->SetValue(index, value); 5694 } 5695 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { 5696 ExternalUnsignedByteArray* array = 5697 ExternalUnsignedByteArray::cast(elements()); 5698 return array->SetValue(index, value); 5699 } 5700 case EXTERNAL_SHORT_ELEMENTS: { 5701 ExternalShortArray* array = ExternalShortArray::cast(elements()); 5702 return array->SetValue(index, value); 5703 } 5704 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { 5705 ExternalUnsignedShortArray* array = 5706 ExternalUnsignedShortArray::cast(elements()); 5707 return array->SetValue(index, value); 5708 } 5709 case EXTERNAL_INT_ELEMENTS: { 5710 ExternalIntArray* array = ExternalIntArray::cast(elements()); 5711 return array->SetValue(index, value); 5712 } 5713 case EXTERNAL_UNSIGNED_INT_ELEMENTS: { 5714 ExternalUnsignedIntArray* array = 5715 ExternalUnsignedIntArray::cast(elements()); 5716 return array->SetValue(index, value); 5717 } 5718 case EXTERNAL_FLOAT_ELEMENTS: { 5719 ExternalFloatArray* array = ExternalFloatArray::cast(elements()); 5720 return array->SetValue(index, value); 5721 } 5722 case DICTIONARY_ELEMENTS: { 5723 // Insert element in the dictionary. 5724 FixedArray* elms = FixedArray::cast(elements()); 5725 NumberDictionary* dictionary = NumberDictionary::cast(elms); 5726 5727 int entry = dictionary->FindEntry(index); 5728 if (entry != NumberDictionary::kNotFound) { 5729 Object* element = dictionary->ValueAt(entry); 5730 PropertyDetails details = dictionary->DetailsAt(entry); 5731 if (details.type() == CALLBACKS) { 5732 // Only accessors allowed as elements. 5733 FixedArray* structure = FixedArray::cast(element); 5734 if (structure->get(kSetterIndex)->IsJSFunction()) { 5735 JSFunction* setter = JSFunction::cast(structure->get(kSetterIndex)); 5736 return SetPropertyWithDefinedSetter(setter, value); 5737 } else { 5738 Handle<Object> self(this); 5739 Handle<Object> key(Factory::NewNumberFromUint(index)); 5740 Handle<Object> args[2] = { key, self }; 5741 return Top::Throw(*Factory::NewTypeError("no_setter_in_callback", 5742 HandleVector(args, 2))); 5743 } 5744 } else { 5745 dictionary->UpdateMaxNumberKey(index); 5746 dictionary->ValueAtPut(entry, value); 5747 } 5748 } else { 5749 // Index not already used. Look for an accessor in the prototype chain. 5750 if (!IsJSArray()) { 5751 Object* setter = LookupCallbackSetterInPrototypes(index); 5752 if (setter->IsJSFunction()) { 5753 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), 5754 value); 5755 } 5756 } 5757 Object* result = dictionary->AtNumberPut(index, value); 5758 if (result->IsFailure()) return result; 5759 if (elms != FixedArray::cast(result)) { 5760 set_elements(FixedArray::cast(result)); 5761 } 5762 } 5763 5764 // Update the array length if this JSObject is an array. 5765 if (IsJSArray()) { 5766 JSArray* array = JSArray::cast(this); 5767 Object* return_value = array->JSArrayUpdateLengthFromIndex(index, 5768 value); 5769 if (return_value->IsFailure()) return return_value; 5770 } 5771 5772 // Attempt to put this object back in fast case. 5773 if (ShouldConvertToFastElements()) { 5774 uint32_t new_length = 0; 5775 if (IsJSArray()) { 5776 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), 5777 &new_length)); 5778 JSArray::cast(this)->set_length(Smi::FromInt(new_length)); 5779 } else { 5780 new_length = NumberDictionary::cast(elements())->max_number_key() + 1; 5781 } 5782 Object* obj = Heap::AllocateFixedArrayWithHoles(new_length); 5783 if (obj->IsFailure()) return obj; 5784 SetFastElements(FixedArray::cast(obj)); 5785#ifdef DEBUG 5786 if (FLAG_trace_normalization) { 5787 PrintF("Object elements are fast case again:\n"); 5788 Print(); 5789 } 5790#endif 5791 } 5792 5793 return value; 5794 } 5795 default: 5796 UNREACHABLE(); 5797 break; 5798 } 5799 // All possible cases have been handled above. Add a return to avoid the 5800 // complaints from the compiler. 5801 UNREACHABLE(); 5802 return Heap::null_value(); 5803} 5804 5805 5806Object* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index, Object* value) { 5807 uint32_t old_len = 0; 5808 CHECK(Array::IndexFromObject(length(), &old_len)); 5809 // Check to see if we need to update the length. For now, we make 5810 // sure that the length stays within 32-bits (unsigned). 5811 if (index >= old_len && index != 0xffffffff) { 5812 Object* len = 5813 Heap::NumberFromDouble(static_cast<double>(index) + 1); 5814 if (len->IsFailure()) return len; 5815 set_length(len); 5816 } 5817 return value; 5818} 5819 5820 5821Object* JSObject::GetElementPostInterceptor(JSObject* receiver, 5822 uint32_t index) { 5823 // Get element works for both JSObject and JSArray since 5824 // JSArray::length cannot change. 5825 switch (GetElementsKind()) { 5826 case FAST_ELEMENTS: { 5827 FixedArray* elms = FixedArray::cast(elements()); 5828 if (index < static_cast<uint32_t>(elms->length())) { 5829 Object* value = elms->get(index); 5830 if (!value->IsTheHole()) return value; 5831 } 5832 break; 5833 } 5834 case PIXEL_ELEMENTS: { 5835 // TODO(iposva): Add testcase and implement. 5836 UNIMPLEMENTED(); 5837 break; 5838 } 5839 case EXTERNAL_BYTE_ELEMENTS: 5840 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 5841 case EXTERNAL_SHORT_ELEMENTS: 5842 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 5843 case EXTERNAL_INT_ELEMENTS: 5844 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 5845 case EXTERNAL_FLOAT_ELEMENTS: { 5846 // TODO(kbr): Add testcase and implement. 5847 UNIMPLEMENTED(); 5848 break; 5849 } 5850 case DICTIONARY_ELEMENTS: { 5851 NumberDictionary* dictionary = element_dictionary(); 5852 int entry = dictionary->FindEntry(index); 5853 if (entry != NumberDictionary::kNotFound) { 5854 Object* element = dictionary->ValueAt(entry); 5855 PropertyDetails details = dictionary->DetailsAt(entry); 5856 if (details.type() == CALLBACKS) { 5857 // Only accessors allowed as elements. 5858 FixedArray* structure = FixedArray::cast(element); 5859 Object* getter = structure->get(kGetterIndex); 5860 if (getter->IsJSFunction()) { 5861 return GetPropertyWithDefinedGetter(receiver, 5862 JSFunction::cast(getter)); 5863 } else { 5864 // Getter is not a function. 5865 return Heap::undefined_value(); 5866 } 5867 } 5868 return element; 5869 } 5870 break; 5871 } 5872 default: 5873 UNREACHABLE(); 5874 break; 5875 } 5876 5877 // Continue searching via the prototype chain. 5878 Object* pt = GetPrototype(); 5879 if (pt == Heap::null_value()) return Heap::undefined_value(); 5880 return pt->GetElementWithReceiver(receiver, index); 5881} 5882 5883 5884Object* JSObject::GetElementWithInterceptor(JSObject* receiver, 5885 uint32_t index) { 5886 // Make sure that the top context does not change when doing 5887 // callbacks or interceptor calls. 5888 AssertNoContextChange ncc; 5889 HandleScope scope; 5890 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 5891 Handle<JSObject> this_handle(receiver); 5892 Handle<JSObject> holder_handle(this); 5893 5894 if (!interceptor->getter()->IsUndefined()) { 5895 v8::IndexedPropertyGetter getter = 5896 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); 5897 LOG(ApiIndexedPropertyAccess("interceptor-indexed-get", this, index)); 5898 CustomArguments args(interceptor->data(), receiver, this); 5899 v8::AccessorInfo info(args.end()); 5900 v8::Handle<v8::Value> result; 5901 { 5902 // Leaving JavaScript. 5903 VMState state(EXTERNAL); 5904 result = getter(index, info); 5905 } 5906 RETURN_IF_SCHEDULED_EXCEPTION(); 5907 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result); 5908 } 5909 5910 Object* raw_result = 5911 holder_handle->GetElementPostInterceptor(*this_handle, index); 5912 RETURN_IF_SCHEDULED_EXCEPTION(); 5913 return raw_result; 5914} 5915 5916 5917Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) { 5918 // Check access rights if needed. 5919 if (IsAccessCheckNeeded() && 5920 !Top::MayIndexedAccess(this, index, v8::ACCESS_GET)) { 5921 Top::ReportFailedAccessCheck(this, v8::ACCESS_GET); 5922 return Heap::undefined_value(); 5923 } 5924 5925 if (HasIndexedInterceptor()) { 5926 return GetElementWithInterceptor(receiver, index); 5927 } 5928 5929 // Get element works for both JSObject and JSArray since 5930 // JSArray::length cannot change. 5931 switch (GetElementsKind()) { 5932 case FAST_ELEMENTS: { 5933 FixedArray* elms = FixedArray::cast(elements()); 5934 if (index < static_cast<uint32_t>(elms->length())) { 5935 Object* value = elms->get(index); 5936 if (!value->IsTheHole()) return value; 5937 } 5938 break; 5939 } 5940 case PIXEL_ELEMENTS: { 5941 PixelArray* pixels = PixelArray::cast(elements()); 5942 if (index < static_cast<uint32_t>(pixels->length())) { 5943 uint8_t value = pixels->get(index); 5944 return Smi::FromInt(value); 5945 } 5946 break; 5947 } 5948 case EXTERNAL_BYTE_ELEMENTS: { 5949 ExternalByteArray* array = ExternalByteArray::cast(elements()); 5950 if (index < static_cast<uint32_t>(array->length())) { 5951 int8_t value = array->get(index); 5952 return Smi::FromInt(value); 5953 } 5954 break; 5955 } 5956 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { 5957 ExternalUnsignedByteArray* array = 5958 ExternalUnsignedByteArray::cast(elements()); 5959 if (index < static_cast<uint32_t>(array->length())) { 5960 uint8_t value = array->get(index); 5961 return Smi::FromInt(value); 5962 } 5963 break; 5964 } 5965 case EXTERNAL_SHORT_ELEMENTS: { 5966 ExternalShortArray* array = ExternalShortArray::cast(elements()); 5967 if (index < static_cast<uint32_t>(array->length())) { 5968 int16_t value = array->get(index); 5969 return Smi::FromInt(value); 5970 } 5971 break; 5972 } 5973 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { 5974 ExternalUnsignedShortArray* array = 5975 ExternalUnsignedShortArray::cast(elements()); 5976 if (index < static_cast<uint32_t>(array->length())) { 5977 uint16_t value = array->get(index); 5978 return Smi::FromInt(value); 5979 } 5980 break; 5981 } 5982 case EXTERNAL_INT_ELEMENTS: { 5983 ExternalIntArray* array = ExternalIntArray::cast(elements()); 5984 if (index < static_cast<uint32_t>(array->length())) { 5985 int32_t value = array->get(index); 5986 return Heap::NumberFromInt32(value); 5987 } 5988 break; 5989 } 5990 case EXTERNAL_UNSIGNED_INT_ELEMENTS: { 5991 ExternalUnsignedIntArray* array = 5992 ExternalUnsignedIntArray::cast(elements()); 5993 if (index < static_cast<uint32_t>(array->length())) { 5994 uint32_t value = array->get(index); 5995 return Heap::NumberFromUint32(value); 5996 } 5997 break; 5998 } 5999 case EXTERNAL_FLOAT_ELEMENTS: { 6000 ExternalFloatArray* array = ExternalFloatArray::cast(elements()); 6001 if (index < static_cast<uint32_t>(array->length())) { 6002 float value = array->get(index); 6003 return Heap::AllocateHeapNumber(value); 6004 } 6005 break; 6006 } 6007 case DICTIONARY_ELEMENTS: { 6008 NumberDictionary* dictionary = element_dictionary(); 6009 int entry = dictionary->FindEntry(index); 6010 if (entry != NumberDictionary::kNotFound) { 6011 Object* element = dictionary->ValueAt(entry); 6012 PropertyDetails details = dictionary->DetailsAt(entry); 6013 if (details.type() == CALLBACKS) { 6014 // Only accessors allowed as elements. 6015 FixedArray* structure = FixedArray::cast(element); 6016 Object* getter = structure->get(kGetterIndex); 6017 if (getter->IsJSFunction()) { 6018 return GetPropertyWithDefinedGetter(receiver, 6019 JSFunction::cast(getter)); 6020 } else { 6021 // Getter is not a function. 6022 return Heap::undefined_value(); 6023 } 6024 } 6025 return element; 6026 } 6027 break; 6028 } 6029 } 6030 6031 Object* pt = GetPrototype(); 6032 if (pt == Heap::null_value()) return Heap::undefined_value(); 6033 return pt->GetElementWithReceiver(receiver, index); 6034} 6035 6036 6037bool JSObject::HasDenseElements() { 6038 int capacity = 0; 6039 int number_of_elements = 0; 6040 6041 switch (GetElementsKind()) { 6042 case FAST_ELEMENTS: { 6043 FixedArray* elms = FixedArray::cast(elements()); 6044 capacity = elms->length(); 6045 for (int i = 0; i < capacity; i++) { 6046 if (!elms->get(i)->IsTheHole()) number_of_elements++; 6047 } 6048 break; 6049 } 6050 case PIXEL_ELEMENTS: 6051 case EXTERNAL_BYTE_ELEMENTS: 6052 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 6053 case EXTERNAL_SHORT_ELEMENTS: 6054 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 6055 case EXTERNAL_INT_ELEMENTS: 6056 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 6057 case EXTERNAL_FLOAT_ELEMENTS: { 6058 return true; 6059 } 6060 case DICTIONARY_ELEMENTS: { 6061 NumberDictionary* dictionary = NumberDictionary::cast(elements()); 6062 capacity = dictionary->Capacity(); 6063 number_of_elements = dictionary->NumberOfElements(); 6064 break; 6065 } 6066 default: 6067 UNREACHABLE(); 6068 break; 6069 } 6070 6071 if (capacity == 0) return true; 6072 return (number_of_elements > (capacity / 2)); 6073} 6074 6075 6076bool JSObject::ShouldConvertToSlowElements(int new_capacity) { 6077 ASSERT(HasFastElements()); 6078 // Keep the array in fast case if the current backing storage is 6079 // almost filled and if the new capacity is no more than twice the 6080 // old capacity. 6081 int elements_length = FixedArray::cast(elements())->length(); 6082 return !HasDenseElements() || ((new_capacity / 2) > elements_length); 6083} 6084 6085 6086bool JSObject::ShouldConvertToFastElements() { 6087 ASSERT(HasDictionaryElements()); 6088 NumberDictionary* dictionary = NumberDictionary::cast(elements()); 6089 // If the elements are sparse, we should not go back to fast case. 6090 if (!HasDenseElements()) return false; 6091 // If an element has been added at a very high index in the elements 6092 // dictionary, we cannot go back to fast case. 6093 if (dictionary->requires_slow_elements()) return false; 6094 // An object requiring access checks is never allowed to have fast 6095 // elements. If it had fast elements we would skip security checks. 6096 if (IsAccessCheckNeeded()) return false; 6097 // If the dictionary backing storage takes up roughly half as much 6098 // space as a fast-case backing storage would the array should have 6099 // fast elements. 6100 uint32_t length = 0; 6101 if (IsJSArray()) { 6102 CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), &length)); 6103 } else { 6104 length = dictionary->max_number_key(); 6105 } 6106 return static_cast<uint32_t>(dictionary->Capacity()) >= 6107 (length / (2 * NumberDictionary::kEntrySize)); 6108} 6109 6110 6111// Certain compilers request function template instantiation when they 6112// see the definition of the other template functions in the 6113// class. This requires us to have the template functions put 6114// together, so even though this function belongs in objects-debug.cc, 6115// we keep it here instead to satisfy certain compilers. 6116#ifdef DEBUG 6117template<typename Shape, typename Key> 6118void Dictionary<Shape, Key>::Print() { 6119 int capacity = HashTable<Shape, Key>::Capacity(); 6120 for (int i = 0; i < capacity; i++) { 6121 Object* k = HashTable<Shape, Key>::KeyAt(i); 6122 if (HashTable<Shape, Key>::IsKey(k)) { 6123 PrintF(" "); 6124 if (k->IsString()) { 6125 String::cast(k)->StringPrint(); 6126 } else { 6127 k->ShortPrint(); 6128 } 6129 PrintF(": "); 6130 ValueAt(i)->ShortPrint(); 6131 PrintF("\n"); 6132 } 6133 } 6134} 6135#endif 6136 6137 6138template<typename Shape, typename Key> 6139void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) { 6140 int pos = 0; 6141 int capacity = HashTable<Shape, Key>::Capacity(); 6142 AssertNoAllocation no_gc; 6143 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); 6144 for (int i = 0; i < capacity; i++) { 6145 Object* k = Dictionary<Shape, Key>::KeyAt(i); 6146 if (Dictionary<Shape, Key>::IsKey(k)) { 6147 elements->set(pos++, ValueAt(i), mode); 6148 } 6149 } 6150 ASSERT(pos == elements->length()); 6151} 6152 6153 6154InterceptorInfo* JSObject::GetNamedInterceptor() { 6155 ASSERT(map()->has_named_interceptor()); 6156 JSFunction* constructor = JSFunction::cast(map()->constructor()); 6157 Object* template_info = constructor->shared()->function_data(); 6158 Object* result = 6159 FunctionTemplateInfo::cast(template_info)->named_property_handler(); 6160 return InterceptorInfo::cast(result); 6161} 6162 6163 6164InterceptorInfo* JSObject::GetIndexedInterceptor() { 6165 ASSERT(map()->has_indexed_interceptor()); 6166 JSFunction* constructor = JSFunction::cast(map()->constructor()); 6167 Object* template_info = constructor->shared()->function_data(); 6168 Object* result = 6169 FunctionTemplateInfo::cast(template_info)->indexed_property_handler(); 6170 return InterceptorInfo::cast(result); 6171} 6172 6173 6174Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver, 6175 String* name, 6176 PropertyAttributes* attributes) { 6177 // Check local property in holder, ignore interceptor. 6178 LookupResult result; 6179 LocalLookupRealNamedProperty(name, &result); 6180 if (result.IsProperty()) { 6181 return GetProperty(receiver, &result, name, attributes); 6182 } 6183 // Continue searching via the prototype chain. 6184 Object* pt = GetPrototype(); 6185 *attributes = ABSENT; 6186 if (pt == Heap::null_value()) return Heap::undefined_value(); 6187 return pt->GetPropertyWithReceiver(receiver, name, attributes); 6188} 6189 6190 6191Object* JSObject::GetLocalPropertyPostInterceptor( 6192 JSObject* receiver, 6193 String* name, 6194 PropertyAttributes* attributes) { 6195 // Check local property in holder, ignore interceptor. 6196 LookupResult result; 6197 LocalLookupRealNamedProperty(name, &result); 6198 if (result.IsProperty()) { 6199 return GetProperty(receiver, &result, name, attributes); 6200 } 6201 return Heap::undefined_value(); 6202} 6203 6204 6205Object* JSObject::GetPropertyWithInterceptor( 6206 JSObject* receiver, 6207 String* name, 6208 PropertyAttributes* attributes) { 6209 InterceptorInfo* interceptor = GetNamedInterceptor(); 6210 HandleScope scope; 6211 Handle<JSObject> receiver_handle(receiver); 6212 Handle<JSObject> holder_handle(this); 6213 Handle<String> name_handle(name); 6214 6215 if (!interceptor->getter()->IsUndefined()) { 6216 v8::NamedPropertyGetter getter = 6217 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter()); 6218 LOG(ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name)); 6219 CustomArguments args(interceptor->data(), receiver, this); 6220 v8::AccessorInfo info(args.end()); 6221 v8::Handle<v8::Value> result; 6222 { 6223 // Leaving JavaScript. 6224 VMState state(EXTERNAL); 6225 result = getter(v8::Utils::ToLocal(name_handle), info); 6226 } 6227 RETURN_IF_SCHEDULED_EXCEPTION(); 6228 if (!result.IsEmpty()) { 6229 *attributes = NONE; 6230 return *v8::Utils::OpenHandle(*result); 6231 } 6232 } 6233 6234 Object* result = holder_handle->GetPropertyPostInterceptor( 6235 *receiver_handle, 6236 *name_handle, 6237 attributes); 6238 RETURN_IF_SCHEDULED_EXCEPTION(); 6239 return result; 6240} 6241 6242 6243bool JSObject::HasRealNamedProperty(String* key) { 6244 // Check access rights if needed. 6245 if (IsAccessCheckNeeded() && 6246 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) { 6247 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS); 6248 return false; 6249 } 6250 6251 LookupResult result; 6252 LocalLookupRealNamedProperty(key, &result); 6253 return result.IsProperty() && (result.type() != INTERCEPTOR); 6254} 6255 6256 6257bool JSObject::HasRealElementProperty(uint32_t index) { 6258 // Check access rights if needed. 6259 if (IsAccessCheckNeeded() && 6260 !Top::MayIndexedAccess(this, index, v8::ACCESS_HAS)) { 6261 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS); 6262 return false; 6263 } 6264 6265 // Handle [] on String objects. 6266 if (this->IsStringObjectWithCharacterAt(index)) return true; 6267 6268 switch (GetElementsKind()) { 6269 case FAST_ELEMENTS: { 6270 uint32_t length = IsJSArray() ? 6271 static_cast<uint32_t>( 6272 Smi::cast(JSArray::cast(this)->length())->value()) : 6273 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 6274 return (index < length) && 6275 !FixedArray::cast(elements())->get(index)->IsTheHole(); 6276 } 6277 case PIXEL_ELEMENTS: { 6278 PixelArray* pixels = PixelArray::cast(elements()); 6279 return index < static_cast<uint32_t>(pixels->length()); 6280 } 6281 case EXTERNAL_BYTE_ELEMENTS: 6282 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 6283 case EXTERNAL_SHORT_ELEMENTS: 6284 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 6285 case EXTERNAL_INT_ELEMENTS: 6286 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 6287 case EXTERNAL_FLOAT_ELEMENTS: { 6288 ExternalArray* array = ExternalArray::cast(elements()); 6289 return index < static_cast<uint32_t>(array->length()); 6290 } 6291 case DICTIONARY_ELEMENTS: { 6292 return element_dictionary()->FindEntry(index) 6293 != NumberDictionary::kNotFound; 6294 } 6295 default: 6296 UNREACHABLE(); 6297 break; 6298 } 6299 // All possibilities have been handled above already. 6300 UNREACHABLE(); 6301 return Heap::null_value(); 6302} 6303 6304 6305bool JSObject::HasRealNamedCallbackProperty(String* key) { 6306 // Check access rights if needed. 6307 if (IsAccessCheckNeeded() && 6308 !Top::MayNamedAccess(this, key, v8::ACCESS_HAS)) { 6309 Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS); 6310 return false; 6311 } 6312 6313 LookupResult result; 6314 LocalLookupRealNamedProperty(key, &result); 6315 return result.IsProperty() && (result.type() == CALLBACKS); 6316} 6317 6318 6319int JSObject::NumberOfLocalProperties(PropertyAttributes filter) { 6320 if (HasFastProperties()) { 6321 DescriptorArray* descs = map()->instance_descriptors(); 6322 int result = 0; 6323 for (int i = 0; i < descs->number_of_descriptors(); i++) { 6324 PropertyDetails details = descs->GetDetails(i); 6325 if (details.IsProperty() && (details.attributes() & filter) == 0) { 6326 result++; 6327 } 6328 } 6329 return result; 6330 } else { 6331 return property_dictionary()->NumberOfElementsFilterAttributes(filter); 6332 } 6333} 6334 6335 6336int JSObject::NumberOfEnumProperties() { 6337 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM)); 6338} 6339 6340 6341void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) { 6342 Object* temp = get(i); 6343 set(i, get(j)); 6344 set(j, temp); 6345 if (this != numbers) { 6346 temp = numbers->get(i); 6347 numbers->set(i, numbers->get(j)); 6348 numbers->set(j, temp); 6349 } 6350} 6351 6352 6353static void InsertionSortPairs(FixedArray* content, 6354 FixedArray* numbers, 6355 int len) { 6356 for (int i = 1; i < len; i++) { 6357 int j = i; 6358 while (j > 0 && 6359 (NumberToUint32(numbers->get(j - 1)) > 6360 NumberToUint32(numbers->get(j)))) { 6361 content->SwapPairs(numbers, j - 1, j); 6362 j--; 6363 } 6364 } 6365} 6366 6367 6368void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) { 6369 // In-place heap sort. 6370 ASSERT(content->length() == numbers->length()); 6371 6372 // Bottom-up max-heap construction. 6373 for (int i = 1; i < len; ++i) { 6374 int child_index = i; 6375 while (child_index > 0) { 6376 int parent_index = ((child_index + 1) >> 1) - 1; 6377 uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); 6378 uint32_t child_value = NumberToUint32(numbers->get(child_index)); 6379 if (parent_value < child_value) { 6380 content->SwapPairs(numbers, parent_index, child_index); 6381 } else { 6382 break; 6383 } 6384 child_index = parent_index; 6385 } 6386 } 6387 6388 // Extract elements and create sorted array. 6389 for (int i = len - 1; i > 0; --i) { 6390 // Put max element at the back of the array. 6391 content->SwapPairs(numbers, 0, i); 6392 // Sift down the new top element. 6393 int parent_index = 0; 6394 while (true) { 6395 int child_index = ((parent_index + 1) << 1) - 1; 6396 if (child_index >= i) break; 6397 uint32_t child1_value = NumberToUint32(numbers->get(child_index)); 6398 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1)); 6399 uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); 6400 if (child_index + 1 >= i || child1_value > child2_value) { 6401 if (parent_value > child1_value) break; 6402 content->SwapPairs(numbers, parent_index, child_index); 6403 parent_index = child_index; 6404 } else { 6405 if (parent_value > child2_value) break; 6406 content->SwapPairs(numbers, parent_index, child_index + 1); 6407 parent_index = child_index + 1; 6408 } 6409 } 6410 } 6411} 6412 6413 6414// Sort this array and the numbers as pairs wrt. the (distinct) numbers. 6415void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { 6416 ASSERT(this->length() == numbers->length()); 6417 // For small arrays, simply use insertion sort. 6418 if (len <= 10) { 6419 InsertionSortPairs(this, numbers, len); 6420 return; 6421 } 6422 // Check the range of indices. 6423 uint32_t min_index = NumberToUint32(numbers->get(0)); 6424 uint32_t max_index = min_index; 6425 uint32_t i; 6426 for (i = 1; i < len; i++) { 6427 if (NumberToUint32(numbers->get(i)) < min_index) { 6428 min_index = NumberToUint32(numbers->get(i)); 6429 } else if (NumberToUint32(numbers->get(i)) > max_index) { 6430 max_index = NumberToUint32(numbers->get(i)); 6431 } 6432 } 6433 if (max_index - min_index + 1 == len) { 6434 // Indices form a contiguous range, unless there are duplicates. 6435 // Do an in-place linear time sort assuming distinct numbers, but 6436 // avoid hanging in case they are not. 6437 for (i = 0; i < len; i++) { 6438 uint32_t p; 6439 uint32_t j = 0; 6440 // While the current element at i is not at its correct position p, 6441 // swap the elements at these two positions. 6442 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i && 6443 j++ < len) { 6444 SwapPairs(numbers, i, p); 6445 } 6446 } 6447 } else { 6448 HeapSortPairs(this, numbers, len); 6449 return; 6450 } 6451} 6452 6453 6454// Fill in the names of local properties into the supplied storage. The main 6455// purpose of this function is to provide reflection information for the object 6456// mirrors. 6457void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) { 6458 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index)); 6459 if (HasFastProperties()) { 6460 DescriptorArray* descs = map()->instance_descriptors(); 6461 for (int i = 0; i < descs->number_of_descriptors(); i++) { 6462 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i)); 6463 } 6464 ASSERT(storage->length() >= index); 6465 } else { 6466 property_dictionary()->CopyKeysTo(storage); 6467 } 6468} 6469 6470 6471int JSObject::NumberOfLocalElements(PropertyAttributes filter) { 6472 return GetLocalElementKeys(NULL, filter); 6473} 6474 6475 6476int JSObject::NumberOfEnumElements() { 6477 // Fast case for objects with no elements. 6478 if (!IsJSValue() && HasFastElements()) { 6479 uint32_t length = IsJSArray() ? 6480 static_cast<uint32_t>( 6481 Smi::cast(JSArray::cast(this)->length())->value()) : 6482 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 6483 if (length == 0) return 0; 6484 } 6485 // Compute the number of enumerable elements. 6486 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM)); 6487} 6488 6489 6490int JSObject::GetLocalElementKeys(FixedArray* storage, 6491 PropertyAttributes filter) { 6492 int counter = 0; 6493 switch (GetElementsKind()) { 6494 case FAST_ELEMENTS: { 6495 int length = IsJSArray() ? 6496 Smi::cast(JSArray::cast(this)->length())->value() : 6497 FixedArray::cast(elements())->length(); 6498 for (int i = 0; i < length; i++) { 6499 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) { 6500 if (storage != NULL) { 6501 storage->set(counter, Smi::FromInt(i)); 6502 } 6503 counter++; 6504 } 6505 } 6506 ASSERT(!storage || storage->length() >= counter); 6507 break; 6508 } 6509 case PIXEL_ELEMENTS: { 6510 int length = PixelArray::cast(elements())->length(); 6511 while (counter < length) { 6512 if (storage != NULL) { 6513 storage->set(counter, Smi::FromInt(counter)); 6514 } 6515 counter++; 6516 } 6517 ASSERT(!storage || storage->length() >= counter); 6518 break; 6519 } 6520 case EXTERNAL_BYTE_ELEMENTS: 6521 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 6522 case EXTERNAL_SHORT_ELEMENTS: 6523 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 6524 case EXTERNAL_INT_ELEMENTS: 6525 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 6526 case EXTERNAL_FLOAT_ELEMENTS: { 6527 int length = ExternalArray::cast(elements())->length(); 6528 while (counter < length) { 6529 if (storage != NULL) { 6530 storage->set(counter, Smi::FromInt(counter)); 6531 } 6532 counter++; 6533 } 6534 ASSERT(!storage || storage->length() >= counter); 6535 break; 6536 } 6537 case DICTIONARY_ELEMENTS: { 6538 if (storage != NULL) { 6539 element_dictionary()->CopyKeysTo(storage, filter); 6540 } 6541 counter = element_dictionary()->NumberOfElementsFilterAttributes(filter); 6542 break; 6543 } 6544 default: 6545 UNREACHABLE(); 6546 break; 6547 } 6548 6549 if (this->IsJSValue()) { 6550 Object* val = JSValue::cast(this)->value(); 6551 if (val->IsString()) { 6552 String* str = String::cast(val); 6553 if (storage) { 6554 for (int i = 0; i < str->length(); i++) { 6555 storage->set(counter + i, Smi::FromInt(i)); 6556 } 6557 } 6558 counter += str->length(); 6559 } 6560 } 6561 ASSERT(!storage || storage->length() == counter); 6562 return counter; 6563} 6564 6565 6566int JSObject::GetEnumElementKeys(FixedArray* storage) { 6567 return GetLocalElementKeys(storage, 6568 static_cast<PropertyAttributes>(DONT_ENUM)); 6569} 6570 6571 6572bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) { 6573 ASSERT(other->IsNumber()); 6574 return key == static_cast<uint32_t>(other->Number()); 6575} 6576 6577 6578uint32_t NumberDictionaryShape::Hash(uint32_t key) { 6579 return ComputeIntegerHash(key); 6580} 6581 6582 6583uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) { 6584 ASSERT(other->IsNumber()); 6585 return ComputeIntegerHash(static_cast<uint32_t>(other->Number())); 6586} 6587 6588 6589Object* NumberDictionaryShape::AsObject(uint32_t key) { 6590 return Heap::NumberFromUint32(key); 6591} 6592 6593 6594bool StringDictionaryShape::IsMatch(String* key, Object* other) { 6595 // We know that all entries in a hash table had their hash keys created. 6596 // Use that knowledge to have fast failure. 6597 if (key->Hash() != String::cast(other)->Hash()) return false; 6598 return key->Equals(String::cast(other)); 6599} 6600 6601 6602uint32_t StringDictionaryShape::Hash(String* key) { 6603 return key->Hash(); 6604} 6605 6606 6607uint32_t StringDictionaryShape::HashForObject(String* key, Object* other) { 6608 return String::cast(other)->Hash(); 6609} 6610 6611 6612Object* StringDictionaryShape::AsObject(String* key) { 6613 return key; 6614} 6615 6616 6617// StringKey simply carries a string object as key. 6618class StringKey : public HashTableKey { 6619 public: 6620 explicit StringKey(String* string) : 6621 string_(string), 6622 hash_(HashForObject(string)) { } 6623 6624 bool IsMatch(Object* string) { 6625 // We know that all entries in a hash table had their hash keys created. 6626 // Use that knowledge to have fast failure. 6627 if (hash_ != HashForObject(string)) { 6628 return false; 6629 } 6630 return string_->Equals(String::cast(string)); 6631 } 6632 6633 uint32_t Hash() { return hash_; } 6634 6635 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); } 6636 6637 Object* AsObject() { return string_; } 6638 6639 String* string_; 6640 uint32_t hash_; 6641}; 6642 6643 6644// StringSharedKeys are used as keys in the eval cache. 6645class StringSharedKey : public HashTableKey { 6646 public: 6647 StringSharedKey(String* source, SharedFunctionInfo* shared) 6648 : source_(source), shared_(shared) { } 6649 6650 bool IsMatch(Object* other) { 6651 if (!other->IsFixedArray()) return false; 6652 FixedArray* pair = FixedArray::cast(other); 6653 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0)); 6654 if (shared != shared_) return false; 6655 String* source = String::cast(pair->get(1)); 6656 return source->Equals(source_); 6657 } 6658 6659 static uint32_t StringSharedHashHelper(String* source, 6660 SharedFunctionInfo* shared) { 6661 uint32_t hash = source->Hash(); 6662 if (shared->HasSourceCode()) { 6663 // Instead of using the SharedFunctionInfo pointer in the hash 6664 // code computation, we use a combination of the hash of the 6665 // script source code and the start and end positions. We do 6666 // this to ensure that the cache entries can survive garbage 6667 // collection. 6668 Script* script = Script::cast(shared->script()); 6669 hash ^= String::cast(script->source())->Hash(); 6670 hash += shared->start_position(); 6671 } 6672 return hash; 6673 } 6674 6675 uint32_t Hash() { 6676 return StringSharedHashHelper(source_, shared_); 6677 } 6678 6679 uint32_t HashForObject(Object* obj) { 6680 FixedArray* pair = FixedArray::cast(obj); 6681 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0)); 6682 String* source = String::cast(pair->get(1)); 6683 return StringSharedHashHelper(source, shared); 6684 } 6685 6686 Object* AsObject() { 6687 Object* obj = Heap::AllocateFixedArray(2); 6688 if (obj->IsFailure()) return obj; 6689 FixedArray* pair = FixedArray::cast(obj); 6690 pair->set(0, shared_); 6691 pair->set(1, source_); 6692 return pair; 6693 } 6694 6695 private: 6696 String* source_; 6697 SharedFunctionInfo* shared_; 6698}; 6699 6700 6701// RegExpKey carries the source and flags of a regular expression as key. 6702class RegExpKey : public HashTableKey { 6703 public: 6704 RegExpKey(String* string, JSRegExp::Flags flags) 6705 : string_(string), 6706 flags_(Smi::FromInt(flags.value())) { } 6707 6708 // Rather than storing the key in the hash table, a pointer to the 6709 // stored value is stored where the key should be. IsMatch then 6710 // compares the search key to the found object, rather than comparing 6711 // a key to a key. 6712 bool IsMatch(Object* obj) { 6713 FixedArray* val = FixedArray::cast(obj); 6714 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex))) 6715 && (flags_ == val->get(JSRegExp::kFlagsIndex)); 6716 } 6717 6718 uint32_t Hash() { return RegExpHash(string_, flags_); } 6719 6720 Object* AsObject() { 6721 // Plain hash maps, which is where regexp keys are used, don't 6722 // use this function. 6723 UNREACHABLE(); 6724 return NULL; 6725 } 6726 6727 uint32_t HashForObject(Object* obj) { 6728 FixedArray* val = FixedArray::cast(obj); 6729 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)), 6730 Smi::cast(val->get(JSRegExp::kFlagsIndex))); 6731 } 6732 6733 static uint32_t RegExpHash(String* string, Smi* flags) { 6734 return string->Hash() + flags->value(); 6735 } 6736 6737 String* string_; 6738 Smi* flags_; 6739}; 6740 6741// Utf8SymbolKey carries a vector of chars as key. 6742class Utf8SymbolKey : public HashTableKey { 6743 public: 6744 explicit Utf8SymbolKey(Vector<const char> string) 6745 : string_(string), hash_field_(0) { } 6746 6747 bool IsMatch(Object* string) { 6748 return String::cast(string)->IsEqualTo(string_); 6749 } 6750 6751 uint32_t Hash() { 6752 if (hash_field_ != 0) return hash_field_ >> String::kHashShift; 6753 unibrow::Utf8InputBuffer<> buffer(string_.start(), 6754 static_cast<unsigned>(string_.length())); 6755 chars_ = buffer.Length(); 6756 hash_field_ = String::ComputeHashField(&buffer, chars_); 6757 uint32_t result = hash_field_ >> String::kHashShift; 6758 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. 6759 return result; 6760 } 6761 6762 uint32_t HashForObject(Object* other) { 6763 return String::cast(other)->Hash(); 6764 } 6765 6766 Object* AsObject() { 6767 if (hash_field_ == 0) Hash(); 6768 return Heap::AllocateSymbol(string_, chars_, hash_field_); 6769 } 6770 6771 Vector<const char> string_; 6772 uint32_t hash_field_; 6773 int chars_; // Caches the number of characters when computing the hash code. 6774}; 6775 6776 6777// SymbolKey carries a string/symbol object as key. 6778class SymbolKey : public HashTableKey { 6779 public: 6780 explicit SymbolKey(String* string) : string_(string) { } 6781 6782 bool IsMatch(Object* string) { 6783 return String::cast(string)->Equals(string_); 6784 } 6785 6786 uint32_t Hash() { return string_->Hash(); } 6787 6788 uint32_t HashForObject(Object* other) { 6789 return String::cast(other)->Hash(); 6790 } 6791 6792 Object* AsObject() { 6793 // If the string is a cons string, attempt to flatten it so that 6794 // symbols will most often be flat strings. 6795 if (StringShape(string_).IsCons()) { 6796 ConsString* cons_string = ConsString::cast(string_); 6797 cons_string->TryFlatten(); 6798 if (cons_string->second()->length() == 0) { 6799 string_ = cons_string->first(); 6800 } 6801 } 6802 // Transform string to symbol if possible. 6803 Map* map = Heap::SymbolMapForString(string_); 6804 if (map != NULL) { 6805 string_->set_map(map); 6806 ASSERT(string_->IsSymbol()); 6807 return string_; 6808 } 6809 // Otherwise allocate a new symbol. 6810 StringInputBuffer buffer(string_); 6811 return Heap::AllocateInternalSymbol(&buffer, 6812 string_->length(), 6813 string_->hash_field()); 6814 } 6815 6816 static uint32_t StringHash(Object* obj) { 6817 return String::cast(obj)->Hash(); 6818 } 6819 6820 String* string_; 6821}; 6822 6823 6824template<typename Shape, typename Key> 6825void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) { 6826 IteratePointers(v, 0, kElementsStartOffset); 6827} 6828 6829 6830template<typename Shape, typename Key> 6831void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) { 6832 IteratePointers(v, 6833 kElementsStartOffset, 6834 kHeaderSize + length() * kPointerSize); 6835} 6836 6837 6838template<typename Shape, typename Key> 6839Object* HashTable<Shape, Key>::Allocate(int at_least_space_for) { 6840 int capacity = RoundUpToPowerOf2(at_least_space_for); 6841 if (capacity < 4) { 6842 capacity = 4; // Guarantee min capacity. 6843 } else if (capacity > HashTable::kMaxCapacity) { 6844 return Failure::OutOfMemoryException(); 6845 } 6846 6847 Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity)); 6848 if (!obj->IsFailure()) { 6849 HashTable::cast(obj)->SetNumberOfElements(0); 6850 HashTable::cast(obj)->SetNumberOfDeletedElements(0); 6851 HashTable::cast(obj)->SetCapacity(capacity); 6852 } 6853 return obj; 6854} 6855 6856 6857// Find entry for key otherwise return kNotFound. 6858template<typename Shape, typename Key> 6859int HashTable<Shape, Key>::FindEntry(Key key) { 6860 uint32_t capacity = Capacity(); 6861 uint32_t entry = FirstProbe(Shape::Hash(key), capacity); 6862 uint32_t count = 1; 6863 // EnsureCapacity will guarantee the hash table is never full. 6864 while (true) { 6865 Object* element = KeyAt(entry); 6866 if (element->IsUndefined()) break; // Empty entry. 6867 if (!element->IsNull() && Shape::IsMatch(key, element)) return entry; 6868 entry = NextProbe(entry, count++, capacity); 6869 } 6870 return kNotFound; 6871} 6872 6873 6874template<typename Shape, typename Key> 6875Object* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) { 6876 int capacity = Capacity(); 6877 int nof = NumberOfElements() + n; 6878 int nod = NumberOfDeletedElements(); 6879 // Return if: 6880 // 50% is still free after adding n elements and 6881 // at most 50% of the free elements are deleted elements. 6882 if ((nof + (nof >> 1) <= capacity) && 6883 (nod <= (capacity - nof) >> 1)) return this; 6884 6885 Object* obj = Allocate(nof * 2); 6886 if (obj->IsFailure()) return obj; 6887 6888 AssertNoAllocation no_gc; 6889 HashTable* table = HashTable::cast(obj); 6890 WriteBarrierMode mode = table->GetWriteBarrierMode(no_gc); 6891 6892 // Copy prefix to new array. 6893 for (int i = kPrefixStartIndex; 6894 i < kPrefixStartIndex + Shape::kPrefixSize; 6895 i++) { 6896 table->set(i, get(i), mode); 6897 } 6898 // Rehash the elements. 6899 for (int i = 0; i < capacity; i++) { 6900 uint32_t from_index = EntryToIndex(i); 6901 Object* k = get(from_index); 6902 if (IsKey(k)) { 6903 uint32_t hash = Shape::HashForObject(key, k); 6904 uint32_t insertion_index = 6905 EntryToIndex(table->FindInsertionEntry(hash)); 6906 for (int j = 0; j < Shape::kEntrySize; j++) { 6907 table->set(insertion_index + j, get(from_index + j), mode); 6908 } 6909 } 6910 } 6911 table->SetNumberOfElements(NumberOfElements()); 6912 table->SetNumberOfDeletedElements(0); 6913 return table; 6914} 6915 6916 6917 6918template<typename Shape, typename Key> 6919uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) { 6920 uint32_t capacity = Capacity(); 6921 uint32_t entry = FirstProbe(hash, capacity); 6922 uint32_t count = 1; 6923 // EnsureCapacity will guarantee the hash table is never full. 6924 while (true) { 6925 Object* element = KeyAt(entry); 6926 if (element->IsUndefined() || element->IsNull()) break; 6927 entry = NextProbe(entry, count++, capacity); 6928 } 6929 return entry; 6930} 6931 6932// Force instantiation of template instances class. 6933// Please note this list is compiler dependent. 6934 6935template class HashTable<SymbolTableShape, HashTableKey*>; 6936 6937template class HashTable<CompilationCacheShape, HashTableKey*>; 6938 6939template class HashTable<MapCacheShape, HashTableKey*>; 6940 6941template class Dictionary<StringDictionaryShape, String*>; 6942 6943template class Dictionary<NumberDictionaryShape, uint32_t>; 6944 6945template Object* Dictionary<NumberDictionaryShape, uint32_t>::Allocate( 6946 int); 6947 6948template Object* Dictionary<StringDictionaryShape, String*>::Allocate( 6949 int); 6950 6951template Object* Dictionary<NumberDictionaryShape, uint32_t>::AtPut( 6952 uint32_t, Object*); 6953 6954template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup( 6955 Object*); 6956 6957template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup( 6958 Object*); 6959 6960template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo( 6961 FixedArray*, PropertyAttributes); 6962 6963template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty( 6964 int, JSObject::DeleteMode); 6965 6966template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty( 6967 int, JSObject::DeleteMode); 6968 6969template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo( 6970 FixedArray*); 6971 6972template int 6973Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes( 6974 PropertyAttributes); 6975 6976template Object* Dictionary<StringDictionaryShape, String*>::Add( 6977 String*, Object*, PropertyDetails); 6978 6979template Object* 6980Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices(); 6981 6982template int 6983Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes( 6984 PropertyAttributes); 6985 6986template Object* Dictionary<NumberDictionaryShape, uint32_t>::Add( 6987 uint32_t, Object*, PropertyDetails); 6988 6989template Object* Dictionary<NumberDictionaryShape, uint32_t>::EnsureCapacity( 6990 int, uint32_t); 6991 6992template Object* Dictionary<StringDictionaryShape, String*>::EnsureCapacity( 6993 int, String*); 6994 6995template Object* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry( 6996 uint32_t, Object*, PropertyDetails, uint32_t); 6997 6998template Object* Dictionary<StringDictionaryShape, String*>::AddEntry( 6999 String*, Object*, PropertyDetails, uint32_t); 7000 7001template 7002int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements(); 7003 7004template 7005int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements(); 7006 7007template 7008int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t); 7009 7010 7011// Collates undefined and unexisting elements below limit from position 7012// zero of the elements. The object stays in Dictionary mode. 7013Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) { 7014 ASSERT(HasDictionaryElements()); 7015 // Must stay in dictionary mode, either because of requires_slow_elements, 7016 // or because we are not going to sort (and therefore compact) all of the 7017 // elements. 7018 NumberDictionary* dict = element_dictionary(); 7019 HeapNumber* result_double = NULL; 7020 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) { 7021 // Allocate space for result before we start mutating the object. 7022 Object* new_double = Heap::AllocateHeapNumber(0.0); 7023 if (new_double->IsFailure()) return new_double; 7024 result_double = HeapNumber::cast(new_double); 7025 } 7026 7027 int capacity = dict->Capacity(); 7028 Object* obj = NumberDictionary::Allocate(dict->Capacity()); 7029 if (obj->IsFailure()) return obj; 7030 NumberDictionary* new_dict = NumberDictionary::cast(obj); 7031 7032 AssertNoAllocation no_alloc; 7033 7034 uint32_t pos = 0; 7035 uint32_t undefs = 0; 7036 for (int i = 0; i < capacity; i++) { 7037 Object* k = dict->KeyAt(i); 7038 if (dict->IsKey(k)) { 7039 ASSERT(k->IsNumber()); 7040 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0); 7041 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0); 7042 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32); 7043 Object* value = dict->ValueAt(i); 7044 PropertyDetails details = dict->DetailsAt(i); 7045 if (details.type() == CALLBACKS) { 7046 // Bail out and do the sorting of undefineds and array holes in JS. 7047 return Smi::FromInt(-1); 7048 } 7049 uint32_t key = NumberToUint32(k); 7050 if (key < limit) { 7051 if (value->IsUndefined()) { 7052 undefs++; 7053 } else { 7054 new_dict->AddNumberEntry(pos, value, details); 7055 pos++; 7056 } 7057 } else { 7058 new_dict->AddNumberEntry(key, value, details); 7059 } 7060 } 7061 } 7062 7063 uint32_t result = pos; 7064 PropertyDetails no_details = PropertyDetails(NONE, NORMAL); 7065 while (undefs > 0) { 7066 new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details); 7067 pos++; 7068 undefs--; 7069 } 7070 7071 set_elements(new_dict); 7072 7073 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) { 7074 return Smi::FromInt(static_cast<int>(result)); 7075 } 7076 7077 ASSERT_NE(NULL, result_double); 7078 result_double->set_value(static_cast<double>(result)); 7079 return result_double; 7080} 7081 7082 7083// Collects all defined (non-hole) and non-undefined (array) elements at 7084// the start of the elements array. 7085// If the object is in dictionary mode, it is converted to fast elements 7086// mode. 7087Object* JSObject::PrepareElementsForSort(uint32_t limit) { 7088 ASSERT(!HasPixelElements() && !HasExternalArrayElements()); 7089 7090 if (HasDictionaryElements()) { 7091 // Convert to fast elements containing only the existing properties. 7092 // Ordering is irrelevant, since we are going to sort anyway. 7093 NumberDictionary* dict = element_dictionary(); 7094 if (IsJSArray() || dict->requires_slow_elements() || 7095 dict->max_number_key() >= limit) { 7096 return PrepareSlowElementsForSort(limit); 7097 } 7098 // Convert to fast elements. 7099 7100 PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED; 7101 Object* new_array = 7102 Heap::AllocateFixedArray(dict->NumberOfElements(), tenure); 7103 if (new_array->IsFailure()) { 7104 return new_array; 7105 } 7106 FixedArray* fast_elements = FixedArray::cast(new_array); 7107 dict->CopyValuesTo(fast_elements); 7108 set_elements(fast_elements); 7109 } 7110 ASSERT(HasFastElements()); 7111 7112 // Collect holes at the end, undefined before that and the rest at the 7113 // start, and return the number of non-hole, non-undefined values. 7114 7115 FixedArray* elements = FixedArray::cast(this->elements()); 7116 uint32_t elements_length = static_cast<uint32_t>(elements->length()); 7117 if (limit > elements_length) { 7118 limit = elements_length ; 7119 } 7120 if (limit == 0) { 7121 return Smi::FromInt(0); 7122 } 7123 7124 HeapNumber* result_double = NULL; 7125 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) { 7126 // Pessimistically allocate space for return value before 7127 // we start mutating the array. 7128 Object* new_double = Heap::AllocateHeapNumber(0.0); 7129 if (new_double->IsFailure()) return new_double; 7130 result_double = HeapNumber::cast(new_double); 7131 } 7132 7133 AssertNoAllocation no_alloc; 7134 7135 // Split elements into defined, undefined and the_hole, in that order. 7136 // Only count locations for undefined and the hole, and fill them afterwards. 7137 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc); 7138 unsigned int undefs = limit; 7139 unsigned int holes = limit; 7140 // Assume most arrays contain no holes and undefined values, so minimize the 7141 // number of stores of non-undefined, non-the-hole values. 7142 for (unsigned int i = 0; i < undefs; i++) { 7143 Object* current = elements->get(i); 7144 if (current->IsTheHole()) { 7145 holes--; 7146 undefs--; 7147 } else if (current->IsUndefined()) { 7148 undefs--; 7149 } else { 7150 continue; 7151 } 7152 // Position i needs to be filled. 7153 while (undefs > i) { 7154 current = elements->get(undefs); 7155 if (current->IsTheHole()) { 7156 holes--; 7157 undefs--; 7158 } else if (current->IsUndefined()) { 7159 undefs--; 7160 } else { 7161 elements->set(i, current, write_barrier); 7162 break; 7163 } 7164 } 7165 } 7166 uint32_t result = undefs; 7167 while (undefs < holes) { 7168 elements->set_undefined(undefs); 7169 undefs++; 7170 } 7171 while (holes < limit) { 7172 elements->set_the_hole(holes); 7173 holes++; 7174 } 7175 7176 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) { 7177 return Smi::FromInt(static_cast<int>(result)); 7178 } 7179 ASSERT_NE(NULL, result_double); 7180 result_double->set_value(static_cast<double>(result)); 7181 return result_double; 7182} 7183 7184 7185Object* PixelArray::SetValue(uint32_t index, Object* value) { 7186 uint8_t clamped_value = 0; 7187 if (index < static_cast<uint32_t>(length())) { 7188 if (value->IsSmi()) { 7189 int int_value = Smi::cast(value)->value(); 7190 if (int_value < 0) { 7191 clamped_value = 0; 7192 } else if (int_value > 255) { 7193 clamped_value = 255; 7194 } else { 7195 clamped_value = static_cast<uint8_t>(int_value); 7196 } 7197 } else if (value->IsHeapNumber()) { 7198 double double_value = HeapNumber::cast(value)->value(); 7199 if (!(double_value > 0)) { 7200 // NaN and less than zero clamp to zero. 7201 clamped_value = 0; 7202 } else if (double_value > 255) { 7203 // Greater than 255 clamp to 255. 7204 clamped_value = 255; 7205 } else { 7206 // Other doubles are rounded to the nearest integer. 7207 clamped_value = static_cast<uint8_t>(double_value + 0.5); 7208 } 7209 } else { 7210 // Clamp undefined to zero (default). All other types have been 7211 // converted to a number type further up in the call chain. 7212 ASSERT(value->IsUndefined()); 7213 } 7214 set(index, clamped_value); 7215 } 7216 return Smi::FromInt(clamped_value); 7217} 7218 7219 7220template<typename ExternalArrayClass, typename ValueType> 7221static Object* ExternalArrayIntSetter(ExternalArrayClass* receiver, 7222 uint32_t index, 7223 Object* value) { 7224 ValueType cast_value = 0; 7225 if (index < static_cast<uint32_t>(receiver->length())) { 7226 if (value->IsSmi()) { 7227 int int_value = Smi::cast(value)->value(); 7228 cast_value = static_cast<ValueType>(int_value); 7229 } else if (value->IsHeapNumber()) { 7230 double double_value = HeapNumber::cast(value)->value(); 7231 cast_value = static_cast<ValueType>(DoubleToInt32(double_value)); 7232 } else { 7233 // Clamp undefined to zero (default). All other types have been 7234 // converted to a number type further up in the call chain. 7235 ASSERT(value->IsUndefined()); 7236 } 7237 receiver->set(index, cast_value); 7238 } 7239 return Heap::NumberFromInt32(cast_value); 7240} 7241 7242 7243Object* ExternalByteArray::SetValue(uint32_t index, Object* value) { 7244 return ExternalArrayIntSetter<ExternalByteArray, int8_t> 7245 (this, index, value); 7246} 7247 7248 7249Object* ExternalUnsignedByteArray::SetValue(uint32_t index, Object* value) { 7250 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t> 7251 (this, index, value); 7252} 7253 7254 7255Object* ExternalShortArray::SetValue(uint32_t index, Object* value) { 7256 return ExternalArrayIntSetter<ExternalShortArray, int16_t> 7257 (this, index, value); 7258} 7259 7260 7261Object* ExternalUnsignedShortArray::SetValue(uint32_t index, Object* value) { 7262 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t> 7263 (this, index, value); 7264} 7265 7266 7267Object* ExternalIntArray::SetValue(uint32_t index, Object* value) { 7268 return ExternalArrayIntSetter<ExternalIntArray, int32_t> 7269 (this, index, value); 7270} 7271 7272 7273Object* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) { 7274 uint32_t cast_value = 0; 7275 if (index < static_cast<uint32_t>(length())) { 7276 if (value->IsSmi()) { 7277 int int_value = Smi::cast(value)->value(); 7278 cast_value = static_cast<uint32_t>(int_value); 7279 } else if (value->IsHeapNumber()) { 7280 double double_value = HeapNumber::cast(value)->value(); 7281 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value)); 7282 } else { 7283 // Clamp undefined to zero (default). All other types have been 7284 // converted to a number type further up in the call chain. 7285 ASSERT(value->IsUndefined()); 7286 } 7287 set(index, cast_value); 7288 } 7289 return Heap::NumberFromUint32(cast_value); 7290} 7291 7292 7293Object* ExternalFloatArray::SetValue(uint32_t index, Object* value) { 7294 float cast_value = 0; 7295 if (index < static_cast<uint32_t>(length())) { 7296 if (value->IsSmi()) { 7297 int int_value = Smi::cast(value)->value(); 7298 cast_value = static_cast<float>(int_value); 7299 } else if (value->IsHeapNumber()) { 7300 double double_value = HeapNumber::cast(value)->value(); 7301 cast_value = static_cast<float>(double_value); 7302 } else { 7303 // Clamp undefined to zero (default). All other types have been 7304 // converted to a number type further up in the call chain. 7305 ASSERT(value->IsUndefined()); 7306 } 7307 set(index, cast_value); 7308 } 7309 return Heap::AllocateHeapNumber(cast_value); 7310} 7311 7312 7313Object* GlobalObject::GetPropertyCell(LookupResult* result) { 7314 ASSERT(!HasFastProperties()); 7315 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry()); 7316 ASSERT(value->IsJSGlobalPropertyCell()); 7317 return value; 7318} 7319 7320 7321Object* GlobalObject::EnsurePropertyCell(String* name) { 7322 ASSERT(!HasFastProperties()); 7323 int entry = property_dictionary()->FindEntry(name); 7324 if (entry == StringDictionary::kNotFound) { 7325 Object* cell = Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value()); 7326 if (cell->IsFailure()) return cell; 7327 PropertyDetails details(NONE, NORMAL); 7328 details = details.AsDeleted(); 7329 Object* dictionary = property_dictionary()->Add(name, cell, details); 7330 if (dictionary->IsFailure()) return dictionary; 7331 set_properties(StringDictionary::cast(dictionary)); 7332 return cell; 7333 } else { 7334 Object* value = property_dictionary()->ValueAt(entry); 7335 ASSERT(value->IsJSGlobalPropertyCell()); 7336 return value; 7337 } 7338} 7339 7340 7341Object* SymbolTable::LookupString(String* string, Object** s) { 7342 SymbolKey key(string); 7343 return LookupKey(&key, s); 7344} 7345 7346 7347// This class is used for looking up two character strings in the symbol table. 7348// If we don't have a hit we don't want to waste much time so we unroll the 7349// string hash calculation loop here for speed. Doesn't work if the two 7350// characters form a decimal integer, since such strings have a different hash 7351// algorithm. 7352class TwoCharHashTableKey : public HashTableKey { 7353 public: 7354 TwoCharHashTableKey(uint32_t c1, uint32_t c2) 7355 : c1_(c1), c2_(c2) { 7356 // Char 1. 7357 uint32_t hash = c1 + (c1 << 10); 7358 hash ^= hash >> 6; 7359 // Char 2. 7360 hash += c2; 7361 hash += hash << 10; 7362 hash ^= hash >> 6; 7363 // GetHash. 7364 hash += hash << 3; 7365 hash ^= hash >> 11; 7366 hash += hash << 15; 7367 if (hash == 0) hash = 27; 7368#ifdef DEBUG 7369 StringHasher hasher(2); 7370 hasher.AddCharacter(c1); 7371 hasher.AddCharacter(c2); 7372 // If this assert fails then we failed to reproduce the two-character 7373 // version of the string hashing algorithm above. One reason could be 7374 // that we were passed two digits as characters, since the hash 7375 // algorithm is different in that case. 7376 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash)); 7377#endif 7378 hash_ = hash; 7379 } 7380 7381 bool IsMatch(Object* o) { 7382 if (!o->IsString()) return false; 7383 String* other = String::cast(o); 7384 if (other->length() != 2) return false; 7385 if (other->Get(0) != c1_) return false; 7386 return other->Get(1) == c2_; 7387 } 7388 7389 uint32_t Hash() { return hash_; } 7390 uint32_t HashForObject(Object* key) { 7391 if (!key->IsString()) return 0; 7392 return String::cast(key)->Hash(); 7393 } 7394 7395 Object* AsObject() { 7396 // The TwoCharHashTableKey is only used for looking in the symbol 7397 // table, not for adding to it. 7398 UNREACHABLE(); 7399 return NULL; 7400 } 7401 private: 7402 uint32_t c1_; 7403 uint32_t c2_; 7404 uint32_t hash_; 7405}; 7406 7407 7408bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { 7409 SymbolKey key(string); 7410 int entry = FindEntry(&key); 7411 if (entry == kNotFound) { 7412 return false; 7413 } else { 7414 String* result = String::cast(KeyAt(entry)); 7415 ASSERT(StringShape(result).IsSymbol()); 7416 *symbol = result; 7417 return true; 7418 } 7419} 7420 7421 7422bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, 7423 uint32_t c2, 7424 String** symbol) { 7425 TwoCharHashTableKey key(c1, c2); 7426 int entry = FindEntry(&key); 7427 if (entry == kNotFound) { 7428 return false; 7429 } else { 7430 String* result = String::cast(KeyAt(entry)); 7431 ASSERT(StringShape(result).IsSymbol()); 7432 *symbol = result; 7433 return true; 7434 } 7435} 7436 7437 7438Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) { 7439 Utf8SymbolKey key(str); 7440 return LookupKey(&key, s); 7441} 7442 7443 7444Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) { 7445 int entry = FindEntry(key); 7446 7447 // Symbol already in table. 7448 if (entry != kNotFound) { 7449 *s = KeyAt(entry); 7450 return this; 7451 } 7452 7453 // Adding new symbol. Grow table if needed. 7454 Object* obj = EnsureCapacity(1, key); 7455 if (obj->IsFailure()) return obj; 7456 7457 // Create symbol object. 7458 Object* symbol = key->AsObject(); 7459 if (symbol->IsFailure()) return symbol; 7460 7461 // If the symbol table grew as part of EnsureCapacity, obj is not 7462 // the current symbol table and therefore we cannot use 7463 // SymbolTable::cast here. 7464 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj); 7465 7466 // Add the new symbol and return it along with the symbol table. 7467 entry = table->FindInsertionEntry(key->Hash()); 7468 table->set(EntryToIndex(entry), symbol); 7469 table->ElementAdded(); 7470 *s = symbol; 7471 return table; 7472} 7473 7474 7475Object* CompilationCacheTable::Lookup(String* src) { 7476 StringKey key(src); 7477 int entry = FindEntry(&key); 7478 if (entry == kNotFound) return Heap::undefined_value(); 7479 return get(EntryToIndex(entry) + 1); 7480} 7481 7482 7483Object* CompilationCacheTable::LookupEval(String* src, Context* context) { 7484 StringSharedKey key(src, context->closure()->shared()); 7485 int entry = FindEntry(&key); 7486 if (entry == kNotFound) return Heap::undefined_value(); 7487 return get(EntryToIndex(entry) + 1); 7488} 7489 7490 7491Object* CompilationCacheTable::LookupRegExp(String* src, 7492 JSRegExp::Flags flags) { 7493 RegExpKey key(src, flags); 7494 int entry = FindEntry(&key); 7495 if (entry == kNotFound) return Heap::undefined_value(); 7496 return get(EntryToIndex(entry) + 1); 7497} 7498 7499 7500Object* CompilationCacheTable::Put(String* src, Object* value) { 7501 StringKey key(src); 7502 Object* obj = EnsureCapacity(1, &key); 7503 if (obj->IsFailure()) return obj; 7504 7505 CompilationCacheTable* cache = 7506 reinterpret_cast<CompilationCacheTable*>(obj); 7507 int entry = cache->FindInsertionEntry(key.Hash()); 7508 cache->set(EntryToIndex(entry), src); 7509 cache->set(EntryToIndex(entry) + 1, value); 7510 cache->ElementAdded(); 7511 return cache; 7512} 7513 7514 7515Object* CompilationCacheTable::PutEval(String* src, 7516 Context* context, 7517 Object* value) { 7518 StringSharedKey key(src, context->closure()->shared()); 7519 Object* obj = EnsureCapacity(1, &key); 7520 if (obj->IsFailure()) return obj; 7521 7522 CompilationCacheTable* cache = 7523 reinterpret_cast<CompilationCacheTable*>(obj); 7524 int entry = cache->FindInsertionEntry(key.Hash()); 7525 7526 Object* k = key.AsObject(); 7527 if (k->IsFailure()) return k; 7528 7529 cache->set(EntryToIndex(entry), k); 7530 cache->set(EntryToIndex(entry) + 1, value); 7531 cache->ElementAdded(); 7532 return cache; 7533} 7534 7535 7536Object* CompilationCacheTable::PutRegExp(String* src, 7537 JSRegExp::Flags flags, 7538 FixedArray* value) { 7539 RegExpKey key(src, flags); 7540 Object* obj = EnsureCapacity(1, &key); 7541 if (obj->IsFailure()) return obj; 7542 7543 CompilationCacheTable* cache = 7544 reinterpret_cast<CompilationCacheTable*>(obj); 7545 int entry = cache->FindInsertionEntry(key.Hash()); 7546 // We store the value in the key slot, and compare the search key 7547 // to the stored value with a custon IsMatch function during lookups. 7548 cache->set(EntryToIndex(entry), value); 7549 cache->set(EntryToIndex(entry) + 1, value); 7550 cache->ElementAdded(); 7551 return cache; 7552} 7553 7554 7555// SymbolsKey used for HashTable where key is array of symbols. 7556class SymbolsKey : public HashTableKey { 7557 public: 7558 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { } 7559 7560 bool IsMatch(Object* symbols) { 7561 FixedArray* o = FixedArray::cast(symbols); 7562 int len = symbols_->length(); 7563 if (o->length() != len) return false; 7564 for (int i = 0; i < len; i++) { 7565 if (o->get(i) != symbols_->get(i)) return false; 7566 } 7567 return true; 7568 } 7569 7570 uint32_t Hash() { return HashForObject(symbols_); } 7571 7572 uint32_t HashForObject(Object* obj) { 7573 FixedArray* symbols = FixedArray::cast(obj); 7574 int len = symbols->length(); 7575 uint32_t hash = 0; 7576 for (int i = 0; i < len; i++) { 7577 hash ^= String::cast(symbols->get(i))->Hash(); 7578 } 7579 return hash; 7580 } 7581 7582 Object* AsObject() { return symbols_; } 7583 7584 private: 7585 FixedArray* symbols_; 7586}; 7587 7588 7589Object* MapCache::Lookup(FixedArray* array) { 7590 SymbolsKey key(array); 7591 int entry = FindEntry(&key); 7592 if (entry == kNotFound) return Heap::undefined_value(); 7593 return get(EntryToIndex(entry) + 1); 7594} 7595 7596 7597Object* MapCache::Put(FixedArray* array, Map* value) { 7598 SymbolsKey key(array); 7599 Object* obj = EnsureCapacity(1, &key); 7600 if (obj->IsFailure()) return obj; 7601 7602 MapCache* cache = reinterpret_cast<MapCache*>(obj); 7603 int entry = cache->FindInsertionEntry(key.Hash()); 7604 cache->set(EntryToIndex(entry), array); 7605 cache->set(EntryToIndex(entry) + 1, value); 7606 cache->ElementAdded(); 7607 return cache; 7608} 7609 7610 7611template<typename Shape, typename Key> 7612Object* Dictionary<Shape, Key>::Allocate(int at_least_space_for) { 7613 Object* obj = HashTable<Shape, Key>::Allocate(at_least_space_for); 7614 // Initialize the next enumeration index. 7615 if (!obj->IsFailure()) { 7616 Dictionary<Shape, Key>::cast(obj)-> 7617 SetNextEnumerationIndex(PropertyDetails::kInitialIndex); 7618 } 7619 return obj; 7620} 7621 7622 7623template<typename Shape, typename Key> 7624Object* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() { 7625 int length = HashTable<Shape, Key>::NumberOfElements(); 7626 7627 // Allocate and initialize iteration order array. 7628 Object* obj = Heap::AllocateFixedArray(length); 7629 if (obj->IsFailure()) return obj; 7630 FixedArray* iteration_order = FixedArray::cast(obj); 7631 for (int i = 0; i < length; i++) { 7632 iteration_order->set(i, Smi::FromInt(i)); 7633 } 7634 7635 // Allocate array with enumeration order. 7636 obj = Heap::AllocateFixedArray(length); 7637 if (obj->IsFailure()) return obj; 7638 FixedArray* enumeration_order = FixedArray::cast(obj); 7639 7640 // Fill the enumeration order array with property details. 7641 int capacity = HashTable<Shape, Key>::Capacity(); 7642 int pos = 0; 7643 for (int i = 0; i < capacity; i++) { 7644 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) { 7645 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index())); 7646 } 7647 } 7648 7649 // Sort the arrays wrt. enumeration order. 7650 iteration_order->SortPairs(enumeration_order, enumeration_order->length()); 7651 7652 // Overwrite the enumeration_order with the enumeration indices. 7653 for (int i = 0; i < length; i++) { 7654 int index = Smi::cast(iteration_order->get(i))->value(); 7655 int enum_index = PropertyDetails::kInitialIndex + i; 7656 enumeration_order->set(index, Smi::FromInt(enum_index)); 7657 } 7658 7659 // Update the dictionary with new indices. 7660 capacity = HashTable<Shape, Key>::Capacity(); 7661 pos = 0; 7662 for (int i = 0; i < capacity; i++) { 7663 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) { 7664 int enum_index = Smi::cast(enumeration_order->get(pos++))->value(); 7665 PropertyDetails details = DetailsAt(i); 7666 PropertyDetails new_details = 7667 PropertyDetails(details.attributes(), details.type(), enum_index); 7668 DetailsAtPut(i, new_details); 7669 } 7670 } 7671 7672 // Set the next enumeration index. 7673 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length); 7674 return this; 7675} 7676 7677template<typename Shape, typename Key> 7678Object* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) { 7679 // Check whether there are enough enumeration indices to add n elements. 7680 if (Shape::kIsEnumerable && 7681 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) { 7682 // If not, we generate new indices for the properties. 7683 Object* result = GenerateNewEnumerationIndices(); 7684 if (result->IsFailure()) return result; 7685 } 7686 return HashTable<Shape, Key>::EnsureCapacity(n, key); 7687} 7688 7689 7690void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) { 7691 // Do nothing if the interval [from, to) is empty. 7692 if (from >= to) return; 7693 7694 int removed_entries = 0; 7695 Object* sentinel = Heap::null_value(); 7696 int capacity = Capacity(); 7697 for (int i = 0; i < capacity; i++) { 7698 Object* key = KeyAt(i); 7699 if (key->IsNumber()) { 7700 uint32_t number = static_cast<uint32_t>(key->Number()); 7701 if (from <= number && number < to) { 7702 SetEntry(i, sentinel, sentinel, Smi::FromInt(0)); 7703 removed_entries++; 7704 } 7705 } 7706 } 7707 7708 // Update the number of elements. 7709 ElementsRemoved(removed_entries); 7710} 7711 7712 7713template<typename Shape, typename Key> 7714Object* Dictionary<Shape, Key>::DeleteProperty(int entry, 7715 JSObject::DeleteMode mode) { 7716 PropertyDetails details = DetailsAt(entry); 7717 // Ignore attributes if forcing a deletion. 7718 if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) { 7719 return Heap::false_value(); 7720 } 7721 SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0)); 7722 HashTable<Shape, Key>::ElementRemoved(); 7723 return Heap::true_value(); 7724} 7725 7726 7727template<typename Shape, typename Key> 7728Object* Dictionary<Shape, Key>::AtPut(Key key, Object* value) { 7729 int entry = FindEntry(key); 7730 7731 // If the entry is present set the value; 7732 if (entry != Dictionary<Shape, Key>::kNotFound) { 7733 ValueAtPut(entry, value); 7734 return this; 7735 } 7736 7737 // Check whether the dictionary should be extended. 7738 Object* obj = EnsureCapacity(1, key); 7739 if (obj->IsFailure()) return obj; 7740 7741 Object* k = Shape::AsObject(key); 7742 if (k->IsFailure()) return k; 7743 PropertyDetails details = PropertyDetails(NONE, NORMAL); 7744 return Dictionary<Shape, Key>::cast(obj)-> 7745 AddEntry(key, value, details, Shape::Hash(key)); 7746} 7747 7748 7749template<typename Shape, typename Key> 7750Object* Dictionary<Shape, Key>::Add(Key key, 7751 Object* value, 7752 PropertyDetails details) { 7753 // Valdate key is absent. 7754 SLOW_ASSERT((FindEntry(key) == Dictionary<Shape, Key>::kNotFound)); 7755 // Check whether the dictionary should be extended. 7756 Object* obj = EnsureCapacity(1, key); 7757 if (obj->IsFailure()) return obj; 7758 return Dictionary<Shape, Key>::cast(obj)-> 7759 AddEntry(key, value, details, Shape::Hash(key)); 7760} 7761 7762 7763// Add a key, value pair to the dictionary. 7764template<typename Shape, typename Key> 7765Object* Dictionary<Shape, Key>::AddEntry(Key key, 7766 Object* value, 7767 PropertyDetails details, 7768 uint32_t hash) { 7769 // Compute the key object. 7770 Object* k = Shape::AsObject(key); 7771 if (k->IsFailure()) return k; 7772 7773 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash); 7774 // Insert element at empty or deleted entry 7775 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) { 7776 // Assign an enumeration index to the property and update 7777 // SetNextEnumerationIndex. 7778 int index = NextEnumerationIndex(); 7779 details = PropertyDetails(details.attributes(), details.type(), index); 7780 SetNextEnumerationIndex(index + 1); 7781 } 7782 SetEntry(entry, k, value, details); 7783 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber() 7784 || Dictionary<Shape, Key>::KeyAt(entry)->IsString())); 7785 HashTable<Shape, Key>::ElementAdded(); 7786 return this; 7787} 7788 7789 7790void NumberDictionary::UpdateMaxNumberKey(uint32_t key) { 7791 // If the dictionary requires slow elements an element has already 7792 // been added at a high index. 7793 if (requires_slow_elements()) return; 7794 // Check if this index is high enough that we should require slow 7795 // elements. 7796 if (key > kRequiresSlowElementsLimit) { 7797 set_requires_slow_elements(); 7798 return; 7799 } 7800 // Update max key value. 7801 Object* max_index_object = get(kMaxNumberKeyIndex); 7802 if (!max_index_object->IsSmi() || max_number_key() < key) { 7803 FixedArray::set(kMaxNumberKeyIndex, 7804 Smi::FromInt(key << kRequiresSlowElementsTagSize)); 7805 } 7806} 7807 7808 7809Object* NumberDictionary::AddNumberEntry(uint32_t key, 7810 Object* value, 7811 PropertyDetails details) { 7812 UpdateMaxNumberKey(key); 7813 SLOW_ASSERT(FindEntry(key) == kNotFound); 7814 return Add(key, value, details); 7815} 7816 7817 7818Object* NumberDictionary::AtNumberPut(uint32_t key, Object* value) { 7819 UpdateMaxNumberKey(key); 7820 return AtPut(key, value); 7821} 7822 7823 7824Object* NumberDictionary::Set(uint32_t key, 7825 Object* value, 7826 PropertyDetails details) { 7827 int entry = FindEntry(key); 7828 if (entry == kNotFound) return AddNumberEntry(key, value, details); 7829 // Preserve enumeration index. 7830 details = PropertyDetails(details.attributes(), 7831 details.type(), 7832 DetailsAt(entry).index()); 7833 SetEntry(entry, NumberDictionaryShape::AsObject(key), value, details); 7834 return this; 7835} 7836 7837 7838 7839template<typename Shape, typename Key> 7840int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes( 7841 PropertyAttributes filter) { 7842 int capacity = HashTable<Shape, Key>::Capacity(); 7843 int result = 0; 7844 for (int i = 0; i < capacity; i++) { 7845 Object* k = HashTable<Shape, Key>::KeyAt(i); 7846 if (HashTable<Shape, Key>::IsKey(k)) { 7847 PropertyDetails details = DetailsAt(i); 7848 if (details.IsDeleted()) continue; 7849 PropertyAttributes attr = details.attributes(); 7850 if ((attr & filter) == 0) result++; 7851 } 7852 } 7853 return result; 7854} 7855 7856 7857template<typename Shape, typename Key> 7858int Dictionary<Shape, Key>::NumberOfEnumElements() { 7859 return NumberOfElementsFilterAttributes( 7860 static_cast<PropertyAttributes>(DONT_ENUM)); 7861} 7862 7863 7864template<typename Shape, typename Key> 7865void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage, 7866 PropertyAttributes filter) { 7867 ASSERT(storage->length() >= NumberOfEnumElements()); 7868 int capacity = HashTable<Shape, Key>::Capacity(); 7869 int index = 0; 7870 for (int i = 0; i < capacity; i++) { 7871 Object* k = HashTable<Shape, Key>::KeyAt(i); 7872 if (HashTable<Shape, Key>::IsKey(k)) { 7873 PropertyDetails details = DetailsAt(i); 7874 if (details.IsDeleted()) continue; 7875 PropertyAttributes attr = details.attributes(); 7876 if ((attr & filter) == 0) storage->set(index++, k); 7877 } 7878 } 7879 storage->SortPairs(storage, index); 7880 ASSERT(storage->length() >= index); 7881} 7882 7883 7884void StringDictionary::CopyEnumKeysTo(FixedArray* storage, 7885 FixedArray* sort_array) { 7886 ASSERT(storage->length() >= NumberOfEnumElements()); 7887 int capacity = Capacity(); 7888 int index = 0; 7889 for (int i = 0; i < capacity; i++) { 7890 Object* k = KeyAt(i); 7891 if (IsKey(k)) { 7892 PropertyDetails details = DetailsAt(i); 7893 if (details.IsDeleted() || details.IsDontEnum()) continue; 7894 storage->set(index, k); 7895 sort_array->set(index, Smi::FromInt(details.index())); 7896 index++; 7897 } 7898 } 7899 storage->SortPairs(sort_array, sort_array->length()); 7900 ASSERT(storage->length() >= index); 7901} 7902 7903 7904template<typename Shape, typename Key> 7905void Dictionary<Shape, Key>::CopyKeysTo(FixedArray* storage) { 7906 ASSERT(storage->length() >= NumberOfElementsFilterAttributes( 7907 static_cast<PropertyAttributes>(NONE))); 7908 int capacity = HashTable<Shape, Key>::Capacity(); 7909 int index = 0; 7910 for (int i = 0; i < capacity; i++) { 7911 Object* k = HashTable<Shape, Key>::KeyAt(i); 7912 if (HashTable<Shape, Key>::IsKey(k)) { 7913 PropertyDetails details = DetailsAt(i); 7914 if (details.IsDeleted()) continue; 7915 storage->set(index++, k); 7916 } 7917 } 7918 ASSERT(storage->length() >= index); 7919} 7920 7921 7922// Backwards lookup (slow). 7923template<typename Shape, typename Key> 7924Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) { 7925 int capacity = HashTable<Shape, Key>::Capacity(); 7926 for (int i = 0; i < capacity; i++) { 7927 Object* k = HashTable<Shape, Key>::KeyAt(i); 7928 if (Dictionary<Shape, Key>::IsKey(k)) { 7929 Object* e = ValueAt(i); 7930 if (e->IsJSGlobalPropertyCell()) { 7931 e = JSGlobalPropertyCell::cast(e)->value(); 7932 } 7933 if (e == value) return k; 7934 } 7935 } 7936 return Heap::undefined_value(); 7937} 7938 7939 7940Object* StringDictionary::TransformPropertiesToFastFor( 7941 JSObject* obj, int unused_property_fields) { 7942 // Make sure we preserve dictionary representation if there are too many 7943 // descriptors. 7944 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj; 7945 7946 // Figure out if it is necessary to generate new enumeration indices. 7947 int max_enumeration_index = 7948 NextEnumerationIndex() + 7949 (DescriptorArray::kMaxNumberOfDescriptors - 7950 NumberOfElements()); 7951 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) { 7952 Object* result = GenerateNewEnumerationIndices(); 7953 if (result->IsFailure()) return result; 7954 } 7955 7956 int instance_descriptor_length = 0; 7957 int number_of_fields = 0; 7958 7959 // Compute the length of the instance descriptor. 7960 int capacity = Capacity(); 7961 for (int i = 0; i < capacity; i++) { 7962 Object* k = KeyAt(i); 7963 if (IsKey(k)) { 7964 Object* value = ValueAt(i); 7965 PropertyType type = DetailsAt(i).type(); 7966 ASSERT(type != FIELD); 7967 instance_descriptor_length++; 7968 if (type == NORMAL && 7969 (!value->IsJSFunction() || Heap::InNewSpace(value))) { 7970 number_of_fields += 1; 7971 } 7972 } 7973 } 7974 7975 // Allocate the instance descriptor. 7976 Object* descriptors_unchecked = 7977 DescriptorArray::Allocate(instance_descriptor_length); 7978 if (descriptors_unchecked->IsFailure()) return descriptors_unchecked; 7979 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked); 7980 7981 int inobject_props = obj->map()->inobject_properties(); 7982 int number_of_allocated_fields = 7983 number_of_fields + unused_property_fields - inobject_props; 7984 7985 // Allocate the fixed array for the fields. 7986 Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields); 7987 if (fields->IsFailure()) return fields; 7988 7989 // Fill in the instance descriptor and the fields. 7990 int next_descriptor = 0; 7991 int current_offset = 0; 7992 for (int i = 0; i < capacity; i++) { 7993 Object* k = KeyAt(i); 7994 if (IsKey(k)) { 7995 Object* value = ValueAt(i); 7996 // Ensure the key is a symbol before writing into the instance descriptor. 7997 Object* key = Heap::LookupSymbol(String::cast(k)); 7998 if (key->IsFailure()) return key; 7999 PropertyDetails details = DetailsAt(i); 8000 PropertyType type = details.type(); 8001 8002 if (value->IsJSFunction() && !Heap::InNewSpace(value)) { 8003 ConstantFunctionDescriptor d(String::cast(key), 8004 JSFunction::cast(value), 8005 details.attributes(), 8006 details.index()); 8007 descriptors->Set(next_descriptor++, &d); 8008 } else if (type == NORMAL) { 8009 if (current_offset < inobject_props) { 8010 obj->InObjectPropertyAtPut(current_offset, 8011 value, 8012 UPDATE_WRITE_BARRIER); 8013 } else { 8014 int offset = current_offset - inobject_props; 8015 FixedArray::cast(fields)->set(offset, value); 8016 } 8017 FieldDescriptor d(String::cast(key), 8018 current_offset++, 8019 details.attributes(), 8020 details.index()); 8021 descriptors->Set(next_descriptor++, &d); 8022 } else if (type == CALLBACKS) { 8023 CallbacksDescriptor d(String::cast(key), 8024 value, 8025 details.attributes(), 8026 details.index()); 8027 descriptors->Set(next_descriptor++, &d); 8028 } else { 8029 UNREACHABLE(); 8030 } 8031 } 8032 } 8033 ASSERT(current_offset == number_of_fields); 8034 8035 descriptors->Sort(); 8036 // Allocate new map. 8037 Object* new_map = obj->map()->CopyDropDescriptors(); 8038 if (new_map->IsFailure()) return new_map; 8039 8040 // Transform the object. 8041 obj->set_map(Map::cast(new_map)); 8042 obj->map()->set_instance_descriptors(descriptors); 8043 obj->map()->set_unused_property_fields(unused_property_fields); 8044 8045 obj->set_properties(FixedArray::cast(fields)); 8046 ASSERT(obj->IsJSObject()); 8047 8048 descriptors->SetNextEnumerationIndex(NextEnumerationIndex()); 8049 // Check that it really works. 8050 ASSERT(obj->HasFastProperties()); 8051 8052 return obj; 8053} 8054 8055 8056#ifdef ENABLE_DEBUGGER_SUPPORT 8057// Check if there is a break point at this code position. 8058bool DebugInfo::HasBreakPoint(int code_position) { 8059 // Get the break point info object for this code position. 8060 Object* break_point_info = GetBreakPointInfo(code_position); 8061 8062 // If there is no break point info object or no break points in the break 8063 // point info object there is no break point at this code position. 8064 if (break_point_info->IsUndefined()) return false; 8065 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0; 8066} 8067 8068 8069// Get the break point info object for this code position. 8070Object* DebugInfo::GetBreakPointInfo(int code_position) { 8071 // Find the index of the break point info object for this code position. 8072 int index = GetBreakPointInfoIndex(code_position); 8073 8074 // Return the break point info object if any. 8075 if (index == kNoBreakPointInfo) return Heap::undefined_value(); 8076 return BreakPointInfo::cast(break_points()->get(index)); 8077} 8078 8079 8080// Clear a break point at the specified code position. 8081void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info, 8082 int code_position, 8083 Handle<Object> break_point_object) { 8084 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position)); 8085 if (break_point_info->IsUndefined()) return; 8086 BreakPointInfo::ClearBreakPoint( 8087 Handle<BreakPointInfo>::cast(break_point_info), 8088 break_point_object); 8089} 8090 8091 8092void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, 8093 int code_position, 8094 int source_position, 8095 int statement_position, 8096 Handle<Object> break_point_object) { 8097 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position)); 8098 if (!break_point_info->IsUndefined()) { 8099 BreakPointInfo::SetBreakPoint( 8100 Handle<BreakPointInfo>::cast(break_point_info), 8101 break_point_object); 8102 return; 8103 } 8104 8105 // Adding a new break point for a code position which did not have any 8106 // break points before. Try to find a free slot. 8107 int index = kNoBreakPointInfo; 8108 for (int i = 0; i < debug_info->break_points()->length(); i++) { 8109 if (debug_info->break_points()->get(i)->IsUndefined()) { 8110 index = i; 8111 break; 8112 } 8113 } 8114 if (index == kNoBreakPointInfo) { 8115 // No free slot - extend break point info array. 8116 Handle<FixedArray> old_break_points = 8117 Handle<FixedArray>(FixedArray::cast(debug_info->break_points())); 8118 debug_info->set_break_points(*Factory::NewFixedArray( 8119 old_break_points->length() + 8120 Debug::kEstimatedNofBreakPointsInFunction)); 8121 Handle<FixedArray> new_break_points = 8122 Handle<FixedArray>(FixedArray::cast(debug_info->break_points())); 8123 for (int i = 0; i < old_break_points->length(); i++) { 8124 new_break_points->set(i, old_break_points->get(i)); 8125 } 8126 index = old_break_points->length(); 8127 } 8128 ASSERT(index != kNoBreakPointInfo); 8129 8130 // Allocate new BreakPointInfo object and set the break point. 8131 Handle<BreakPointInfo> new_break_point_info = 8132 Handle<BreakPointInfo>::cast(Factory::NewStruct(BREAK_POINT_INFO_TYPE)); 8133 new_break_point_info->set_code_position(Smi::FromInt(code_position)); 8134 new_break_point_info->set_source_position(Smi::FromInt(source_position)); 8135 new_break_point_info-> 8136 set_statement_position(Smi::FromInt(statement_position)); 8137 new_break_point_info->set_break_point_objects(Heap::undefined_value()); 8138 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object); 8139 debug_info->break_points()->set(index, *new_break_point_info); 8140} 8141 8142 8143// Get the break point objects for a code position. 8144Object* DebugInfo::GetBreakPointObjects(int code_position) { 8145 Object* break_point_info = GetBreakPointInfo(code_position); 8146 if (break_point_info->IsUndefined()) { 8147 return Heap::undefined_value(); 8148 } 8149 return BreakPointInfo::cast(break_point_info)->break_point_objects(); 8150} 8151 8152 8153// Get the total number of break points. 8154int DebugInfo::GetBreakPointCount() { 8155 if (break_points()->IsUndefined()) return 0; 8156 int count = 0; 8157 for (int i = 0; i < break_points()->length(); i++) { 8158 if (!break_points()->get(i)->IsUndefined()) { 8159 BreakPointInfo* break_point_info = 8160 BreakPointInfo::cast(break_points()->get(i)); 8161 count += break_point_info->GetBreakPointCount(); 8162 } 8163 } 8164 return count; 8165} 8166 8167 8168Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info, 8169 Handle<Object> break_point_object) { 8170 if (debug_info->break_points()->IsUndefined()) return Heap::undefined_value(); 8171 for (int i = 0; i < debug_info->break_points()->length(); i++) { 8172 if (!debug_info->break_points()->get(i)->IsUndefined()) { 8173 Handle<BreakPointInfo> break_point_info = 8174 Handle<BreakPointInfo>(BreakPointInfo::cast( 8175 debug_info->break_points()->get(i))); 8176 if (BreakPointInfo::HasBreakPointObject(break_point_info, 8177 break_point_object)) { 8178 return *break_point_info; 8179 } 8180 } 8181 } 8182 return Heap::undefined_value(); 8183} 8184 8185 8186// Find the index of the break point info object for the specified code 8187// position. 8188int DebugInfo::GetBreakPointInfoIndex(int code_position) { 8189 if (break_points()->IsUndefined()) return kNoBreakPointInfo; 8190 for (int i = 0; i < break_points()->length(); i++) { 8191 if (!break_points()->get(i)->IsUndefined()) { 8192 BreakPointInfo* break_point_info = 8193 BreakPointInfo::cast(break_points()->get(i)); 8194 if (break_point_info->code_position()->value() == code_position) { 8195 return i; 8196 } 8197 } 8198 } 8199 return kNoBreakPointInfo; 8200} 8201 8202 8203// Remove the specified break point object. 8204void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info, 8205 Handle<Object> break_point_object) { 8206 // If there are no break points just ignore. 8207 if (break_point_info->break_point_objects()->IsUndefined()) return; 8208 // If there is a single break point clear it if it is the same. 8209 if (!break_point_info->break_point_objects()->IsFixedArray()) { 8210 if (break_point_info->break_point_objects() == *break_point_object) { 8211 break_point_info->set_break_point_objects(Heap::undefined_value()); 8212 } 8213 return; 8214 } 8215 // If there are multiple break points shrink the array 8216 ASSERT(break_point_info->break_point_objects()->IsFixedArray()); 8217 Handle<FixedArray> old_array = 8218 Handle<FixedArray>( 8219 FixedArray::cast(break_point_info->break_point_objects())); 8220 Handle<FixedArray> new_array = 8221 Factory::NewFixedArray(old_array->length() - 1); 8222 int found_count = 0; 8223 for (int i = 0; i < old_array->length(); i++) { 8224 if (old_array->get(i) == *break_point_object) { 8225 ASSERT(found_count == 0); 8226 found_count++; 8227 } else { 8228 new_array->set(i - found_count, old_array->get(i)); 8229 } 8230 } 8231 // If the break point was found in the list change it. 8232 if (found_count > 0) break_point_info->set_break_point_objects(*new_array); 8233} 8234 8235 8236// Add the specified break point object. 8237void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info, 8238 Handle<Object> break_point_object) { 8239 // If there was no break point objects before just set it. 8240 if (break_point_info->break_point_objects()->IsUndefined()) { 8241 break_point_info->set_break_point_objects(*break_point_object); 8242 return; 8243 } 8244 // If the break point object is the same as before just ignore. 8245 if (break_point_info->break_point_objects() == *break_point_object) return; 8246 // If there was one break point object before replace with array. 8247 if (!break_point_info->break_point_objects()->IsFixedArray()) { 8248 Handle<FixedArray> array = Factory::NewFixedArray(2); 8249 array->set(0, break_point_info->break_point_objects()); 8250 array->set(1, *break_point_object); 8251 break_point_info->set_break_point_objects(*array); 8252 return; 8253 } 8254 // If there was more than one break point before extend array. 8255 Handle<FixedArray> old_array = 8256 Handle<FixedArray>( 8257 FixedArray::cast(break_point_info->break_point_objects())); 8258 Handle<FixedArray> new_array = 8259 Factory::NewFixedArray(old_array->length() + 1); 8260 for (int i = 0; i < old_array->length(); i++) { 8261 // If the break point was there before just ignore. 8262 if (old_array->get(i) == *break_point_object) return; 8263 new_array->set(i, old_array->get(i)); 8264 } 8265 // Add the new break point. 8266 new_array->set(old_array->length(), *break_point_object); 8267 break_point_info->set_break_point_objects(*new_array); 8268} 8269 8270 8271bool BreakPointInfo::HasBreakPointObject( 8272 Handle<BreakPointInfo> break_point_info, 8273 Handle<Object> break_point_object) { 8274 // No break point. 8275 if (break_point_info->break_point_objects()->IsUndefined()) return false; 8276 // Single beak point. 8277 if (!break_point_info->break_point_objects()->IsFixedArray()) { 8278 return break_point_info->break_point_objects() == *break_point_object; 8279 } 8280 // Multiple break points. 8281 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects()); 8282 for (int i = 0; i < array->length(); i++) { 8283 if (array->get(i) == *break_point_object) { 8284 return true; 8285 } 8286 } 8287 return false; 8288} 8289 8290 8291// Get the number of break points. 8292int BreakPointInfo::GetBreakPointCount() { 8293 // No break point. 8294 if (break_point_objects()->IsUndefined()) return 0; 8295 // Single beak point. 8296 if (!break_point_objects()->IsFixedArray()) return 1; 8297 // Multiple break points. 8298 return FixedArray::cast(break_point_objects())->length(); 8299} 8300#endif 8301 8302 8303} } // namespace v8::internal 8304